Przeglądaj źródła

Animation editor load/save from/to animation clip implemented
Don't allow imported animation curves to be edited

BearishSun 9 lat temu
rodzic
commit
40a9a03be0

+ 23 - 1
Source/MBansheeEditor/General/EditorApplication.cs

@@ -182,6 +182,14 @@ namespace BansheeEditor
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Returns an object that can be used for storing data that persists throughout the entire editor session.
+        /// </summary>
+        internal static EditorPersistentData PersistentData
+        {
+            get { return persistentData; }
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the path where the script compiler is located at.
         /// Returns the path where the script compiler is located at.
         /// </summary>
         /// </summary>
@@ -630,12 +638,17 @@ namespace BansheeEditor
         [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "Save project", 1999)]
         [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "Save project", 1999)]
         public static void SaveProject()
         public static void SaveProject()
         {
         {
+            // Apply changes to any animation clips edited using the animation editor
+            foreach (var KVP in persistentData.dirtyAnimClips)
+                KVP.Value.SaveToClip();
+
+            // Save all dirty resources to disk
             foreach (var KVP in persistentData.dirtyResources)
             foreach (var KVP in persistentData.dirtyResources)
             {
             {
                 string resourceUUID = KVP.Key;
                 string resourceUUID = KVP.Key;
                 string path = ProjectLibrary.GetPath(resourceUUID);
                 string path = ProjectLibrary.GetPath(resourceUUID);
                 if (!IsNative(path))
                 if (!IsNative(path))
-                    continue; // Native resources can't be changed
+                    continue; // Imported resources can't be changed
 
 
                 Resource resource = ProjectLibrary.Load<Resource>(path);
                 Resource resource = ProjectLibrary.Load<Resource>(path);
 
 
@@ -643,6 +656,7 @@ namespace BansheeEditor
                     ProjectLibrary.Save(resource);
                     ProjectLibrary.Save(resource);
             }
             }
 
 
+            persistentData.dirtyAnimClips.Clear();
             persistentData.dirtyResources.Clear();
             persistentData.dirtyResources.Clear();
             SetStatusProject(false);
             SetStatusProject(false);
 
 
@@ -711,6 +725,14 @@ namespace BansheeEditor
             persistentData.dirtyResources[resource.UUID] = true;
             persistentData.dirtyResources[resource.UUID] = true;
         }
         }
 
 
+        /// <summary>
+        /// Marks the current project dirty (requires saving in order for changes not to be lost).
+        /// </summary>
+        public static void SetProjectDirty()
+        {
+            SetStatusProject(true);
+        }
+
         /// <summary>
         /// <summary>
         /// Marks the current scene as dirty.
         /// Marks the current scene as dirty.
         /// </summary>
         /// </summary>

+ 4 - 1
Source/MBansheeEditor/Windows/Inspector/EditorPersistentData.cs → Source/MBansheeEditor/General/EditorPersistentData.cs

@@ -5,7 +5,7 @@ using System.Collections.Generic;
 
 
 namespace BansheeEditor
 namespace BansheeEditor
 {
 {
-    /** @addtogroup Inspector
+    /** @addtogroup General
      *  @{
      *  @{
      */
      */
 
 
@@ -16,6 +16,9 @@ namespace BansheeEditor
     {
     {
         [SerializeField]
         [SerializeField]
         internal Dictionary<string, bool> dirtyResources = new Dictionary<string, bool>();
         internal Dictionary<string, bool> dirtyResources = new Dictionary<string, bool>();
+
+        [SerializeField]
+        internal Dictionary<string, EditorAnimClipInfo> dirtyAnimClips = new Dictionary<string, EditorAnimClipInfo>();
     }
     }
 
 
     /** @} */
     /** @} */

+ 3 - 3
Source/MBansheeEditor/General/Program.cs

@@ -140,7 +140,7 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Attempts to save the current scene, and keeps retrying if failed or until user cancels.
         /// Attempts to save the current scene, and keeps retrying if failed or until user cancels.
         /// </summary>
         /// </summary>
-        static void TrySaveScene()
+        static void TrySaveSceneOnQuit()
         {
         {
             Action success = () =>
             Action success = () =>
             {
             {
@@ -148,7 +148,7 @@ namespace BansheeEditor
                 EditorApplication.Quit();
                 EditorApplication.Quit();
             };
             };
 
 
-            EditorApplication.SaveScene(success, TrySaveScene);
+            EditorApplication.SaveScene(success, TrySaveSceneOnQuit);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -161,7 +161,7 @@ namespace BansheeEditor
             (result) =>
             (result) =>
             {
             {
                 if (result == DialogBox.ResultType.Yes)
                 if (result == DialogBox.ResultType.Yes)
-                    TrySaveScene();
+                    TrySaveSceneOnQuit();
                 else if (result == DialogBox.ResultType.No)
                 else if (result == DialogBox.ResultType.No)
                 {
                 {
                     EditorApplication.SaveProject();
                     EditorApplication.SaveProject();

+ 2 - 1
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -51,6 +51,7 @@
     <Compile Include="Utility\SerializedObject.cs" />
     <Compile Include="Utility\SerializedObject.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
+    <Compile Include="Windows\Animation\EditorAnimInfo.cs" />
     <Compile Include="Windows\Animation\FieldSelectionWindow.cs" />
     <Compile Include="Windows\Animation\FieldSelectionWindow.cs" />
     <Compile Include="Windows\Animation\GUIAnimEvents.cs" />
     <Compile Include="Windows\Animation\GUIAnimEvents.cs" />
     <Compile Include="Windows\Animation\GUIAnimFieldDisplay.cs" />
     <Compile Include="Windows\Animation\GUIAnimFieldDisplay.cs" />
@@ -66,7 +67,7 @@
     <Compile Include="Windows\Build\BuildWindow.cs" />
     <Compile Include="Windows\Build\BuildWindow.cs" />
     <Compile Include="Script\CodeEditor.cs" />
     <Compile Include="Script\CodeEditor.cs" />
     <Compile Include="Windows\ColorPicker.cs" />
     <Compile Include="Windows\ColorPicker.cs" />
-    <Compile Include="Windows\Inspector\EditorPersistentData.cs" />
+    <Compile Include="General\EditorPersistentData.cs" />
     <Compile Include="General\IGlobalShortcuts.cs" />
     <Compile Include="General\IGlobalShortcuts.cs" />
     <Compile Include="Inspectors\BoxColliderInspector.cs" />
     <Compile Include="Inspectors\BoxColliderInspector.cs" />
     <Compile Include="Inspectors\CapsuleColliderInspector.cs" />
     <Compile Include="Inspectors\CapsuleColliderInspector.cs" />

+ 459 - 0
Source/MBansheeEditor/Windows/Animation/EditorAnimInfo.cs

@@ -0,0 +1,459 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using System.Text;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup AnimationEditor
+     *  @{
+     */
+
+
+    /// <summary>
+    /// A set of animation curves for a field of a certain type.
+    /// </summary>
+    internal struct FieldAnimCurves
+    {
+        public SerializableProperty.FieldType type;
+        public EdAnimationCurve[] curves;
+    }
+
+    /// <summary>
+    /// Stores tangent modes for an 3D vector animation curve (one mode for each keyframe).
+    /// </summary>
+    [SerializeObject]
+    internal class EditorVector3CurveTangents
+    {
+        public string name;
+        public TangentMode[] tangentsX;
+        public TangentMode[] tangentsY;
+        public TangentMode[] tangentsZ;
+    }
+
+    /// <summary>
+    /// Stores tangent modes for an float animation curve (one mode for each keyframe).
+    /// </summary>
+    [SerializeObject]
+    internal class EditorFloatCurveTangents
+    {
+        public string name;
+        public TangentMode[] tangents;
+    }
+
+    /// <summary>
+    /// Stores tangent information for all curves in an animation clip.
+    /// </summary>
+    [SerializeObject]
+    internal class EditorAnimClipTangents
+    {
+        public EditorVector3CurveTangents[] positionCurves;
+        public EditorVector3CurveTangents[] rotationCurves;
+        public EditorVector3CurveTangents[] scaleCurves;
+        public EditorFloatCurveTangents[] floatCurves;
+    }
+
+    /// <summary>
+    /// Stores animation clip data for clips that are currently being edited.
+    /// </summary>
+    internal class EditorAnimClipInfo
+    {
+        public AnimationClip clip;
+        public Dictionary<string, FieldAnimCurves> curves = new Dictionary<string, FieldAnimCurves>();
+        public AnimationEvent[] events = new AnimationEvent[0];
+
+        /// <summary>
+        /// Loads curve and event information from the provided clip, and creates a new instance of this object containing
+        /// the required data for editing the source clip in the animation editor.
+        /// </summary>
+        /// <param name="clip">Clip to load.</param>
+        /// <returns>Editor specific editable information about an animation clip.</returns>
+        public static EditorAnimClipInfo Create(AnimationClip clip)
+        {
+            EditorAnimClipInfo clipInfo = new EditorAnimClipInfo();
+            clipInfo.clip = clip;
+
+            AnimationCurves clipCurves = clip.Curves;
+            EditorAnimClipTangents editorCurveData = null;
+
+            string resourcePath = ProjectLibrary.GetPath(clip);
+            if (!string.IsNullOrEmpty(resourcePath))
+            {
+                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
+                string clipName = PathEx.GetTail(resourcePath);
+
+                if (entry != null && entry.Type == LibraryEntryType.File)
+                {
+                    FileEntry fileEntry = (FileEntry)entry;
+                    ResourceMeta[] metas = fileEntry.ResourceMetas;
+
+                    for (int i = 0; i < metas.Length; i++)
+                    {
+                        if (clipName == metas[i].SubresourceName)
+                        {
+                            editorCurveData = metas[i].EditorData as EditorAnimClipTangents;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (editorCurveData == null)
+                editorCurveData = new EditorAnimClipTangents();
+
+            Action<NamedVector3Curve[], EditorVector3CurveTangents[], string> loadVector3Curve =
+                (curves, tangents, subPath) =>
+                {
+                    foreach (var curveEntry in curves)
+                    {
+                        TangentMode[] tangentsX = null;
+                        TangentMode[] tangentsY = null;
+                        TangentMode[] tangentsZ = null;
+                        foreach (var tangentEntry in tangents)
+                        {
+                            if (tangentEntry.name == curveEntry.Name)
+                            {
+                                tangentsX = tangentEntry.tangentsX;
+                                tangentsY = tangentEntry.tangentsY;
+                                tangentsZ = tangentEntry.tangentsZ;
+                                break;
+                            }
+                        }
+
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
+                        fieldCurves.type = SerializableProperty.FieldType.Vector3;
+                        fieldCurves.curves = new EdAnimationCurve[3];
+
+                        fieldCurves.curves[0] = new EdAnimationCurve(curveEntry.X, tangentsX);
+                        fieldCurves.curves[1] = new EdAnimationCurve(curveEntry.Y, tangentsY);
+                        fieldCurves.curves[2] = new EdAnimationCurve(curveEntry.Z, tangentsZ);
+
+                        string curvePath = curveEntry.Name.TrimEnd('/') + subPath;
+                        clipInfo.curves[curvePath] = fieldCurves;
+                    }
+                };
+
+            loadVector3Curve(clipCurves.PositionCurves, editorCurveData.positionCurves, "/Position");
+            loadVector3Curve(clipCurves.RotationCurves, editorCurveData.rotationCurves, "/Rotation");
+            loadVector3Curve(clipCurves.ScaleCurves, editorCurveData.scaleCurves, "/Scale");
+
+            // Find which individual float curves belong to the same field
+            Dictionary<string, Tuple<int, int, bool>[]> floatCurveMapping = new Dictionary<string, Tuple<int, int, bool>[]>();
+            {
+                int curveIdx = 0;
+                foreach (var curveEntry in clipCurves.FloatCurves)
+                {
+                    string path = curveEntry.Name;
+                    string pathNoSuffix = null;
+
+                    string pathSuffix;
+                    if (path.Length >= 2)
+                    {
+                        pathSuffix = path.Substring(path.Length - 2, 2);
+                        pathNoSuffix = path.Substring(0, path.Length - 2);
+                    }
+                    else
+                        pathSuffix = "";
+
+                    int tangentIdx = -1;
+                    int currentTangentIdx = 0;
+                    foreach (var tangentEntry in editorCurveData.floatCurves)
+                    {
+                        if (tangentEntry.name == curveEntry.Name)
+                        {
+                            tangentIdx = currentTangentIdx;
+                            break;
+                        }
+
+                        currentTangentIdx++;
+                    }
+
+                    Animation.PropertySuffixInfo suffixInfo;
+                    if (Animation.PropertySuffixInfos.TryGetValue(pathSuffix, out suffixInfo))
+                    {
+                        Tuple<int, int, bool>[] curveInfo;
+                        if (!floatCurveMapping.TryGetValue(pathNoSuffix, out curveInfo))
+                            curveInfo = new Tuple<int, int, bool>[4];
+
+                        curveInfo[suffixInfo.elementIdx] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);
+                        floatCurveMapping[pathNoSuffix] = curveInfo;
+                    }
+                    else
+                    {
+                        Tuple<int, int, bool>[] curveInfo = new Tuple<int, int, bool>[4];
+                        curveInfo[0] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);
+
+                        floatCurveMapping[path] = curveInfo;
+                    }
+
+                    curveIdx++;
+                }
+            }
+
+            foreach (var KVP in floatCurveMapping)
+            {
+                int numCurves = 0;
+                for (int i = 0; i < 4; i++)
+                {
+                    if (KVP.Value[i] == null)
+                        continue;
+
+                    numCurves++;
+                }
+
+                if (numCurves == 0)
+                    continue; // Invalid curve
+
+                FieldAnimCurves fieldCurves = new FieldAnimCurves();
+
+                // Deduce type (note that all single value types are assumed to be float even if their source type is int or bool)
+                if (numCurves == 1)
+                    fieldCurves.type = SerializableProperty.FieldType.Float;
+                else if (numCurves == 2)
+                    fieldCurves.type = SerializableProperty.FieldType.Vector2;
+                else if (numCurves == 3)
+                    fieldCurves.type = SerializableProperty.FieldType.Vector3;
+                else // 4 curves
+                {
+                    bool isVector = KVP.Value[0].Item3;
+                    if (isVector)
+                        fieldCurves.type = SerializableProperty.FieldType.Vector4;
+                    else
+                        fieldCurves.type = SerializableProperty.FieldType.Color;
+                }
+
+                fieldCurves.curves = new EdAnimationCurve[numCurves];
+
+                for (int i = 0; i < numCurves; i++)
+                {
+                    int curveIdx = KVP.Value[i].Item1;
+                    int tangentIdx = KVP.Value[i].Item2;
+
+                    TangentMode[] tangents = null;
+                    if (tangentIdx != -1)
+                        tangents = editorCurveData.floatCurves[tangentIdx].tangents;
+
+                    fieldCurves.curves[i] = new EdAnimationCurve(clipCurves.FloatCurves[curveIdx].Curve, tangents);
+                }
+
+                string curvePath = KVP.Key;
+                clipInfo.curves[curvePath] = fieldCurves;
+            }
+
+            // Add events
+            clipInfo.events = clip.Events;
+            return clipInfo;
+        }
+
+        /// <summary>
+        /// Checks is the specified animation clip is imported from an external file, or created within the editor.
+        /// </summary>
+        /// <param name="clip">Clip to check.</param>
+        /// <returns>True if the clip is imported from an external file (e.g. FBX file), or false if the clip is a native
+        ///          resource created within the editor.</returns>
+        public static bool IsClipImported(AnimationClip clip)
+        {
+            string resourcePath = ProjectLibrary.GetPath(clip);
+            return ProjectLibrary.IsSubresource(resourcePath);
+        }
+
+        /// <summary>
+        /// Saves the animation curves and events stored in this object, into the associated animation clip resource.
+        /// Relevant animation clip resource must already be created and exist in the project library.
+        /// </summary>
+        public void SaveToClip()
+        {
+            bool clipIsImported = IsClipImported(clip);
+
+            if (!clipIsImported)
+            {
+                List<NamedVector3Curve> positionCurves = new List<NamedVector3Curve>();
+                List<NamedVector3Curve> rotationCurves = new List<NamedVector3Curve>();
+                List<NamedVector3Curve> scaleCurves = new List<NamedVector3Curve>();
+                List<NamedFloatCurve> floatCurves = new List<NamedFloatCurve>();
+
+                List<EditorVector3CurveTangents> positionTangents = new List<EditorVector3CurveTangents>();
+                List<EditorVector3CurveTangents> rotationTangents = new List<EditorVector3CurveTangents>();
+                List<EditorVector3CurveTangents> scaleTangents = new List<EditorVector3CurveTangents>();
+                List<EditorFloatCurveTangents> floatTangents = new List<EditorFloatCurveTangents>();
+
+                foreach (var kvp in curves)
+                {
+                    string[] pathEntries = kvp.Key.Split('/');
+                    if (pathEntries.Length == 0)
+                        continue;
+
+                    string lastEntry = pathEntries[pathEntries.Length - 1];
+
+                    if (lastEntry == "Position" || lastEntry == "Rotation" || lastEntry == "Scale")
+                    {
+                        StringBuilder sb = new StringBuilder();
+                        for (int i = 0; i < pathEntries.Length - 2; i++)
+                            sb.Append(pathEntries[i] + "/");
+
+                        if (pathEntries.Length > 1)
+                            sb.Append(pathEntries[pathEntries.Length - 2]);
+
+                        string curvePath = sb.ToString();
+
+                        NamedVector3Curve curve = new NamedVector3Curve(curvePath,
+                            new AnimationCurve(kvp.Value.curves[0].KeyFrames),
+                            new AnimationCurve(kvp.Value.curves[1].KeyFrames),
+                            new AnimationCurve(kvp.Value.curves[2].KeyFrames));
+
+                        EditorVector3CurveTangents tangents = new EditorVector3CurveTangents();
+                        tangents.name = curvePath;
+                        tangents.tangentsX = kvp.Value.curves[0].TangentModes;
+                        tangents.tangentsY = kvp.Value.curves[1].TangentModes;
+                        tangents.tangentsZ = kvp.Value.curves[2].TangentModes;
+
+                        if (lastEntry == "Position")
+                        {
+                            positionCurves.Add(curve);
+                            positionTangents.Add(tangents);
+                        }
+                        else if (lastEntry == "Rotation")
+                        {
+                            rotationCurves.Add(curve);
+                            rotationTangents.Add(tangents);
+                        }
+                        else if (lastEntry == "Scale")
+                        {
+                            scaleCurves.Add(curve);
+                            scaleTangents.Add(tangents);
+                        }
+                    }
+                    else
+                    {
+                        Action<int, string> addCurve = (idx, subPath) =>
+                        {
+                            string path = kvp.Key + subPath;
+
+                            NamedFloatCurve curve = new NamedFloatCurve(path,
+                            new AnimationCurve(kvp.Value.curves[idx].KeyFrames));
+
+                            EditorFloatCurveTangents tangents = new EditorFloatCurveTangents();
+                            tangents.name = path;
+                            tangents.tangents = kvp.Value.curves[idx].TangentModes;
+
+                            floatCurves.Add(curve);
+                            floatTangents.Add(tangents);
+                        };
+
+                        switch (kvp.Value.type)
+                        {
+                            case SerializableProperty.FieldType.Vector2:
+                                addCurve(0, ".x");
+                                addCurve(1, ".y");
+                                break;
+                            case SerializableProperty.FieldType.Vector3:
+                                addCurve(0, ".x");
+                                addCurve(1, ".y");
+                                addCurve(2, ".z");
+                                break;
+                            case SerializableProperty.FieldType.Vector4:
+                                addCurve(0, ".x");
+                                addCurve(1, ".y");
+                                addCurve(2, ".z");
+                                addCurve(3, ".w");
+                                break;
+                            case SerializableProperty.FieldType.Color:
+                                addCurve(0, ".r");
+                                addCurve(1, ".g");
+                                addCurve(2, ".b");
+                                addCurve(3, ".a");
+                                break;
+                            case SerializableProperty.FieldType.Bool:
+                            case SerializableProperty.FieldType.Int:
+                            case SerializableProperty.FieldType.Float:
+                                addCurve(0, "");
+                                break;
+                        }
+                    }
+                }
+
+                AnimationCurves newClipCurves = new AnimationCurves();
+                newClipCurves.PositionCurves = positionCurves.ToArray();
+                newClipCurves.RotationCurves = rotationCurves.ToArray();
+                newClipCurves.ScaleCurves = scaleCurves.ToArray();
+                newClipCurves.FloatCurves = floatCurves.ToArray();
+
+                clip.Curves = newClipCurves;
+                clip.Events = events;
+
+                string resourcePath = ProjectLibrary.GetPath(clip);
+                ProjectLibrary.Save(clip);
+
+                // Save tangents for editor only use
+                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
+                string clipName = PathEx.GetTail(resourcePath);
+
+                if (entry != null && entry.Type == LibraryEntryType.File)
+                {
+                    FileEntry fileEntry = (FileEntry)entry;
+                    ResourceMeta[] metas = fileEntry.ResourceMetas;
+
+                    for (int i = 0; i < metas.Length; i++)
+                    {
+                        if (clipName == metas[i].SubresourceName)
+                        {
+                            EditorAnimClipTangents newCurveData = new EditorAnimClipTangents();
+                            newCurveData.positionCurves = positionTangents.ToArray();
+                            newCurveData.rotationCurves = rotationTangents.ToArray();
+                            newCurveData.scaleCurves = scaleTangents.ToArray();
+                            newCurveData.floatCurves = floatTangents.ToArray();
+
+                            ProjectLibrary.SetEditorData(resourcePath, newCurveData);
+                            break;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                string resourcePath = ProjectLibrary.GetPath(clip);
+                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
+
+                if (entry != null && entry.Type == LibraryEntryType.File)
+                {
+                    FileEntry fileEntry = (FileEntry)entry;
+                    MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options;
+
+                    string clipName = PathEx.GetTail(resourcePath);
+
+                    List<ImportedAnimationEvents> newEvents = new List<ImportedAnimationEvents>();
+                    newEvents.AddRange(meshImportOptions.AnimationEvents);
+
+                    bool isExisting = false;
+                    for (int i = 0; i < newEvents.Count; i++)
+                    {
+                        if (newEvents[i].name == clipName)
+                        {
+                            newEvents[i].events = events;
+                            isExisting = true;
+                            break;
+                        }
+                    }
+
+                    if (!isExisting)
+                    {
+                        ImportedAnimationEvents newEntry = new ImportedAnimationEvents();
+                        newEntry.name = clipName;
+                        newEntry.events = events;
+
+                        newEvents.Add(newEntry);
+                    }
+
+                    meshImportOptions.AnimationEvents = newEvents.ToArray();
+
+                    ProjectLibrary.Reimport(resourcePath, meshImportOptions, true);
+                }
+            }
+        }
+    }
+
+    /** @} */
+}
+          

+ 90 - 23
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -80,6 +80,8 @@ namespace BansheeEditor
         private Vector2I contextClickPosition;
         private Vector2I contextClickPosition;
 
 
         private EdAnimationCurve[] curves = new EdAnimationCurve[0];
         private EdAnimationCurve[] curves = new EdAnimationCurve[0];
+        private bool disableCurveEdit = false;
+
         private float xRange = 60.0f;
         private float xRange = 60.0f;
         private float yRange = 10.0f;
         private float yRange = 10.0f;
         private Vector2 offset;
         private Vector2 offset;
@@ -105,6 +107,26 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         public Action<int> OnFrameSelected;
         public Action<int> OnFrameSelected;
 
 
+        /// <summary>
+        /// Triggered whenever a new animation event is added.
+        /// </summary>
+        public Action OnEventAdded;
+
+        /// <summary>
+        /// Triggered whenever values in an animation event change.
+        /// </summary>
+        public Action OnEventModified;
+
+        /// <summary>
+        /// Triggered whenever an animation event is deleted.
+        /// </summary>
+        public Action OnEventDeleted;
+
+        /// <summary>
+        /// Triggered whenever keyframe in a curve is modified (added, removed or edited).
+        /// </summary>
+        public Action OnCurveModified;
+
         /// <summary>
         /// <summary>
         /// The displayed range of the curve, where:
         /// The displayed range of the curve, where:
         ///   .x - Range of the horizontal area. Displayed area ranges from [0, x].
         ///   .x - Range of the horizontal area. Displayed area ranges from [0, x].
@@ -162,6 +184,14 @@ namespace BansheeEditor
             get { return height; }
             get { return height; }
         }
         }
 
 
+        /// <summary>
+        /// Set to true if curves are not allowed to be edited.
+        /// </summary>
+        public bool DisableCurveEdit
+        {
+            set { disableCurveEdit = value; }
+        }
+
         /// <summary>
         /// <summary>
         /// Animation events displayed on the curve editor.
         /// Animation events displayed on the curve editor.
         /// </summary>
         /// </summary>
@@ -170,6 +200,9 @@ namespace BansheeEditor
             get
             get
             {
             {
                 AnimationEvent[] animEvents = new AnimationEvent[events.Count];
                 AnimationEvent[] animEvents = new AnimationEvent[events.Count];
+
+                // Note: Hidden dependency. Returned events must point to the same event class this object is using, so
+                // that any modifications made in this class will be visible in the returned values.
                 for (int i = 0; i < events.Count; i++)
                 for (int i = 0; i < events.Count; i++)
                     animEvents[i] = events[i].animEvent;
                     animEvents[i] = events[i].animEvent;
 
 
@@ -215,6 +248,7 @@ namespace BansheeEditor
 
 
             keyframeContextMenu = new ContextMenu();
             keyframeContextMenu = new ContextMenu();
             keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
             keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
+            keyframeContextMenu.AddItem("Delete", EditSelectedKeyframe);
             keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
             keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
             keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
             keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
             keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
             keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
@@ -466,7 +500,7 @@ namespace BansheeEditor
                         int distance = Vector2I.Distance(drawingPos, dragStart);
                         int distance = Vector2I.Distance(drawingPos, dragStart);
                         if (distance >= DRAG_START_DISTANCE)
                         if (distance >= DRAG_START_DISTANCE)
                         {
                         {
-                            if (isMousePressedOverKey)
+                            if (isMousePressedOverKey && !disableCurveEdit)
                             {
                             {
                                 draggedKeyframes.Clear();
                                 draggedKeyframes.Clear();
                                 foreach (var selectedEntry in selectedKeyframes)
                                 foreach (var selectedEntry in selectedKeyframes)
@@ -491,7 +525,7 @@ namespace BansheeEditor
 
 
                     if (isDragInProgress)
                     if (isDragInProgress)
                     {
                     {
-                        if (isMousePressedOverKey)
+                        if (isMousePressedOverKey && !disableCurveEdit)
                         {
                         {
                             Vector2 diff = Vector2.Zero;
                             Vector2 diff = Vector2.Zero;
 
 
@@ -534,10 +568,11 @@ namespace BansheeEditor
                                     SelectKeyframe(new KeyframeRef(draggedEntry.curveIdx, keyframe.index));
                                     SelectKeyframe(new KeyframeRef(draggedEntry.curveIdx, keyframe.index));
                             }
                             }
 
 
+                            OnCurveModified?.Invoke();
                             guiCurveDrawing.Rebuild();
                             guiCurveDrawing.Rebuild();
                             UpdateEventsGUI();
                             UpdateEventsGUI();
                         }
                         }
-                        else if (isMousePressedOverTangent)
+                        else if (isMousePressedOverTangent && !disableCurveEdit)
                         {
                         {
                             EdAnimationCurve curve = curves[draggedTangent.keyframeRef.curveIdx];
                             EdAnimationCurve curve = curves[draggedTangent.keyframeRef.curveIdx];
                             KeyFrame keyframe = curve.KeyFrames[draggedTangent.keyframeRef.keyIdx];
                             KeyFrame keyframe = curve.KeyFrames[draggedTangent.keyframeRef.keyIdx];
@@ -570,6 +605,7 @@ namespace BansheeEditor
                                 curve.KeyFrames[draggedTangent.keyframeRef.keyIdx] = keyframe;
                                 curve.KeyFrames[draggedTangent.keyframeRef.keyIdx] = keyframe;
                                 curve.Apply();
                                 curve.Apply();
 
 
+                                OnCurveModified?.Invoke();
                                 guiCurveDrawing.Rebuild();
                                 guiCurveDrawing.Rebuild();
                             }
                             }
                         }
                         }
@@ -684,17 +720,21 @@ namespace BansheeEditor
         {
         {
             ClearSelection();
             ClearSelection();
 
 
-            foreach (var curve in curves)
+            if (!disableCurveEdit)
             {
             {
-                float t = guiCurveDrawing.GetTimeForFrame(markedFrameIdx);
-                float value = curve.Evaluate(t);
+                foreach (var curve in curves)
+                {
+                    float t = guiCurveDrawing.GetTimeForFrame(markedFrameIdx);
+                    float value = curve.Evaluate(t);
 
 
-                curve.AddKeyframe(t, value);
-                curve.Apply();
+                    curve.AddKeyframe(t, value);
+                    curve.Apply();
+                }
             }
             }
 
 
             // TODO - UNDOREDO
             // TODO - UNDOREDO
 
 
+            OnCurveModified?.Invoke();
             guiCurveDrawing.Rebuild();
             guiCurveDrawing.Rebuild();
             UpdateEventsGUI();
             UpdateEventsGUI();
         }
         }
@@ -711,6 +751,7 @@ namespace BansheeEditor
             eventInfo.animEvent = new AnimationEvent("", eventTime);
             eventInfo.animEvent = new AnimationEvent("", eventTime);
             
             
             events.Add(eventInfo); // TODO - UNDOREDO
             events.Add(eventInfo); // TODO - UNDOREDO
+            OnEventAdded?.Invoke();
 
 
             UpdateEventsGUI();
             UpdateEventsGUI();
             guiCurveDrawing.Rebuild();
             guiCurveDrawing.Rebuild();
@@ -754,6 +795,9 @@ namespace BansheeEditor
         ///                    tangent will be kept as is.</param>
         ///                    tangent will be kept as is.</param>
         private void ChangeSelectionTangentMode(TangentMode mode)
         private void ChangeSelectionTangentMode(TangentMode mode)
         {
         {
+            if (disableCurveEdit)
+                return;
+
             foreach (var selectedEntry in selectedKeyframes)
             foreach (var selectedEntry in selectedKeyframes)
             {
             {
                 EdAnimationCurve curve = curves[selectedEntry.curveIdx];
                 EdAnimationCurve curve = curves[selectedEntry.curveIdx];
@@ -794,6 +838,7 @@ namespace BansheeEditor
 
 
             // TODO - UNDOREDO
             // TODO - UNDOREDO
 
 
+            OnCurveModified?.Invoke();
             guiCurveDrawing.Rebuild();
             guiCurveDrawing.Rebuild();
         }
         }
 
 
@@ -807,17 +852,21 @@ namespace BansheeEditor
             {
             {
                 ClearSelection();
                 ClearSelection();
 
 
-                foreach (var curve in curves)
+                if (!disableCurveEdit)
                 {
                 {
-                    float t = curveCoord.x;
-                    float value = curveCoord.y;
+                    foreach (var curve in curves)
+                    {
+                        float t = curveCoord.x;
+                        float value = curveCoord.y;
 
 
-                    curve.AddKeyframe(t, value);
-                    curve.Apply();
+                        curve.AddKeyframe(t, value);
+                        curve.Apply();
+                    }
                 }
                 }
 
 
                 // TODO - UNDOREDO
                 // TODO - UNDOREDO
 
 
+                OnCurveModified?.Invoke();
                 guiCurveDrawing.Rebuild();
                 guiCurveDrawing.Rebuild();
                 UpdateEventsGUI();
                 UpdateEventsGUI();
             }
             }
@@ -839,6 +888,7 @@ namespace BansheeEditor
                 eventInfo.animEvent = new AnimationEvent("", time);
                 eventInfo.animEvent = new AnimationEvent("", time);
 
 
                 events.Add(eventInfo); // TODO - UNDOREDO
                 events.Add(eventInfo); // TODO - UNDOREDO
+                OnEventAdded?.Invoke();
 
 
                 UpdateEventsGUI();
                 UpdateEventsGUI();
                 guiCurveDrawing.Rebuild();
                 guiCurveDrawing.Rebuild();
@@ -852,26 +902,30 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         private void DeleteSelectedKeyframes()
         private void DeleteSelectedKeyframes()
         {
         {
-            foreach (var selectedEntry in selectedKeyframes)
+            if (!disableCurveEdit)
             {
             {
-                EdAnimationCurve curve = curves[selectedEntry.curveIdx];
-
-                // Sort keys from highest to lowest so the indices don't change
-                selectedEntry.keyIndices.Sort((x, y) =>
+                foreach (var selectedEntry in selectedKeyframes)
                 {
                 {
-                    return y.CompareTo(x);
-                });
+                    EdAnimationCurve curve = curves[selectedEntry.curveIdx];
 
 
-                foreach (var keyframeIdx in selectedEntry.keyIndices)
-                    curve.RemoveKeyframe(keyframeIdx);
+                    // Sort keys from highest to lowest so the indices don't change
+                    selectedEntry.keyIndices.Sort((x, y) =>
+                    {
+                        return y.CompareTo(x);
+                    });
 
 
-                curve.Apply();
+                    foreach (var keyframeIdx in selectedEntry.keyIndices)
+                        curve.RemoveKeyframe(keyframeIdx);
+
+                    curve.Apply();
+                }
             }
             }
 
 
             // TODO - UNDOREDO
             // TODO - UNDOREDO
 
 
             ClearSelection();
             ClearSelection();
 
 
+            OnCurveModified?.Invoke();
             guiCurveDrawing.Rebuild();
             guiCurveDrawing.Rebuild();
             UpdateEventsGUI();
             UpdateEventsGUI();
         }
         }
@@ -889,6 +943,7 @@ namespace BansheeEditor
             }
             }
 
 
             events = newEvents; // TODO - UNDOREDO
             events = newEvents; // TODO - UNDOREDO
+            OnEventDeleted?.Invoke();
 
 
             ClearSelection();
             ClearSelection();
 
 
@@ -960,6 +1015,17 @@ namespace BansheeEditor
             return keyIdx != -1;
             return keyIdx != -1;
         }
         }
 
 
+        /// <summary>
+        /// Opens the edit window for the currently selected keyframe.
+        /// </summary>
+        private void EditSelectedKeyframe()
+        {
+            if (disableCurveEdit)
+                return;
+
+            // TODO - Open keyframe edit window
+        }
+
         /// <summary>
         /// <summary>
         /// Opens the edit window for the currently selected event.
         /// Opens the edit window for the currently selected event.
         /// </summary>
         /// </summary>
@@ -994,6 +1060,7 @@ namespace BansheeEditor
             editWindow.Initialize(animEvent, () =>
             editWindow.Initialize(animEvent, () =>
             {
             {
                 UpdateEventsGUI();
                 UpdateEventsGUI();
+                OnEventModified?.Invoke();
             });
             });
         }
         }
     }
     }

+ 168 - 451
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -22,7 +22,6 @@ namespace BansheeEditor
         private const float DRAG_SCALE = 10.0f;
         private const float DRAG_SCALE = 10.0f;
         private const float ZOOM_SCALE = 0.1f/120.0f; // One scroll step is usually 120 units, we want 1/10 of that
         private const float ZOOM_SCALE = 0.1f/120.0f; // One scroll step is usually 120 units, we want 1/10 of that
 
 
-        private bool isInitialized;
         private SceneObject selectedSO;
         private SceneObject selectedSO;
 
 
         #region Overrides
         #region Overrides
@@ -45,17 +44,13 @@ namespace BansheeEditor
         private void OnInitialize()
         private void OnInitialize()
         {
         {
             Selection.OnSelectionChanged += OnSelectionChanged;
             Selection.OnSelectionChanged += OnSelectionChanged;
-            EditorInput.OnPointerPressed += OnPointerPressed;
-            EditorInput.OnPointerMoved += OnPointerMoved;
-            EditorInput.OnPointerReleased += OnPointerReleased;
-            EditorInput.OnButtonUp += OnButtonUp;
 
 
-            RebuildGUI();
+            UpdateSelectedSO();
         }
         }
 
 
         private void OnEditorUpdate()
         private void OnEditorUpdate()
         {
         {
-            if (!isInitialized)
+            if (selectedSO == null)
                 return;
                 return;
 
 
             HandleDragAndZoomInput();
             HandleDragAndZoomInput();
@@ -64,15 +59,19 @@ namespace BansheeEditor
         private void OnDestroy()
         private void OnDestroy()
         {
         {
             Selection.OnSelectionChanged -= OnSelectionChanged;
             Selection.OnSelectionChanged -= OnSelectionChanged;
-            EditorInput.OnPointerPressed -= OnPointerPressed;
-            EditorInput.OnPointerMoved -= OnPointerMoved;
-            EditorInput.OnPointerReleased -= OnPointerReleased;
-            EditorInput.OnButtonUp -= OnButtonUp;
+
+            if (selectedSO != null)
+            {
+                EditorInput.OnPointerPressed -= OnPointerPressed;
+                EditorInput.OnPointerMoved -= OnPointerMoved;
+                EditorInput.OnPointerReleased -= OnPointerReleased;
+                EditorInput.OnButtonUp -= OnButtonUp;
+            }
         }
         }
 
 
         protected override void WindowResized(int width, int height)
         protected override void WindowResized(int width, int height)
         {
         {
-            if (!isInitialized)
+            if (selectedSO == null)
                 return;
                 return;
 
 
             ResizeGUI(width, height);
             ResizeGUI(width, height);
@@ -111,15 +110,6 @@ namespace BansheeEditor
         private void RebuildGUI()
         private void RebuildGUI()
         {
         {
             GUI.Clear();
             GUI.Clear();
-            selectedFields.Clear();
-            curves.Clear();
-            isInitialized = false;
-
-            if (selectedSO != Selection.SceneObject)
-            {
-                zoomAmount = 0.0f;
-                selectedSO = Selection.SceneObject;
-            }
 
 
             if (selectedSO == null)
             if (selectedSO == null)
             {
             {
@@ -137,9 +127,6 @@ namespace BansheeEditor
                 return;
                 return;
             }
             }
 
 
-            // TODO - Retrieve Animation & AnimationClip from the selected object, fill curves dictionary
-            //  - If not available, show a button to create new animation clip
-
             // Top button row
             // Top button row
             GUIContent playIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Play),
             GUIContent playIcon = new GUIContent(EditorBuiltin.GetAnimationWindowIcon(AnimationWindowIcon.Play),
                 new LocEdString("Play"));
                 new LocEdString("Play"));
@@ -220,23 +207,90 @@ namespace BansheeEditor
 
 
             addPropertyBtn.OnClick += () =>
             addPropertyBtn.OnClick += () =>
             {
             {
-                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-                FieldSelectionWindow fieldSelection = DropDownWindow.Open<FieldSelectionWindow>(this, windowPos);
-                fieldSelection.OnFieldSelected += OnFieldAdded;
+                Action openPropertyWindow = () =>
+                {
+                    Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
+                    FieldSelectionWindow fieldSelection = DropDownWindow.Open<FieldSelectionWindow>(this, windowPos);
+                    fieldSelection.OnFieldSelected += OnFieldAdded;
+                };
+
+                if (clipInfo.clip == null)
+                {
+                    LocEdString title = new LocEdString("Warning");
+                    LocEdString message =
+                        new LocEdString("Selected object doesn't have an animation clip assigned. Would you like to create" +
+                                        " a new animation clip?");
+
+                    DialogBox.Open(title, message, DialogBox.Type.YesNoCancel, type =>
+                    {
+                        if (type == DialogBox.ResultType.Yes)
+                        {
+                            string[] clipSavePaths;
+                            if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out clipSavePaths))
+                            {
+                                if (clipSavePaths.Length > 0)
+                                {
+                                    AnimationClip newClip = new AnimationClip();
+
+                                    ProjectLibrary.Create(newClip, clipSavePaths[0]);
+                                    LoadAnimClip(newClip);
+
+                                    Animation animation = selectedSO.GetComponent<Animation>();
+                                    if (animation == null)
+                                        animation = selectedSO.AddComponent<Animation>();
+
+                                    animation.DefaultClip = newClip;
+                                    EditorApplication.SetSceneDirty();
+
+                                    openPropertyWindow();
+                                }
+                            }
+                        }
+                    });
+                }
+                else
+                {
+                    if (IsClipImported(clipInfo.clip))
+                    {
+                        LocEdString title = new LocEdString("Warning");
+                        LocEdString message =
+                            new LocEdString("You cannot add/edit/remove curves from animation clips that" +
+                                            " are imported from an external file.");
+
+                        DialogBox.Open(title, message, DialogBox.Type.OK);
+                    }
+                    else
+                        openPropertyWindow();
+                }
             };
             };
 
 
             delPropertyBtn.OnClick += () =>
             delPropertyBtn.OnClick += () =>
             {
             {
-                LocEdString title = new LocEdString("Warning");
-                LocEdString message = new LocEdString("Are you sure you want to remove all selected fields?");
+                if (clipInfo.clip == null)
+                    return;
+
+                if (IsClipImported(clipInfo.clip))
+                {
+                    LocEdString title = new LocEdString("Warning");
+                    LocEdString message =
+                        new LocEdString("You cannot add/edit/remove curves from animation clips that" +
+                                        " are imported from an external file.");
 
 
-                DialogBox.Open(title, message, DialogBox.Type.YesNo, x =>
+                    DialogBox.Open(title, message, DialogBox.Type.OK);
+                }
+                else
                 {
                 {
-                    if (x == DialogBox.ResultType.Yes)
+                    LocEdString title = new LocEdString("Warning");
+                    LocEdString message = new LocEdString("Are you sure you want to remove all selected fields?");
+
+                    DialogBox.Open(title, message, DialogBox.Type.YesNo, x =>
                     {
                     {
-                        RemoveSelectedFields();
-                    }
-                });
+                        if (x == DialogBox.ResultType.Yes)
+                        {
+                            RemoveSelectedFields();
+                        }
+                    });
+                }
             };
             };
 
 
             GUILayout mainLayout = GUI.AddLayoutY();
             GUILayout mainLayout = GUI.AddLayoutY();
@@ -291,6 +345,10 @@ namespace BansheeEditor
             Vector2I curveEditorSize = GetCurveEditorSize();
             Vector2I curveEditorSize = GetCurveEditorSize();
             guiCurveEditor = new GUICurveEditor(this, editorPanel, curveEditorSize.x, curveEditorSize.y);
             guiCurveEditor = new GUICurveEditor(this, editorPanel, curveEditorSize.x, curveEditorSize.y);
             guiCurveEditor.OnFrameSelected += OnFrameSelected;
             guiCurveEditor.OnFrameSelected += OnFrameSelected;
+            guiCurveEditor.OnEventAdded += OnEventsChanged;
+            guiCurveEditor.OnEventModified += EditorApplication.SetProjectDirty;
+            guiCurveEditor.OnEventDeleted += OnEventsChanged;
+            guiCurveEditor.OnCurveModified += EditorApplication.SetProjectDirty;
             guiCurveEditor.Redraw();
             guiCurveEditor.Redraw();
 
 
             horzScrollBar.SetWidth(curveEditorSize.x);
             horzScrollBar.SetWidth(curveEditorSize.x);
@@ -298,8 +356,6 @@ namespace BansheeEditor
 
 
             SetCurrentFrame(currentFrameIdx);
             SetCurrentFrame(currentFrameIdx);
             UpdateScrollBarSize();
             UpdateScrollBarSize();
-
-            isInitialized = true;
         }
         }
 
 
         private void ResizeGUI(int width, int height)
         private void ResizeGUI(int width, int height)
@@ -470,420 +526,85 @@ namespace BansheeEditor
         #endregion
         #endregion
 
 
         #region Curve save/load
         #region Curve save/load
-        /// <summary>
-        /// A set of animation curves for a field of a certain type.
-        /// </summary>
-        private struct FieldCurves
-        {
-            public SerializableProperty.FieldType type;
-            public EdAnimationCurve[] curves;
-        }
-
-        [SerializeObject]
-        private class EditorVector3CurveTangents
-        {
-            public string name;
-            public TangentMode[] tangentsX;
-            public TangentMode[] tangentsY;
-            public TangentMode[] tangentsZ;
-        }
+        private EditorAnimClipInfo clipInfo;
 
 
-        [SerializeObject]
-        private class EditorFloatCurveTangents
+        private void LoadAnimClip(AnimationClip clip)
         {
         {
-            public string name;
-            public TangentMode[] tangents;
-        }
-
-        [SerializeObject]
-        private class EditorCurveData
-        {
-            public EditorVector3CurveTangents[] positionCurves;
-            public EditorVector3CurveTangents[] rotationCurves;
-            public EditorVector3CurveTangents[] scaleCurves;
-            public EditorFloatCurveTangents[] floatCurves;
-        }
+            EditorPersistentData persistentData = EditorApplication.PersistentData;
 
 
-        private Dictionary<string, FieldCurves> curves = new Dictionary<string, FieldCurves>();
-        private bool clipIsImported;
-
-        private void LoadFromClip(AnimationClip clip)
-        {
-            curves.Clear();
-            selectedFields.Clear();
-            guiFieldDisplay.SetFields(new string[0]);
-
-            clipIsImported = IsClipImported(clip);
-
-            AnimationCurves clipCurves = clip.Curves;
-            EditorCurveData editorCurveData = null;
-
-            string resourcePath = ProjectLibrary.GetPath(clip);
-            if (!string.IsNullOrEmpty(resourcePath))
+            bool clipIsImported = IsClipImported(clip);
+            if (persistentData.dirtyAnimClips.TryGetValue(clip.UUID, out clipInfo))
             {
             {
-                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
-                string clipName = PathEx.GetTail(resourcePath);
-
-                if (entry != null && entry.Type == LibraryEntryType.File)
-                {
-                    FileEntry fileEntry = (FileEntry)entry;
-                    ResourceMeta[] metas = fileEntry.ResourceMetas;
-
-                    for (int i = 0; i < metas.Length; i++)
-                    {
-                        if (clipName == metas[i].SubresourceName)
-                        {
-                            editorCurveData = metas[i].EditorData as EditorCurveData;
-                            break;
-                        }
-                    }
-                }
-            }
-
-            if(editorCurveData == null)
-                editorCurveData = new EditorCurveData();
-
-            Action<NamedVector3Curve[], EditorVector3CurveTangents[], string> loadVector3Curve =
-                (curves, tangents, subPath) =>
+                // If an animation clip is imported, we don't care about it's cached curve values as they could have changed
+                // since last modification, so we re-load the clip. But we persist the events as those can only be set
+                // within the editor.
+                if (clipIsImported)
                 {
                 {
-                    foreach (var curveEntry in curves)
-                    {
-                        TangentMode[] tangentsX = null;
-                        TangentMode[] tangentsY = null;
-                        TangentMode[] tangentsZ = null;
-                        foreach (var tangentEntry in tangents)
-                        {
-                            if (tangentEntry.name == curveEntry.Name)
-                            {
-                                tangentsX = tangentEntry.tangentsX;
-                                tangentsY = tangentEntry.tangentsY;
-                                tangentsZ = tangentEntry.tangentsZ;
-                                break;
-                            }
-                        }
-
-                        FieldCurves fieldCurves = new FieldCurves();
-                        fieldCurves.type = SerializableProperty.FieldType.Vector3;
-                        fieldCurves.curves = new EdAnimationCurve[3];
-
-                        fieldCurves.curves[0] = new EdAnimationCurve(curveEntry.X, tangentsX);
-                        fieldCurves.curves[1] = new EdAnimationCurve(curveEntry.Y, tangentsY);
-                        fieldCurves.curves[2] = new EdAnimationCurve(curveEntry.Z, tangentsZ);
-
-                        string curvePath = curveEntry.Name.TrimEnd('/') + subPath;
-                        guiFieldDisplay.AddField(curvePath);
-                        this.curves[curvePath] = fieldCurves;
-                    }
-                };
-
-            loadVector3Curve(clipCurves.PositionCurves, editorCurveData.positionCurves, "/Position");
-            loadVector3Curve(clipCurves.RotationCurves, editorCurveData.rotationCurves, "/Rotation");
-            loadVector3Curve(clipCurves.ScaleCurves, editorCurveData.scaleCurves, "/Scale");
-
-            // Find which individual float curves belong to the same field
-            Dictionary<string, Tuple<int, int, bool>[]> floatCurveMapping = new Dictionary<string, Tuple<int, int, bool>[]>();
-            {
-                int curveIdx = 0;
-                foreach (var curveEntry in clipCurves.FloatCurves)
-                {
-                    string path = curveEntry.Name;
-                    string pathNoSuffix = null;
-
-                    string pathSuffix;
-                    if (path.Length >= 2)
-                    {
-                        pathSuffix = path.Substring(path.Length - 2, 2);
-                        pathNoSuffix = path.Substring(0, path.Length - 2);
-                    }
-                    else
-                        pathSuffix = "";
-
-                    int tangentIdx = -1;
-                    int currentTangentIdx = 0;
-                    foreach (var tangentEntry in editorCurveData.floatCurves)
-                    {
-                        if (tangentEntry.name == curveEntry.Name)
-                        {
-                            tangentIdx = currentTangentIdx;
-                            break;
-                        }
-
-                        currentTangentIdx++;
-                    }
-
-                    Animation.PropertySuffixInfo suffixInfo;
-                    if (Animation.PropertySuffixInfos.TryGetValue(pathSuffix, out suffixInfo))
-                    {
-                        Tuple<int, int, bool>[] curveInfo;
-                        if (!floatCurveMapping.TryGetValue(pathNoSuffix, out curveInfo))
-                            curveInfo = new Tuple<int, int, bool>[4];
-
-                        curveInfo[suffixInfo.elementIdx] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);
-                        floatCurveMapping[pathNoSuffix] = curveInfo;
-                    }
-                    else
-                    {
-                        Tuple<int, int, bool>[] curveInfo = new Tuple<int, int, bool>[4];
-                        curveInfo[0] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);
-
-                        floatCurveMapping[path] = curveInfo;
-                    }
-
-                    curveIdx++;
+                    EditorAnimClipInfo newClipInfo = EditorAnimClipInfo.Create(clip);
+                    newClipInfo.events = clipInfo.events;
                 }
                 }
             }
             }
+            else
+                clipInfo = EditorAnimClipInfo.Create(clip);
 
 
-            foreach (var KVP in floatCurveMapping)
-            {
-                int numCurves = 0;
-                for (int i = 0; i < 4; i++)
-                {
-                    if (KVP.Value[i] == null)
-                        continue;
-
-                    numCurves++;
-                }
-
-                if (numCurves == 0)
-                    continue; // Invalid curve
-
-                FieldCurves fieldCurves = new FieldCurves();
-
-                // Deduce type (note that all single value types are assumed to be float even if their source type is int or bool)
-                if (numCurves == 1)
-                    fieldCurves.type = SerializableProperty.FieldType.Float;
-                else if (numCurves == 2)
-                    fieldCurves.type = SerializableProperty.FieldType.Vector2;
-                else if (numCurves == 3)
-                    fieldCurves.type = SerializableProperty.FieldType.Vector3;
-                else // 4 curves
-                {
-                    bool isVector = KVP.Value[0].Item3;
-                    if (isVector)
-                        fieldCurves.type = SerializableProperty.FieldType.Vector4;
-                    else
-                        fieldCurves.type = SerializableProperty.FieldType.Color;
-                }
-
-                fieldCurves.curves = new EdAnimationCurve[numCurves];
-
-                for (int i = 0; i < numCurves; i++)
-                {
-                    int curveIdx = KVP.Value[i].Item1;
-                    int tangentIdx = KVP.Value[i].Item2;
-
-                    TangentMode[] tangents = null;
-                    if (tangentIdx != -1)
-                        tangents = editorCurveData.floatCurves[tangentIdx].tangents;
-
-                    fieldCurves.curves[i] = new EdAnimationCurve(clipCurves.FloatCurves[curveIdx].Curve, tangents);
-                }
+            persistentData.dirtyAnimClips[clip.UUID] = clipInfo;
 
 
-                string curvePath = KVP.Key;
+            foreach (var curve in clipInfo.curves)
+                guiFieldDisplay.AddField(curve.Key);
 
 
-                guiFieldDisplay.AddField(curvePath);
-                curves[curvePath] = fieldCurves;
-            }
+            guiCurveEditor.Events = clipInfo.events;
+            guiCurveEditor.DisableCurveEdit = clipIsImported;
+        }
 
 
-            // Add events
-            guiCurveEditor.Events = clip.Events;
+        private static bool IsClipImported(AnimationClip clip)
+        {
+            string resourcePath = ProjectLibrary.GetPath(clip);
+            return ProjectLibrary.IsSubresource(resourcePath);
         }
         }
 
 
-        private void SaveToClip(AnimationClip clip, string saveToPath)
+        private void UpdateSelectedSO()
         {
         {
-            if (!clipIsImported)
+            SceneObject so = Selection.SceneObject;
+            if (selectedSO != so)
             {
             {
-                List<NamedVector3Curve> positionCurves = new List<NamedVector3Curve>();
-                List<NamedVector3Curve> rotationCurves = new List<NamedVector3Curve>();
-                List<NamedVector3Curve> scaleCurves = new List<NamedVector3Curve>();
-                List<NamedFloatCurve> floatCurves = new List<NamedFloatCurve>();
-
-                List<EditorVector3CurveTangents> positionTangents = new List<EditorVector3CurveTangents>();
-                List<EditorVector3CurveTangents> rotationTangents = new List<EditorVector3CurveTangents>();
-                List<EditorVector3CurveTangents> scaleTangents = new List<EditorVector3CurveTangents>();
-                List<EditorFloatCurveTangents> floatTangents = new List<EditorFloatCurveTangents>();
-
-                foreach (var kvp in curves)
+                if (selectedSO != null && so == null)
                 {
                 {
-                    string[] pathEntries = kvp.Key.Split('/');
-                    if (pathEntries.Length == 0)
-                        continue;
-
-                    string lastEntry = pathEntries[pathEntries.Length - 1];
-
-                    if (lastEntry == "Position" || lastEntry == "Rotation" || lastEntry == "Scale")
-                    {
-                        StringBuilder sb = new StringBuilder();
-                        for (int i = 0; i < pathEntries.Length - 2; i++)
-                            sb.Append(pathEntries[i] + "/");
-
-                        if (pathEntries.Length > 1)
-                            sb.Append(pathEntries[pathEntries.Length - 2]);
-
-                        string curvePath = sb.ToString();
-
-                        NamedVector3Curve curve = new NamedVector3Curve(curvePath,
-                            new AnimationCurve(kvp.Value.curves[0].KeyFrames),
-                            new AnimationCurve(kvp.Value.curves[1].KeyFrames),
-                            new AnimationCurve(kvp.Value.curves[2].KeyFrames));
-
-                        EditorVector3CurveTangents tangents = new EditorVector3CurveTangents();
-                        tangents.name = curvePath;
-                        tangents.tangentsX = kvp.Value.curves[0].TangentModes;
-                        tangents.tangentsY = kvp.Value.curves[1].TangentModes;
-                        tangents.tangentsZ = kvp.Value.curves[2].TangentModes;
-
-                        if (lastEntry == "Position")
-                        {
-                            positionCurves.Add(curve);
-                            positionTangents.Add(tangents);
-                        }
-                        else if (lastEntry == "Rotation")
-                        {
-                            rotationCurves.Add(curve);
-                            rotationTangents.Add(tangents);
-                        }
-                        else if (lastEntry == "Scale")
-                        {
-                            scaleCurves.Add(curve);
-                            scaleTangents.Add(tangents);
-                        }
-                    }
-                    else
-                    {
-                        Action<int, string> addCurve = (idx, subPath) =>
-                        {
-                            string path = kvp.Key + subPath;
-
-                            NamedFloatCurve curve = new NamedFloatCurve(path,
-                            new AnimationCurve(kvp.Value.curves[idx].KeyFrames));
-
-                            EditorFloatCurveTangents tangents = new EditorFloatCurveTangents();
-                            tangents.name = path;
-                            tangents.tangents = kvp.Value.curves[idx].TangentModes;
-
-                            floatCurves.Add(curve);
-                            floatTangents.Add(tangents);
-                        };
-
-                        switch (kvp.Value.type)
-                        {
-                            case SerializableProperty.FieldType.Vector2:
-                                addCurve(0, ".x");
-                                addCurve(1, ".y");
-                                break;
-                            case SerializableProperty.FieldType.Vector3:
-                                addCurve(0, ".x");
-                                addCurve(1, ".y");
-                                addCurve(2, ".z");
-                                break;
-                            case SerializableProperty.FieldType.Vector4:
-                                addCurve(0, ".x");
-                                addCurve(1, ".y");
-                                addCurve(2, ".z");
-                                addCurve(3, ".w");
-                                break;
-                            case SerializableProperty.FieldType.Color:
-                                addCurve(0, ".r");
-                                addCurve(1, ".g");
-                                addCurve(2, ".b");
-                                addCurve(3, ".a");
-                                break;
-                            case SerializableProperty.FieldType.Bool:
-                            case SerializableProperty.FieldType.Int:
-                            case SerializableProperty.FieldType.Float:
-                                addCurve(0, "");
-                                break;
-                        }
-                    }
+                    EditorInput.OnPointerPressed -= OnPointerPressed;
+                    EditorInput.OnPointerMoved -= OnPointerMoved;
+                    EditorInput.OnPointerReleased -= OnPointerReleased;
+                    EditorInput.OnButtonUp -= OnButtonUp;
                 }
                 }
-
-                AnimationCurves newClipCurves = new AnimationCurves();
-                newClipCurves.PositionCurves = positionCurves.ToArray();
-                newClipCurves.RotationCurves = rotationCurves.ToArray();
-                newClipCurves.ScaleCurves = scaleCurves.ToArray();
-                newClipCurves.FloatCurves = floatCurves.ToArray();
-
-                clip.Curves = newClipCurves;
-                clip.Events = guiCurveEditor.Events;
-
-                string resourcePath = ProjectLibrary.GetPath(clip);
-                if (string.IsNullOrEmpty(resourcePath))
-                    ProjectLibrary.Create(clip, saveToPath);
-                else
-                    ProjectLibrary.Save(clip);
-
-                // Save tangents for editor only use
-                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
-                string clipName = PathEx.GetTail(resourcePath);
-
-                if (entry != null && entry.Type == LibraryEntryType.File)
+                else if (selectedSO == null && so != null)
                 {
                 {
-                    FileEntry fileEntry = (FileEntry)entry;
-                    ResourceMeta[] metas = fileEntry.ResourceMetas;
-
-                    for (int i = 0; i < metas.Length; i++)
-                    {
-                        if (clipName == metas[i].SubresourceName)
-                        {
-                            EditorCurveData newCurveData = new EditorCurveData();
-                            newCurveData.positionCurves = positionTangents.ToArray();
-                            newCurveData.rotationCurves = rotationTangents.ToArray();
-                            newCurveData.scaleCurves = scaleTangents.ToArray();
-                            newCurveData.floatCurves = floatTangents.ToArray();
-
-                            ProjectLibrary.SetEditorData(resourcePath, newCurveData);
-                            break;
-                        }
-                    }
+                    EditorInput.OnPointerPressed += OnPointerPressed;
+                    EditorInput.OnPointerMoved += OnPointerMoved;
+                    EditorInput.OnPointerReleased += OnPointerReleased;
+                    EditorInput.OnButtonUp += OnButtonUp;
                 }
                 }
-            }
-            else
-            {
-                string resourcePath = ProjectLibrary.GetPath(clip);
-                LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath);
 
 
-                if (entry != null && entry.Type == LibraryEntryType.File)
-                {
-                    FileEntry fileEntry = (FileEntry) entry;
-                    MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options;
+                zoomAmount = 0.0f;
+                selectedSO = so;
+                selectedFields.Clear();
 
 
-                    string clipName = PathEx.GetTail(resourcePath);
+                RebuildGUI();
 
 
-                    List<ImportedAnimationEvents> newEvents = new List<ImportedAnimationEvents>();
-                    newEvents.AddRange(meshImportOptions.AnimationEvents);
+                clipInfo = null;
 
 
-                    bool isExisting = false;
-                    for (int i = 0; i < newEvents.Count; i++)
-                    {
-                        if (newEvents[i].name == clipName)
-                        {
-                            newEvents[i].events = guiCurveEditor.Events;
-                            isExisting = true;
-                            break;
-                        }
-                    }
-
-                    if (!isExisting)
+                // Load existing clip if one exists
+                if (selectedSO != null)
+                {
+                    Animation animation = selectedSO.GetComponent<Animation>();
+                    if (animation != null)
                     {
                     {
-                        ImportedAnimationEvents newEntry = new ImportedAnimationEvents();
-                        newEntry.name = clipName;
-                        newEntry.events = guiCurveEditor.Events;
-
-                        newEvents.Add(newEntry);
+                        AnimationClip clip = animation.DefaultClip;
+                        if (clip != null)
+                            LoadAnimClip(clip);
                     }
                     }
-
-                    meshImportOptions.AnimationEvents = newEvents.ToArray();
-
-                    ProjectLibrary.Reimport(resourcePath, meshImportOptions, true);
                 }
                 }
-            }
-        }
 
 
-        private bool IsClipImported(AnimationClip clip)
-        {
-            string resourcePath = ProjectLibrary.GetPath(clip);
-            return ProjectLibrary.IsSubresource(resourcePath);
+                if(clipInfo == null)
+                    clipInfo = new EditorAnimClipInfo();
+            }
         }
         }
 
 
         #endregion
         #endregion
@@ -909,7 +630,7 @@ namespace BansheeEditor
             float time = guiCurveEditor.GetTimeForFrame(currentFrameIdx);
             float time = guiCurveEditor.GetTimeForFrame(currentFrameIdx);
 
 
             List<GUIAnimFieldPathValue> values = new List<GUIAnimFieldPathValue>();
             List<GUIAnimFieldPathValue> values = new List<GUIAnimFieldPathValue>();
-            foreach (var kvp in curves)
+            foreach (var kvp in clipInfo.curves)
             {
             {
                 string suffix;
                 string suffix;
                 SerializableProperty property = Animation.FindProperty(selectedSO, kvp.Key, out suffix);
                 SerializableProperty property = Animation.FindProperty(selectedSO, kvp.Key, out suffix);
@@ -1035,7 +756,7 @@ namespace BansheeEditor
             {
             {
                 case SerializableProperty.FieldType.Vector4:
                 case SerializableProperty.FieldType.Vector4:
                     {
                     {
-                        FieldCurves fieldCurves = new FieldCurves();
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
                         fieldCurves.type = type;
                         fieldCurves.type = type;
                         fieldCurves.curves = new EdAnimationCurve[4];
                         fieldCurves.curves = new EdAnimationCurve[4];
 
 
@@ -1047,12 +768,12 @@ namespace BansheeEditor
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
 
 
-                        curves[path] = fieldCurves;
+                        clipInfo.curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 case SerializableProperty.FieldType.Vector3:
                 case SerializableProperty.FieldType.Vector3:
                     {
                     {
-                        FieldCurves fieldCurves = new FieldCurves();
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
                         fieldCurves.type = type;
                         fieldCurves.type = type;
                         fieldCurves.curves = new EdAnimationCurve[3];
                         fieldCurves.curves = new EdAnimationCurve[3];
 
 
@@ -1064,12 +785,12 @@ namespace BansheeEditor
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
 
 
-                        curves[path] = fieldCurves;
+                        clipInfo.curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 case SerializableProperty.FieldType.Vector2:
                 case SerializableProperty.FieldType.Vector2:
                     {
                     {
-                        FieldCurves fieldCurves = new FieldCurves();
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
                         fieldCurves.type = type;
                         fieldCurves.type = type;
                         fieldCurves.curves = new EdAnimationCurve[2];
                         fieldCurves.curves = new EdAnimationCurve[2];
 
 
@@ -1081,12 +802,12 @@ namespace BansheeEditor
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
 
 
-                        curves[path] = fieldCurves;
+                        clipInfo.curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 case SerializableProperty.FieldType.Color:
                 case SerializableProperty.FieldType.Color:
                     {
                     {
-                        FieldCurves fieldCurves = new FieldCurves();
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
                         fieldCurves.type = type;
                         fieldCurves.type = type;
                         fieldCurves.curves = new EdAnimationCurve[4];
                         fieldCurves.curves = new EdAnimationCurve[4];
 
 
@@ -1098,23 +819,24 @@ namespace BansheeEditor
                             selectedFields.Add(subFieldPath);
                             selectedFields.Add(subFieldPath);
                         }
                         }
 
 
-                        curves[path] = fieldCurves;
+                        clipInfo.curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
                 default: // Primitive type
                 default: // Primitive type
                     {
                     {
-                        FieldCurves fieldCurves = new FieldCurves();
+                        FieldAnimCurves fieldCurves = new FieldAnimCurves();
                         fieldCurves.type = type;
                         fieldCurves.type = type;
                         fieldCurves.curves = new EdAnimationCurve[1];
                         fieldCurves.curves = new EdAnimationCurve[1];
 
 
                         fieldCurves.curves[0] = new EdAnimationCurve();
                         fieldCurves.curves[0] = new EdAnimationCurve();
                         selectedFields.Add(path);
                         selectedFields.Add(path);
 
 
-                        curves[path] = fieldCurves;
+                        clipInfo.curves[path] = fieldCurves;
                     }
                     }
                     break;
                     break;
             }
             }
 
 
+            EditorApplication.SetProjectDirty();
             UpdateDisplayedCurves();
             UpdateDisplayedCurves();
         }
         }
 
 
@@ -1139,16 +861,17 @@ namespace BansheeEditor
             for (int i = 0; i < selectedFields.Count; i++)
             for (int i = 0; i < selectedFields.Count; i++)
             {
             {
                 selectedFields.Remove(selectedFields[i]);
                 selectedFields.Remove(selectedFields[i]);
-                curves.Remove(GetSubPathParent(selectedFields[i]));
+                clipInfo.curves.Remove(GetSubPathParent(selectedFields[i]));
             }
             }
 
 
             List<string> existingFields = new List<string>();
             List<string> existingFields = new List<string>();
-            foreach (var KVP in curves)
+            foreach (var KVP in clipInfo.curves)
                 existingFields.Add(KVP.Key);
                 existingFields.Add(KVP.Key);
 
 
             guiFieldDisplay.SetFields(existingFields.ToArray());
             guiFieldDisplay.SetFields(existingFields.ToArray());
 
 
             selectedFields.Clear();
             selectedFields.Clear();
+            EditorApplication.SetProjectDirty();
             UpdateDisplayedCurves();
             UpdateDisplayedCurves();
         }
         }
         #endregion
         #endregion
@@ -1195,8 +918,8 @@ namespace BansheeEditor
                 subPathSuffix = path.Substring(index, path.Length - index);
                 subPathSuffix = path.Substring(index, path.Length - index);
             }
             }
 
 
-            FieldCurves fieldCurves;
-            if (curves.TryGetValue(parentPath, out fieldCurves))
+            FieldAnimCurves fieldCurves;
+            if (clipInfo.curves.TryGetValue(parentPath, out fieldCurves))
             {
             {
                 if (!string.IsNullOrEmpty(subPathSuffix))
                 if (!string.IsNullOrEmpty(subPathSuffix))
                 {
                 {
@@ -1264,9 +987,6 @@ namespace BansheeEditor
         #region Input callbacks
         #region Input callbacks
         private void OnPointerPressed(PointerEvent ev)
         private void OnPointerPressed(PointerEvent ev)
         {
         {
-            if (!isInitialized)
-                return;
-
             guiCurveEditor.OnPointerPressed(ev);
             guiCurveEditor.OnPointerPressed(ev);
 
 
             if (ev.button == PointerButton.Middle)
             if (ev.button == PointerButton.Middle)
@@ -1283,9 +1003,6 @@ namespace BansheeEditor
 
 
         private void OnPointerMoved(PointerEvent ev)
         private void OnPointerMoved(PointerEvent ev)
         {
         {
-            if (!isInitialized)
-                return;
-
             guiCurveEditor.OnPointerMoved(ev);
             guiCurveEditor.OnPointerMoved(ev);
 
 
             if (isButtonHeld)
             if (isButtonHeld)
@@ -1321,17 +1038,11 @@ namespace BansheeEditor
             isButtonHeld = false;
             isButtonHeld = false;
             isDragInProgress = false;
             isDragInProgress = false;
 
 
-            if (!isInitialized)
-                return;
-
             guiCurveEditor.OnPointerReleased(ev);
             guiCurveEditor.OnPointerReleased(ev);
         }
         }
 
 
         private void OnButtonUp(ButtonEvent ev)
         private void OnButtonUp(ButtonEvent ev)
         {
         {
-            if (!isInitialized)
-                return;
-
             guiCurveEditor.OnButtonUp(ev);
             guiCurveEditor.OnButtonUp(ev);
         }
         }
         #endregion
         #endregion
@@ -1368,13 +1079,19 @@ namespace BansheeEditor
 
 
         private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
         private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
         {
         {
-            RebuildGUI();
+            UpdateSelectedSO();
         }
         }
 
 
         private void OnFrameSelected(int frameIdx)
         private void OnFrameSelected(int frameIdx)
         {
         {
             SetCurrentFrame(frameIdx);
             SetCurrentFrame(frameIdx);
         }
         }
+
+        private void OnEventsChanged()
+        {
+            clipInfo.events = guiCurveEditor.Events;
+            EditorApplication.SetProjectDirty();
+        }
         #endregion
         #endregion
     }
     }
 
 

+ 14 - 5
Source/MBansheeEngine/Animation/AnimationClip.cs

@@ -15,6 +15,18 @@ namespace BansheeEngine
     /// </summary>
     /// </summary>
     public class AnimationClip : Resource
     public class AnimationClip : Resource
     {
     {
+        // Constructor for runtime use only (dummy parameter to differentiate from the normal constructor)
+        private AnimationClip(bool dummy)
+        { }
+
+        /// <summary>
+        /// Creates a new animation clip with no curves or events.
+        /// </summary>
+        public AnimationClip()
+        {
+            Internal_CreateInstance(this);
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the length of the animation clip, in seconds.
         /// Returns the length of the animation clip, in seconds.
         /// </summary>
         /// </summary>
@@ -41,11 +53,8 @@ namespace BansheeEngine
             set { Internal_SetAnimationEvents(mCachedPtr, value); }
             set { Internal_SetAnimationEvents(mCachedPtr, value); }
         }
         }
 
 
-        /// <summary>
-        /// Constructor for internal use by the runtime.
-        /// </summary>
-        private AnimationClip()
-        { }
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(AnimationClip instance);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern AnimationCurves Internal_GetAnimationCurves(IntPtr thisPtr);
         private static extern AnimationCurves Internal_GetAnimationCurves(IntPtr thisPtr);

+ 1 - 0
Source/SBansheeEngine/Include/BsScriptAnimationClip.h

@@ -29,6 +29,7 @@ namespace BansheeEngine
 		/************************************************************************/
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		/************************************************************************/
+		static void internal_CreateInstance(MonoObject* instance);
 		static MonoObject* internal_GetAnimationCurves(ScriptAnimationClip* thisPtr);
 		static MonoObject* internal_GetAnimationCurves(ScriptAnimationClip* thisPtr);
 		static void internal_SetAnimationCurves(ScriptAnimationClip* thisPtr, MonoObject* curves);
 		static void internal_SetAnimationCurves(ScriptAnimationClip* thisPtr, MonoObject* curves);
 		static MonoArray* internal_GetAnimationEvents(ScriptAnimationClip* thisPtr);
 		static MonoArray* internal_GetAnimationEvents(ScriptAnimationClip* thisPtr);

+ 16 - 1
Source/SBansheeEngine/Source/BsScriptAnimationClip.cpp

@@ -4,6 +4,7 @@
 #include "BsScriptAnimationCurves.h"
 #include "BsScriptAnimationCurves.h"
 #include "BsScriptMeta.h"
 #include "BsScriptMeta.h"
 #include "BsMonoClass.h"
 #include "BsMonoClass.h"
+#include "BsScriptResourceManager.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -15,6 +16,7 @@ namespace BansheeEngine
 
 
 	void ScriptAnimationClip::initRuntimeData()
 	void ScriptAnimationClip::initRuntimeData()
 	{
 	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptAnimationClip::internal_CreateInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetAnimationCurves", &ScriptAnimationClip::internal_GetAnimationCurves);
 		metaData.scriptClass->addInternalCall("Internal_GetAnimationCurves", &ScriptAnimationClip::internal_GetAnimationCurves);
 		metaData.scriptClass->addInternalCall("Internal_SetAnimationCurves", &ScriptAnimationClip::internal_SetAnimationCurves);
 		metaData.scriptClass->addInternalCall("Internal_SetAnimationCurves", &ScriptAnimationClip::internal_SetAnimationCurves);
 		metaData.scriptClass->addInternalCall("Internal_GetAnimationEvents", &ScriptAnimationClip::internal_GetAnimationEvents);
 		metaData.scriptClass->addInternalCall("Internal_GetAnimationEvents", &ScriptAnimationClip::internal_GetAnimationEvents);
@@ -24,7 +26,20 @@ namespace BansheeEngine
 
 
 	MonoObject* ScriptAnimationClip::createInstance()
 	MonoObject* ScriptAnimationClip::createInstance()
 	{
 	{
-		return metaData.scriptClass->createInstance();
+		bool dummy = false;
+
+		void* params[1];
+		params[0] = &dummy;
+
+		return metaData.scriptClass->createInstance("bool", params);
+	}
+
+	void ScriptAnimationClip::internal_CreateInstance(MonoObject* instance)
+	{
+		HAnimationClip clip = AnimationClip::create();
+
+		ScriptAnimationClip* scriptInstance;
+		ScriptResourceManager::instance().createScriptResource(instance, clip, &scriptInstance);
 	}
 	}
 
 
 	MonoObject* ScriptAnimationClip::internal_GetAnimationCurves(ScriptAnimationClip* thisPtr)
 	MonoObject* ScriptAnimationClip::internal_GetAnimationCurves(ScriptAnimationClip* thisPtr)