private void UpdateCurveColors()
        {
            int globalCurveIdx = 0;

            foreach (var curveGroup in clipInfo.curves)
            {
                for (int i = 0; i < curveGroup.Value.curveInfos.Length; i++)
                {
                    curveGroup.Value.curveInfos[i].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);
                }
            }
        }
Example #2
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;
            clipInfo.isImported = IsClipImported(clip);
            clipInfo.sampleRate = (int)clip.SampleRate;

            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;

                    if (clipInfo.isImported)
                    {
                        for (int i = 0; i < metas.Length; i++)
                        {
                            if (clipName == metas[i].SubresourceName)
                            {
                                editorCurveData = metas[i].EditorData as EditorAnimClipTangents;
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (metas.Length > 0)
                        {
                            editorCurveData = metas[0].EditorData as EditorAnimClipTangents;
                        }
                    }
                }
            }

            if (editorCurveData == null)
            {
                editorCurveData = new EditorAnimClipTangents();
            }

            int globalCurveIdx = 0;
            Action <NamedVector3Curve[], EditorVector3CurveTangents[], string> loadVector3Curve =
                (curves, tangents, subPath) =>
            {
                foreach (var curveEntry in curves)
                {
                    TangentMode[] tangentsX = null;
                    TangentMode[] tangentsY = null;
                    TangentMode[] tangentsZ = null;

                    if (tangents != null)
                    {
                        foreach (var tangentEntry in tangents)
                        {
                            if (tangentEntry.name == curveEntry.name)
                            {
                                tangentsX = tangentEntry.tangentsX;
                                tangentsY = tangentEntry.tangentsY;
                                tangentsZ = tangentEntry.tangentsZ;
                                break;
                            }
                        }
                    }

                    // Convert compound curve to three per-component curves
                    AnimationCurve[] componentCurves = AnimationUtility.SplitCurve(curveEntry.curve);

                    FieldAnimCurves fieldCurves = new FieldAnimCurves();
                    fieldCurves.type            = SerializableProperty.FieldType.Vector3;
                    fieldCurves.curveInfos      = new CurveDrawInfo[3];
                    fieldCurves.isPropertyCurve = !clipInfo.isImported;

                    fieldCurves.curveInfos[0]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[0].curve = new EdAnimationCurve(componentCurves[0], tangentsX);
                    fieldCurves.curveInfos[0].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    fieldCurves.curveInfos[1]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[1].curve = new EdAnimationCurve(componentCurves[1], tangentsY);
                    fieldCurves.curveInfos[1].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    fieldCurves.curveInfos[2]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[2].curve = new EdAnimationCurve(componentCurves[2], tangentsZ);
                    fieldCurves.curveInfos[2].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    string curvePath = curveEntry.name.TrimEnd('/') + subPath;
                    clipInfo.curves[curvePath] = fieldCurves;
                }
            };

            // Convert rotation from quaternion to euler
            NamedQuaternionCurve[] rotationCurves      = clipCurves.Rotation;
            NamedVector3Curve[]    eulerRotationCurves = new NamedVector3Curve[rotationCurves.Length];
            for (int i = 0; i < rotationCurves.Length; i++)
            {
                eulerRotationCurves[i]       = new NamedVector3Curve();
                eulerRotationCurves[i].name  = rotationCurves[i].name;
                eulerRotationCurves[i].flags = rotationCurves[i].flags;
                eulerRotationCurves[i].curve = AnimationUtility.QuaternionToEulerCurve(rotationCurves[i].curve);
            }

            loadVector3Curve(clipCurves.Position, editorCurveData.positionCurves, "/Position");
            loadVector3Curve(eulerRotationCurves, editorCurveData.rotationCurves, "/Rotation");
            loadVector3Curve(clipCurves.Scale, 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.Generic)
                {
                    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;
                    }
                }

                bool   isMorphCurve = false;
                string curvePath    = KVP.Key;

                fieldCurves.curveInfos = new CurveDrawInfo[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.curveInfos[i]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[i].curve = new EdAnimationCurve(clipCurves.Generic[curveIdx].curve, tangents);
                    fieldCurves.curveInfos[i].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    if (clipCurves.Generic[curveIdx].flags.HasFlag(AnimationCurveFlags.MorphFrame))
                    {
                        curvePath    = "MorphShapes/Frames/" + KVP.Key;
                        isMorphCurve = true;
                    }
                    else if (clipCurves.Generic[curveIdx].flags.HasFlag(AnimationCurveFlags.MorphWeight))
                    {
                        curvePath    = "MorphShapes/Weight/" + KVP.Key;
                        isMorphCurve = true;
                    }
                }

                fieldCurves.isPropertyCurve = !clipInfo.isImported && !isMorphCurve;

                clipInfo.curves[curvePath] = fieldCurves;
            }

            // Add events
            clipInfo.events = clip.Events;
            return(clipInfo);
        }
Example #3
0
        /// <summary>
        /// Creates a new curve editor GUI elements.
        /// </summary>
        /// <param name="window">Parent window of the GUI element.</param>
        /// <param name="gui">GUI layout into which to place the GUI element.</param>
        /// <param name="width">Width in pixels.</param>
        /// <param name="height">Height in pixels.</param>
        public GUICurveEditor(AnimationWindow window, GUILayout gui, int width, int height)
        {
            this.window = window;
            this.gui    = gui;

            this.width  = width;
            this.height = height;

            blankContextMenu = new ContextMenu();
            blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);

            blankEventContextMenu = new ContextMenu();
            blankEventContextMenu.AddItem("Add event", AddEventAtPosition);

            keyframeContextMenu = new ContextMenu();
            keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
            keyframeContextMenu.AddItem("Edit", EditSelectedKeyframe);
            keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
            keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
            keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
            keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
            keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
            keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
            keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
            keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
            keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
            keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });

            eventContextMenu = new ContextMenu();
            eventContextMenu.AddItem("Delete", DeleteSelectedEvents);
            eventContextMenu.AddItem("Edit", EditSelectedEvent);

            GUIPanel timelinePanel = gui.AddPanel();

            guiTimeline = new GUIGraphTime(timelinePanel, width, TIMELINE_HEIGHT);

            GUIPanel timelineBgPanel = gui.AddPanel(1);

            GUITexture timelineBackground = new GUITexture(null, EditorStyles.Header);

            timelineBackground.Bounds = new Rect2I(0, 0, width, TIMELINE_HEIGHT + VERT_PADDING);
            timelineBgPanel.AddElement(timelineBackground);

            eventsPanel = gui.AddPanel();
            eventsPanel.SetPosition(0, TIMELINE_HEIGHT + VERT_PADDING);
            guiEvents = new GUIAnimEvents(eventsPanel, width, EVENTS_HEIGHT);

            GUIPanel eventsBgPanel = eventsPanel.AddPanel(1);

            GUITexture eventsBackground = new GUITexture(null, EditorStyles.Header);

            eventsBackground.Bounds = new Rect2I(0, 0, width, EVENTS_HEIGHT + VERT_PADDING);
            eventsBgPanel.AddElement(eventsBackground);

            drawingPanel = gui.AddPanel();
            drawingPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT + VERT_PADDING);

            guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT - EVENTS_HEIGHT - VERT_PADDING * 2, curveInfos);
            guiCurveDrawing.SetRange(60.0f, 20.0f);

            GUIPanel sidebarPanel = gui.AddPanel(-10);

            sidebarPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT + VERT_PADDING);

            guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT - EVENTS_HEIGHT - VERT_PADDING * 2);
            guiSidebar.SetRange(-10.0f, 10.0f);
        }
        /// <summary>
        /// Creates a new curve editor GUI elements.
        /// </summary>
        /// <param name="window">Parent window of the GUI element.</param>
        /// <param name="gui">GUI layout into which to place the GUI element.</param>
        /// <param name="width">Width in pixels.</param>
        /// <param name="height">Height in pixels.</param>
        public GUICurveEditor(AnimationWindow window, GUILayout gui, int width, int height)
        {
            this.window = window;
            this.gui = gui;

            this.width = width;
            this.height = height;

            blankContextMenu = new ContextMenu();
            blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);

            blankEventContextMenu = new ContextMenu();
            blankEventContextMenu.AddItem("Add event", AddEventAtPosition);

            keyframeContextMenu = new ContextMenu();
            keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
            keyframeContextMenu.AddItem("Edit", EditSelectedKeyframe);
            keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
            keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
            keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
            keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
            keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
            keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
            keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
            keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
            keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
            keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });

            eventContextMenu = new ContextMenu();
            eventContextMenu.AddItem("Delete", DeleteSelectedEvents);
            eventContextMenu.AddItem("Edit", EditSelectedEvent);

            GUIPanel timelinePanel = gui.AddPanel();
            guiTimeline = new GUIGraphTime(timelinePanel, width, TIMELINE_HEIGHT);

            GUIPanel timelineBgPanel = gui.AddPanel(1);

            GUITexture timelineBackground = new GUITexture(null, EditorStyles.Header);
            timelineBackground.Bounds = new Rect2I(0, 0, width, TIMELINE_HEIGHT + VERT_PADDING);
            timelineBgPanel.AddElement(timelineBackground);

            eventsPanel = gui.AddPanel();
            eventsPanel.SetPosition(0, TIMELINE_HEIGHT + VERT_PADDING);
            guiEvents = new GUIAnimEvents(eventsPanel, width, EVENTS_HEIGHT);

            GUIPanel eventsBgPanel = eventsPanel.AddPanel(1);

            GUITexture eventsBackground = new GUITexture(null, EditorStyles.Header);
            eventsBackground.Bounds = new Rect2I(0, 0, width, EVENTS_HEIGHT + VERT_PADDING);
            eventsBgPanel.AddElement(eventsBackground);

            drawingPanel = gui.AddPanel();
            drawingPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT + VERT_PADDING);

            guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT - EVENTS_HEIGHT - VERT_PADDING * 2, curveInfos);
            guiCurveDrawing.SetRange(60.0f, 20.0f);

            GUIPanel sidebarPanel = gui.AddPanel(-10);
            sidebarPanel.SetPosition(0, TIMELINE_HEIGHT + EVENTS_HEIGHT + VERT_PADDING);

            guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT - EVENTS_HEIGHT - VERT_PADDING * 2);
            guiSidebar.SetRange(-10.0f, 10.0f);
        }