static void SaveCloneToAsset(Object clone, Object newOwner) { var containerPath = AssetDatabase.GetAssetPath(newOwner); var containerAsset = AssetDatabase.LoadAssetAtPath <Object>(containerPath); if (containerAsset != null) { TimelineCreateUtilities.SaveAssetIntoObject(clone, containerAsset); EditorUtility.SetDirty(containerAsset); } }
static void AddMarkerToParent(ScriptableObject marker, TrackAsset parent) { TimelineCreateUtilities.SaveAssetIntoObject(marker, parent); TimelineUndo.RegisterCreatedObjectUndo(marker, "Duplicate Marker"); TimelineUndo.PushUndo(parent, "Duplicate Marker"); if (parent != null) { parent.AddMarker(marker); ((IMarker)marker).Initialize(parent); } }
internal static void RecursiveSubtrackClone(TrackAsset source, TrackAsset duplicate, PlayableDirector director) { List <TrackAsset> subTracks = source.subTracks; foreach (TrackAsset current in subTracks) { TrackAsset trackAsset = TimelineHelpers.Clone(duplicate, current, director); duplicate.AddChild(trackAsset); TrackExtensions.RecursiveSubtrackClone(current, trackAsset, director); Undo.RegisterCreatedObjectUndo(trackAsset, "Duplicate"); TimelineCreateUtilities.SaveAssetIntoObject(trackAsset, source); } }
internal static void RecursiveSubtrackClone(TrackAsset source, TrackAsset duplicate, PlayableDirector director, PlayableAsset assetOwner) { var subtracks = source.GetChildTracks(); foreach (var sub in subtracks) { var newSub = TimelineHelpers.Clone(duplicate, sub, director, assetOwner); duplicate.AddChild(newSub); RecursiveSubtrackClone(sub, newSub, director, assetOwner); // registration has to happen AFTER recursion TimelineUndo.RegisterCreatedObjectUndo(newSub, "Duplicate"); TimelineCreateUtilities.SaveAssetIntoObject(newSub, assetOwner); } }
internal static bool Duplicate(this TrackAsset track, PlayableDirector director, TimelineAsset destinationTimeline = null) { bool result; if (track == null) { result = false; } else { if (destinationTimeline == track.timelineAsset) { destinationTimeline = null; } TimelineAsset timelineAsset = track.parent as TimelineAsset; TrackAsset trackAsset = track.parent as TrackAsset; if (timelineAsset == null && trackAsset == null) { Debug.LogWarning("Cannot duplicate track because it is not parented to known type"); result = false; } else { PlayableAsset playableAsset = destinationTimeline ?? track.parent; TrackAsset trackAsset2 = TimelineHelpers.Clone(playableAsset, track, director); TrackExtensions.RecursiveSubtrackClone(track, trackAsset2, director); Undo.RegisterCreatedObjectUndo(trackAsset2, "Duplicate"); TimelineCreateUtilities.SaveAssetIntoObject(trackAsset2, playableAsset); TimelineUndo.PushUndo(playableAsset, "Duplicate"); if (destinationTimeline != null) { destinationTimeline.AddTrackInternal(trackAsset2); } else if (timelineAsset != null) { TrackExtensions.ReparentTracks(new List <TrackAsset> { trackAsset2 }, timelineAsset, track, false); } else { trackAsset.AddChildAfter(trackAsset2, track); } result = true; } } return(result); }
internal static TimelineClip Clone(TimelineClip clip, PlayableDirector director, PlayableAsset newOwner) { var editorClip = EditorClipFactory.GetEditorClip(clip); // Workaround for Clips not being unity object, assign it to a editor clip wrapper, clone it, and pull the clip back out var newClip = Object.Instantiate(editorClip).clip; // perform fix ups for what Instantiate cannot properly detect SelectionManager.Remove(newClip); newClip.parentTrack = null; newClip.curves = null; // instantiate might copy the reference, we need to clear it // curves are explicitly owned by the clip if (clip.curves != null) { newClip.CreateCurves(AnimationTrackRecorder.GetUniqueRecordedClipName(newOwner, clip.curves.name)); EditorUtility.CopySerialized(clip.curves, newClip.curves); TimelineCreateUtilities.SaveAssetIntoObject(newClip.curves, newOwner); } ScriptableObject playableAsset = newClip.asset as ScriptableObject; if (playableAsset != null && newClip.asset is IPlayableAsset) { var clone = CloneReferencedPlayableAsset(playableAsset, director); SaveCloneToAsset(clone, newOwner); newClip.asset = clone; // special case to make the name match the recordable clips, but only if they match on the original clip var originalRecordedAsset = clip.asset as AnimationPlayableAsset; if (clip.recordable && originalRecordedAsset != null && originalRecordedAsset.clip != null) { AnimationPlayableAsset clonedAnimationAsset = clone as AnimationPlayableAsset; if (clonedAnimationAsset != null && clonedAnimationAsset.clip != null) { clonedAnimationAsset.clip = CloneAnimationClip(originalRecordedAsset.clip, newOwner); if (clip.displayName == originalRecordedAsset.clip.name && newClip.recordable) { clonedAnimationAsset.name = clonedAnimationAsset.clip.name; newClip.displayName = clonedAnimationAsset.name; } } } } return(newClip); }
static void RecursiveSubtrackClone(TrackAsset source, TrackAsset duplicate, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset assetOwner) { var subtracks = source.GetChildTracks(); foreach (var sub in subtracks) { var newSub = TimelineHelpers.Clone(duplicate, sub, sourceTable, destTable, assetOwner); duplicate.AddChild(newSub); RecursiveSubtrackClone(sub, newSub, sourceTable, destTable, assetOwner); // Call the custom editor on Create var customEditor = CustomTimelineEditorCache.GetTrackEditor(newSub); customEditor.OnCreate_Safe(newSub, sub); // registration has to happen AFTER recursion TimelineCreateUtilities.SaveAssetIntoObject(newSub, assetOwner); TimelineUndo.RegisterCreatedObjectUndo(newSub, L10n.Tr("Duplicate")); } }
static void SaveCloneToAsset(Object clone, Object newOwner) { if (newOwner == null) { return; } var containerPath = AssetDatabase.GetAssetPath(newOwner); var containerAsset = AssetDatabase.LoadAssetAtPath <Object>(containerPath); if (containerAsset != null) { TimelineCreateUtilities.SaveAssetIntoObject(clone, containerAsset); EditorUtility.SetDirty(containerAsset); } else { clone.hideFlags |= newOwner.hideFlags & HideFlags.DontSave; } }
public static TimelineClip CreateClipOnTrack(Type playableAssetType, TrackAsset parentTrack, ITimelineState state, Vector2 mousePosition) { TimelineClip result; if (!typeof(IPlayableAsset).IsAssignableFrom(playableAssetType) || !typeof(ScriptableObject).IsAssignableFrom(playableAssetType)) { result = null; } else { ScriptableObject scriptableObject = ScriptableObject.CreateInstance(playableAssetType); if (scriptableObject == null) { throw new InvalidOperationException("Could not create an instance of the ScriptableObject type " + playableAssetType.Name); } scriptableObject.set_name(playableAssetType.Name); TimelineCreateUtilities.SaveAssetIntoObject(scriptableObject, parentTrack); result = TimelineHelpers.CreateClipOnTrack(scriptableObject, parentTrack, state, mousePosition); } return(result); }
public static TimelineClip AddRecordableClip(TrackAsset parentTrack, TimelineWindow.TimelineState state) { TimelineAsset timeline = state.timeline; TimelineClip result; if (timeline == null) { Debug.LogError("Parent Track needs to be bound to an asset to add a recordable"); result = null; } else { AnimationClip animationClip = new AnimationClip(); animationClip.set_name(AnimationTrackRecorder.GetUniqueRecordedClipName(parentTrack, AnimationTrackRecorder.kRecordClipDefaultName)); animationClip.set_frameRate(state.frameRate); AnimationUtility.SetGenerateMotionCurves(animationClip, true); Undo.RegisterCreatedObjectUndo(animationClip, "Create Clip"); TimelineHelpers.SaveAnimClipIntoObject(animationClip, parentTrack); TimelineClip timelineClip = parentTrack.CreateClipFromAsset(animationClip); if (timelineClip != null) { timelineClip.recordable = true; timelineClip.displayName = animationClip.get_name(); timelineClip.timeScale = 1.0; timelineClip.start = 0.0; timelineClip.duration = 0.0; timelineClip.mixInCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); timelineClip.mixOutCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f); timelineClip.preExtrapolationMode = TimelineClip.ClipExtrapolation.Hold; timelineClip.postExtrapolationMode = TimelineClip.ClipExtrapolation.Hold; TimelineCreateUtilities.SaveAssetIntoObject(timelineClip.asset, parentTrack); state.Refresh(); } result = timelineClip; } return(result); }
public DragAndDropVisualMode HandleObjectDrop(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, TreeViewDragging.DropPosition dropPos) { DragAndDropVisualMode result; if (!perform) { List <Object> list = new List <Object>(); if (DragAndDrop.get_objectReferences().Any <Object>()) { list.AddRange(DragAndDrop.get_objectReferences()); using (List <Object> .Enumerator enumerator = list.GetEnumerator()) { while (enumerator.MoveNext()) { Object o = enumerator.Current; TrackType[] trackTypeHandle = TimelineHelpers.GetTrackTypeHandle(o.GetType()); if (!trackTypeHandle.Any <TrackType>() || trackTypeHandle.Any((TrackType t) => !TimelineDragging.ValidateObjectDrop(o))) { result = 0; return(result); } } } result = 1; } else if (DragAndDrop.get_paths().Any <string>()) { TimelineGroupGUI timelineGroupGUI = targetItem as TimelineGroupGUI; if (timelineGroupGUI == null) { result = 1; } else if (timelineGroupGUI.track == null) { result = 0; } else { TrackType tt = TimelineHelpers.TrackTypeFromType(timelineGroupGUI.track.GetType()); string[] paths = DragAndDrop.get_paths(); for (int i = 0; i < paths.Length; i++) { string text = paths[i]; list.AddRange(AssetDatabase.LoadAllAssetsAtPath(text)); } bool flag = (from o in list where o != null select o).Any((Object o) => TimelineHelpers.IsTypeSupportedByTrack(tt, o.GetType())); result = ((!flag) ? 0 : 1); } } else { result = 0; } } else { List <Object> list2 = new List <Object>(); list2.AddRange(DragAndDrop.get_objectReferences()); TrackAsset trackAsset = null; TimelineGroupGUI timelineGroupGUI2 = (TimelineGroupGUI)targetItem; if (targetItem != null && timelineGroupGUI2.track != null) { trackAsset = timelineGroupGUI2.track; } Vector2 mousePosition = Event.get_current().get_mousePosition(); foreach (Object current in list2) { if (!(current == null)) { if (current is TimelineAsset) { if (TimelineHelpers.IsCircularRef(this.m_Timeline, current as TimelineAsset)) { Debug.LogError("Cannot add " + current.get_name() + " to the sequence because it would cause a circular reference"); continue; } TimelineAsset timelineAsset = current as TimelineAsset; foreach (TrackAsset current2 in timelineAsset.tracks) { if (current2.mediaType == TimelineAsset.MediaType.Group) { this.m_Timeline.AddTrackInternal(current2); } } this.m_Window.state.Refresh(); EditorUtility.SetDirty(this.m_Timeline); } else if (current is TrackAsset) { this.m_Timeline.AddTrackInternal((TrackAsset)current); } else { TrackType trackType = null; TrackAsset trackAsset2 = null; TrackType[] trackTypeHandle2 = TimelineHelpers.GetTrackTypeHandle(current.GetType()); if (trackAsset != null) { TrackType[] array = trackTypeHandle2; for (int j = 0; j < array.Length; j++) { TrackType trackType2 = array[j]; if (trackAsset.GetType() == trackType2.trackType) { trackType = trackType2; break; } } } if (trackType == null) { if (trackTypeHandle2.Length == 1) { trackType = trackTypeHandle2[0]; } else if (trackTypeHandle2.Length > 1) { trackType = this.ResolveTypeAmbiguity(trackTypeHandle2); } } if (trackType == null) { continue; } if (!TimelineDragging.ValidateObjectDrop(current)) { continue; } if (trackAsset != null && trackAsset.GetType() == trackType.trackType) { trackAsset2 = trackAsset; } if (trackAsset2 == null) { trackAsset2 = this.m_Window.AddTrack(trackType.trackType, null, string.Empty); } if (trackAsset2 == null) { result = 0; return(result); } Object @object = TimelineDragging.TransformObjectBeingDroppedAccordingToTrackRules(trackAsset2, current); if (@object == null) { continue; } TimelineUndo.PushUndo(trackAsset2, "Create Clip"); AnimationTrack animationTrack = trackAsset2 as AnimationTrack; if (animationTrack != null) { animationTrack.ConvertToClipMode(); } TimelineClip timelineClip = TimelineHelpers.CreateClipOnTrack(@object, trackAsset2, this.m_Window.state, mousePosition); if (timelineClip != null) { float num = this.m_Window.state.TimeToPixel(1.0) - this.m_Window.state.TimeToPixel(0.0); mousePosition.x += (float)timelineClip.duration * num; if (timelineClip.asset is ScriptableObject) { string assetPath = AssetDatabase.GetAssetPath(timelineClip.asset); if (assetPath.Length == 0) { TimelineCreateUtilities.SaveAssetIntoObject(timelineClip.asset, trackAsset2); } } TimelineDragging.FrameClips(this.m_Window.state); trackAsset2.SetCollapsed(false); } } this.m_Window.state.Refresh(); EditorUtility.SetDirty(this.m_Timeline); } } result = 1; } return(result); }
public DragAndDropVisualMode HandleTrackDrop(TreeViewItem parentItem, TreeViewItem targetItem, bool perform, DropPosition dropPos) { ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = false; var trackDragData = (TimelineDragData)DragAndDrop.GetGenericData(k_GenericDragId); bool validDrag = ValidDrag(targetItem, trackDragData.draggedItems); if (!validDrag) return DragAndDropVisualMode.None; var draggedTracks = trackDragData.draggedItems.OfType<TimelineGroupGUI>().Select(x => x.track).ToList(); if (draggedTracks.Count == 0) return DragAndDropVisualMode.None; if (parentItem != null) { var parentActor = parentItem as TimelineGroupGUI; if (parentActor != null && parentActor.track != null) { if (parentActor.track.lockedInHierarchy) return DragAndDropVisualMode.Rejected; if (draggedTracks.Any(x => !TimelineCreateUtilities.ValidateParentTrack(parentActor.track, x.GetType()))) return DragAndDropVisualMode.Rejected; } } var insertAfterItem = targetItem as TimelineGroupGUI; if (insertAfterItem != null && insertAfterItem.track != null) { ((TimelineTreeView)m_Window.treeView.gui).showInsertionMarker = true; } if (dropPos == DropPosition.Upon) { var groupGUI = targetItem as TimelineGroupGUI; if (groupGUI != null) groupGUI.isDropTarget = true; } if (perform) { PlayableAsset targetParent = m_Timeline; var parentActor = parentItem as TimelineGroupGUI; if (parentActor != null && parentActor.track != null) targetParent = parentActor.track; TrackAsset siblingTrack = insertAfterItem != null ? insertAfterItem.track : null; // where the user drops after the last track, make sure to place it after all the tracks if (targetParent == m_Timeline && dropPos == DropPosition.Below && siblingTrack == null) { siblingTrack = m_Timeline.GetRootTracks().LastOrDefault(x => !draggedTracks.Contains(x)); } if (TrackExtensions.ReparentTracks(TrackExtensions.FilterTracks(draggedTracks).ToList(), targetParent, siblingTrack, dropPos == DropPosition.Above)) { m_Window.state.Refresh(); } } return DragAndDropVisualMode.Move; }
// Reparents a list of tracks to a new parent // the new parent cannot be null (has to be track asset or sequence) // the insertAfter can be null (will not reorder) internal static bool ReparentTracks(List <TrackAsset> tracksToMove, PlayableAsset targetParent, TrackAsset insertMarker = null, bool insertBefore = false) { var targetParentTrack = targetParent as TrackAsset; var targetSequenceTrack = targetParent as TimelineAsset; if (tracksToMove == null || tracksToMove.Count == 0 || (targetParentTrack == null && targetSequenceTrack == null)) { return(false); } // invalid parent type on a track if (targetParentTrack != null && tracksToMove.Any(x => !TimelineCreateUtilities.ValidateParentTrack(targetParentTrack, x.GetType()))) { return(false); } // no valid tracks means this is simply a rearrangement var validTracks = tracksToMove.Where(x => x.parent != targetParent).ToList(); if (insertMarker == null && !validTracks.Any()) { return(false); } var parents = validTracks.Select(x => x.parent).Where(x => x != null).Distinct().ToList(); // push the current state of the tracks that will change foreach (var p in parents) { TimelineUndo.PushUndo(p, "Reparent"); } foreach (var t in validTracks) { TimelineUndo.PushUndo(t, "Reparent"); } TimelineUndo.PushUndo(targetParent, "Reparent"); // need to reparent tracks first, before moving them. foreach (var t in validTracks) { if (t.parent != targetParent) { TrackAsset toMoveParent = t.parent as TrackAsset; TimelineAsset toMoveTimeline = t.parent as TimelineAsset; if (toMoveTimeline != null) { toMoveTimeline.RemoveTrack(t); } else if (toMoveParent != null) { toMoveParent.RemoveSubTrack(t); } if (targetParentTrack != null) { targetParentTrack.AddChild(t); targetParentTrack.SetCollapsed(false); } else { targetSequenceTrack.AddTrackInternal(t); } } } if (insertMarker != null) { // re-ordering track. This is using internal APIs, so invalidation of the tracks must be done manually to avoid // cache mismatches var children = targetParentTrack != null ? targetParentTrack.subTracksObjects : targetSequenceTrack.trackObjects; TimelineUtility.ReorderTracks(children, tracksToMove, insertMarker, insertBefore); if (targetParentTrack != null) { targetParentTrack.Invalidate(); } if (insertMarker.timelineAsset != null) { insertMarker.timelineAsset.Invalidate(); } } return(true); }
internal static TrackAsset Duplicate(this TrackAsset track, PlayableDirector director, TimelineAsset destinationTimeline = null) { if (track == null) { return(null); } // if the destination is us, clear to avoid bad parenting (case 919421) if (destinationTimeline == track.timelineAsset) { destinationTimeline = null; } var timelineParent = track.parent as TimelineAsset; var trackParent = track.parent as TrackAsset; if (timelineParent == null && trackParent == null) { Debug.LogWarning("Cannot duplicate track because it is not parented to known type"); return(null); } // Determine who the final parent is. If we are pasting into another track, it's always the timeline. // Otherwise it's the original parent PlayableAsset finalParent = destinationTimeline != null ? destinationTimeline : track.parent; // grab the list of tracks to generate a name from (923360) to get the list of names // no need to do this part recursively var finalTrackParent = finalParent as TrackAsset; var finalTimelineAsset = finalParent as TimelineAsset; var otherTracks = (finalTimelineAsset != null) ? finalTimelineAsset.trackObjects : finalTrackParent.subTracksObjects; // Important to create the new objects before pushing the original undo, or redo breaks the // sequence var newTrack = TimelineHelpers.Clone(finalParent, track, director, finalParent); newTrack.name = TimelineCreateUtilities.GenerateUniqueActorName(otherTracks, newTrack.name); RecursiveSubtrackClone(track, newTrack, director, finalParent); TimelineUndo.RegisterCreatedObjectUndo(newTrack, "Duplicate"); TimelineCreateUtilities.SaveAssetIntoObject(newTrack, finalParent); TimelineUndo.PushUndo(finalParent, "Duplicate"); if (destinationTimeline != null) // other timeline { destinationTimeline.AddTrackInternal(newTrack); } else if (timelineParent != null) // this timeline, no parent { ReparentTracks(new List <TrackAsset> { newTrack }, timelineParent, timelineParent.GetRootTracks().Last(), false); } else // this timeline, with parent { trackParent.AddChild(newTrack); } // Call the custom editor. this check prevents the call when copying to the clipboard if (destinationTimeline == null || destinationTimeline == TimelineEditor.inspectedAsset) { var customEditor = CustomTimelineEditorCache.GetTrackEditor(newTrack); try { customEditor.OnCreate(newTrack, track); } catch (Exception e) { Debug.LogException(e); } } return(newTrack); }