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++); } } }
/// <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); }
/// <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); }