Modelowanie_Wirtualnych_Swi.../Library/PackageCache/com.unity.timeline@1.5.4/Editor/Recording/AnimationTrackRecorder.cs
2021-07-09 23:35:24 +02:00

307 lines
11 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
class AnimationTrackRecorder
{
public static readonly string kRecordClipDefaultName = L10n.Tr("Recorded");
AnimationClip m_TargetClip;
int m_CurveCount = 0;
double m_ClipTime;
bool m_needRebuildRects;
bool m_TrackHasPreviewComponents;
public TimelineClip recordClip { get; private set; }
public void PrepareForRecord(WindowState state)
{
m_CurveCount = 0;
m_TargetClip = null;
m_TrackHasPreviewComponents = false;
}
public AnimationClip PrepareTrack(TrackAsset track, WindowState state, GameObject gameObject, out double startTime)
{
AnimationClip animationClip = null;
// if we are not in clip mode, we simply use the track clip
var animationTrack = (AnimationTrack)track;
// ignore recording if we are in Legacy auto mode
startTime = -1;
var parentTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
if (parentTrack != null && parentTrack.trackOffset == TrackOffset.Auto)
return null;
if (!animationTrack.inClipMode)
{
var trackClip = animationTrack.GetOrCreateClip();
startTime = trackClip.frameRate * state.editSequence.time;
// Make the first key be at time 0 of the clip
if (trackClip.empty)
{
animationTrack.infiniteClipTimeOffset = 0; // state.time;
animationTrack.infiniteClipPreExtrapolation = TimelineClip.ClipExtrapolation.Hold;
animationTrack.infiniteClipPostExtrapolation = TimelineClip.ClipExtrapolation.Hold;
}
animationClip = trackClip;
}
else
{
TimelineClip activeClip = null;
// if it fails, but returns no clip, we can add one.
if (!track.FindRecordingClipAtTime(state.editSequence.time, out activeClip) && activeClip != null)
{
return null;
}
if (activeClip == null)
{
activeClip = AddRecordableClip(track, state, state.editSequence.time);
}
var clip = activeClip.animationClip;
// flags this as the clip being recorded for the track
var clipTime = state.editSequence.time - activeClip.start;
// if we are in the past
if (clipTime < 0)
{
Undo.RegisterCompleteObjectUndo(clip, L10n.Tr("Record Key"));
UndoExtensions.RegisterTrack(track, L10n.Tr("Prepend Key"));
ShiftAnimationClip(clip, (float)-clipTime);
activeClip.start = state.editSequence.time;
activeClip.duration += -clipTime;
clipTime = 0;
}
m_ClipTime = clipTime;
recordClip = activeClip;
startTime = recordClip.ToLocalTimeUnbound(state.editSequence.time) * clip.frameRate;
m_needRebuildRects = clip.empty;
animationClip = clip;
}
m_TargetClip = animationClip;
m_CurveCount = GetCurveCount(animationClip);
m_TrackHasPreviewComponents = animationTrack.hasPreviewComponents;
return animationClip;
}
static int GetCurveCount(AnimationClip animationClip)
{
int count = 0;
if (animationClip != null)
{
var clipCache = AnimationClipCurveCache.Instance.GetCurveInfo(animationClip);
count = clipCache.curves.Length + clipCache.objectCurves.Count;
}
return count;
}
public void FinializeTrack(TrackAsset track, WindowState state)
{
// make sure we dirty the clip if we are in non clip mode
var animTrack = track as AnimationTrack;
if (!animTrack.inClipMode)
{
EditorUtility.SetDirty(animTrack.GetOrCreateClip());
}
// in clip mode we need to do some extra work
if (recordClip != null)
{
// stretch the clip out to meet the new recording time
if (m_ClipTime > recordClip.duration)
{
UndoExtensions.RegisterTrack(track, L10n.Tr("Add Key"));
recordClip.duration = m_ClipTime;
}
track.CalculateExtrapolationTimes();
}
recordClip = null;
m_ClipTime = 0;
if (m_needRebuildRects)
{
state.CalculateRowRects();
m_needRebuildRects = false;
}
}
public void FinalizeRecording(WindowState state)
{
// rebuild the graph if we add/remove a clip. Rebuild the graph with an evaluation immediately
// so previews and scene position is maintained.
if (m_CurveCount != GetCurveCount(m_TargetClip))
{
state.rebuildGraph = true;
state.GetWindow().RebuildGraphIfNecessary(true);
}
else if (m_TrackHasPreviewComponents)
{
// Track with preview components potentially has modifications impacting other properties that need
// to be refreshed before inspector or scene view to not interfere with manipulation.
state.EvaluateImmediate();
}
}
// For a given track asset get a unique clip name
public static string GetUniqueRecordedClipName(Object owner, string name)
{
// first attempt -- uniquely named in file
var path = AssetDatabase.GetAssetPath(owner);
if (!string.IsNullOrEmpty(path))
{
var names = AssetDatabase.LoadAllAssetsAtPath(path).Where(x => x != null).Select(x => x.name);
return ObjectNames.GetUniqueName(names.ToArray(), name);
}
TrackAsset asset = owner as TrackAsset;
if (asset == null || asset.clips.Length == 0)
return name;
// final attempt - uniquely named in track
return ObjectNames.GetUniqueName(asset.clips.Select(x => x.displayName).ToArray(), name);
}
// Given an appropriate parent track, create a recordable clip
public static TimelineClip AddRecordableClip(TrackAsset parentTrack, WindowState state, double atTime)
{
var sequenceAsset = state.editSequence.asset;
if (sequenceAsset == null)
{
Debug.LogError("Parent Track needs to be bound to an asset to add a recordable");
return null;
}
var animTrack = parentTrack as AnimationTrack;
if (animTrack == null)
{
Debug.LogError("Recordable clips are only valid on Animation Tracks");
return null;
}
var newClip = animTrack.CreateRecordableClip(GetUniqueRecordedClipName(parentTrack, kRecordClipDefaultName));
if (newClip == null)
{
Debug.LogError("Could not create a recordable clip");
return null;
}
newClip.mixInCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
newClip.mixOutCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
newClip.preExtrapolationMode = TimelineClip.ClipExtrapolation.Hold;
newClip.postExtrapolationMode = TimelineClip.ClipExtrapolation.Hold;
double startTime = 0;
double endTime = 0;
GetAddedRecordingClipRange(animTrack, state, atTime, out startTime, out endTime);
newClip.start = startTime;
newClip.duration = endTime - startTime;
state.Refresh();
return newClip;
}
// get the start and end times of what an added recording clip at a given time would be
internal static void GetAddedRecordingClipRange(TrackAsset track, WindowState state, double atTime, out double start, out double end)
{
// size to make the clip in pixels. Reasonably big so that both handles are easily manipulated,
// and the full title is normally visible
double defaultDuration = state.PixelDeltaToDeltaTime(100);
start = atTime;
end = atTime + defaultDuration;
double gapStart = 0;
double gapEnd = 0;
// no gap, pick are reasonable amount
if (!track.GetGapAtTime(atTime, out gapStart, out gapEnd))
{
start = atTime;
return;
}
if (!double.IsInfinity(gapEnd))
end = gapEnd;
start = TimeReferenceUtility.SnapToFrameIfRequired(start);
end = TimeReferenceUtility.SnapToFrameIfRequired(end);
}
// Given a clip, shifts the keys in that clip by the given amount.
internal static void ShiftAnimationClip(AnimationClip clip, float amount)
{
if (clip == null)
return;
var curveBindings = AnimationUtility.GetCurveBindings(clip);
var objectCurveBindings = AnimationUtility.GetObjectReferenceCurveBindings(clip);
foreach (var binding in curveBindings)
{
AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);
curve.keys = ShiftKeys(curve.keys, amount);
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
foreach (var binding in objectCurveBindings)
{
ObjectReferenceKeyframe[] keyframes = AnimationUtility.GetObjectReferenceCurve(clip, binding);
keyframes = ShiftObjectKeys(keyframes, amount);
AnimationUtility.SetObjectReferenceCurve(clip, binding, keyframes);
}
EditorUtility.SetDirty(clip);
}
// shift all the keys over by the given time, stretching the time 0 key
static Keyframe[] ShiftKeys(Keyframe[] keys, float time)
{
if (keys == null || keys.Length == 0 || time == 0)
return keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time += time;
}
return keys;
}
// Shift object keys over by the appropriate amount
static ObjectReferenceKeyframe[] ShiftObjectKeys(ObjectReferenceKeyframe[] keys, float time)
{
if (keys == null || keys.Length == 0 || time == 0)
return keys;
for (int i = 0; i < keys.Length; i++)
{
keys[i].time += time;
}
return keys;
}
}
}