first commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class AnimatedParameterExtensions
|
||||
{
|
||||
public static bool HasAnyAnimatableParameters(this ICurvesOwner curvesOwner)
|
||||
{
|
||||
return AnimatedParameterUtility.HasAnyAnimatableParameters(curvesOwner.asset);
|
||||
}
|
||||
|
||||
public static IEnumerable<SerializedProperty> GetAllAnimatableParameters(this ICurvesOwner curvesOwner)
|
||||
{
|
||||
return AnimatedParameterUtility.GetAllAnimatableParameters(curvesOwner.asset);
|
||||
}
|
||||
|
||||
public static bool IsParameterAnimatable(this ICurvesOwner curvesOwner, string parameterName)
|
||||
{
|
||||
return AnimatedParameterUtility.IsParameterAnimatable(curvesOwner.asset, parameterName);
|
||||
}
|
||||
|
||||
public static bool IsParameterAnimated(this ICurvesOwner curvesOwner, string parameterName)
|
||||
{
|
||||
return AnimatedParameterUtility.IsParameterAnimated(curvesOwner.asset, curvesOwner.curves, parameterName);
|
||||
}
|
||||
|
||||
public static EditorCurveBinding GetCurveBinding(this ICurvesOwner curvesOwner, string parameterName)
|
||||
{
|
||||
return AnimatedParameterUtility.GetCurveBinding(curvesOwner.asset, parameterName);
|
||||
}
|
||||
|
||||
public static string GetUniqueRecordedClipName(this ICurvesOwner curvesOwner)
|
||||
{
|
||||
return AnimationTrackRecorder.GetUniqueRecordedClipName(curvesOwner.assetOwner, curvesOwner.defaultCurvesName);
|
||||
}
|
||||
|
||||
public static AnimationCurve GetAnimatedParameter(this ICurvesOwner curvesOwner, string bindingName)
|
||||
{
|
||||
return AnimatedParameterUtility.GetAnimatedParameter(curvesOwner.asset, curvesOwner.curves, bindingName);
|
||||
}
|
||||
|
||||
public static bool AddAnimatedParameterValueAt(this ICurvesOwner curvesOwner, string parameterName, float value, float time)
|
||||
{
|
||||
if (!curvesOwner.IsParameterAnimatable(parameterName))
|
||||
return false;
|
||||
|
||||
if (curvesOwner.curves == null)
|
||||
curvesOwner.CreateCurves(curvesOwner.GetUniqueRecordedClipName());
|
||||
|
||||
var binding = curvesOwner.GetCurveBinding(parameterName);
|
||||
var curve = AnimationUtility.GetEditorCurve(curvesOwner.curves, binding) ?? new AnimationCurve();
|
||||
|
||||
var serializedObject = AnimatedParameterUtility.GetSerializedPlayableAsset(curvesOwner.asset);
|
||||
var property = serializedObject.FindProperty(parameterName);
|
||||
|
||||
bool isStepped = property.propertyType == SerializedPropertyType.Boolean ||
|
||||
property.propertyType == SerializedPropertyType.Integer ||
|
||||
property.propertyType == SerializedPropertyType.Enum;
|
||||
|
||||
TimelineUndo.PushUndo(curvesOwner.curves, "Set Key");
|
||||
CurveEditUtility.AddKeyFrameToCurve(curve, time, curvesOwner.curves.frameRate, value, isStepped);
|
||||
AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, curve);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SanitizeCurvesData(this ICurvesOwner curvesOwner)
|
||||
{
|
||||
var curves = curvesOwner.curves;
|
||||
if (curves == null)
|
||||
return;
|
||||
|
||||
// Remove any 0-length curves
|
||||
foreach (var binding in AnimationUtility.GetCurveBindings(curves))
|
||||
{
|
||||
var curve = AnimationUtility.GetEditorCurve(curves, binding);
|
||||
if (curve.length == 0)
|
||||
AnimationUtility.SetEditorCurve(curves, binding, null);
|
||||
}
|
||||
|
||||
// If no curves remain, delete the curves asset
|
||||
if (curves.empty)
|
||||
{
|
||||
var track = curvesOwner.targetTrack;
|
||||
var timeline = track != null ? track.timelineAsset : null;
|
||||
TimelineUndo.PushDestroyUndo(timeline, track, curves);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AddAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName)
|
||||
{
|
||||
var newBinding = new EditorCurveBinding();
|
||||
|
||||
SerializedProperty property;
|
||||
if (!InternalAddParameter(curvesOwner, parameterName, ref newBinding, out property))
|
||||
return false;
|
||||
|
||||
var duration = (float)curvesOwner.duration;
|
||||
CurveEditUtility.AddKey(curvesOwner.curves, newBinding, property, 0);
|
||||
CurveEditUtility.AddKey(curvesOwner.curves, newBinding, property, duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool RemoveAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName)
|
||||
{
|
||||
if (!curvesOwner.IsParameterAnimated(parameterName) || curvesOwner.curves == null)
|
||||
return false;
|
||||
|
||||
var binding = curvesOwner.GetCurveBinding(parameterName);
|
||||
AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set an animated parameter. Requires the field identifier 'position.x', but will add default curves to all fields
|
||||
public static bool SetAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName, AnimationCurve curve)
|
||||
{
|
||||
// this will add a basic curve for all the related parameters
|
||||
if (!curvesOwner.IsParameterAnimated(parameterName) && !curvesOwner.AddAnimatedParameter(parameterName))
|
||||
return false;
|
||||
|
||||
var binding = curvesOwner.GetCurveBinding(parameterName);
|
||||
AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, curve);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool InternalAddParameter([NotNull] ICurvesOwner curvesOwner, string parameterName, ref EditorCurveBinding binding, out SerializedProperty property)
|
||||
{
|
||||
property = null;
|
||||
|
||||
if (curvesOwner.IsParameterAnimated(parameterName))
|
||||
return false;
|
||||
|
||||
var serializedObject = AnimatedParameterUtility.GetSerializedPlayableAsset(curvesOwner.asset);
|
||||
if (serializedObject == null)
|
||||
return false;
|
||||
|
||||
property = serializedObject.FindProperty(parameterName);
|
||||
if (property == null || !AnimatedParameterUtility.IsTypeAnimatable(property.propertyType))
|
||||
return false;
|
||||
|
||||
if (curvesOwner.curves == null)
|
||||
curvesOwner.CreateCurves(curvesOwner.GetUniqueRecordedClipName());
|
||||
|
||||
binding = curvesOwner.GetCurveBinding(parameterName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d3aa106cfe752241997b3759bf80163
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension Methods for AnimationTracks that require the Unity Editor, and may require the Timeline containing the Animation Track to be currently loaded in the Timeline Editor Window.
|
||||
/// </summary>
|
||||
public static class AnimationTrackExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the Timeline window can enable recording mode on an AnimationTrack.
|
||||
/// For a track to support recording, it needs to have a valid scene binding,
|
||||
/// its offset mode should not be Auto and needs to be currently visible in the Timeline Window.
|
||||
/// </summary>
|
||||
/// <param name="track">The track to query.</param>
|
||||
/// <returns>True if recording can start, False otherwise.</returns>
|
||||
public static bool CanStartRecording(this AnimationTrack track)
|
||||
{
|
||||
if (track == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(track));
|
||||
}
|
||||
if (TimelineEditor.state == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var director = TimelineEditor.inspectedDirector;
|
||||
var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
|
||||
return animTrack != null && animTrack.trackOffset != TrackOffset.Auto &&
|
||||
TimelineEditor.inspectedAsset == animTrack.timelineAsset &&
|
||||
director != null && TimelineUtility.GetSceneGameObject(director, animTrack) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method that allows querying if a track is current enabled for animation recording.
|
||||
/// </summary>
|
||||
/// <param name="track">The track to query.</param>
|
||||
/// <returns>True if currently recording and False otherwise.</returns>
|
||||
public static bool IsRecording(this AnimationTrack track)
|
||||
{
|
||||
if (track == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(track));
|
||||
}
|
||||
return TimelineEditor.state != null && TimelineEditor.state.IsArmedForRecord(track);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method that enables animation recording for an AnimationTrack.
|
||||
/// </summary>
|
||||
/// <param name="track">The AnimationTrack which will be put in recording mode.</param>
|
||||
/// <returns>True if track was put successfully in recording mode, False otherwise. </returns>
|
||||
public static bool StartRecording(this AnimationTrack track)
|
||||
{
|
||||
if (!CanStartRecording(track))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TimelineEditor.state.ArmForRecord(track);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables recording mode of an AnimationTrack.
|
||||
/// </summary>
|
||||
/// <param name="track">The AnimationTrack which will be taken out of recording mode.</param>
|
||||
public static void StopRecording(this AnimationTrack track)
|
||||
{
|
||||
if (!IsRecording(track) || TimelineEditor.state == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TimelineEditor.state.UnarmForRecord(track);
|
||||
}
|
||||
|
||||
internal static void ConvertToClipMode(this AnimationTrack track)
|
||||
{
|
||||
if (!track.CanConvertToClipMode())
|
||||
return;
|
||||
|
||||
UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip"));
|
||||
|
||||
if (!track.infiniteClip.empty)
|
||||
{
|
||||
var animClip = track.infiniteClip;
|
||||
TimelineUndo.PushUndo(animClip, L10n.Tr("Convert To Clip"));
|
||||
UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip"));
|
||||
var start = AnimationClipCurveCache.Instance.GetCurveInfo(animClip).keyTimes.FirstOrDefault();
|
||||
animClip.ShiftBySeconds(-start);
|
||||
|
||||
track.infiniteClip = null;
|
||||
var clip = track.CreateClip(animClip);
|
||||
|
||||
clip.start = start;
|
||||
clip.preExtrapolationMode = track.infiniteClipPreExtrapolation;
|
||||
clip.postExtrapolationMode = track.infiniteClipPostExtrapolation;
|
||||
clip.recordable = true;
|
||||
if (Mathf.Abs(animClip.length) < TimelineClip.kMinDuration)
|
||||
{
|
||||
clip.duration = 1;
|
||||
}
|
||||
|
||||
var animationAsset = clip.asset as AnimationPlayableAsset;
|
||||
if (animationAsset)
|
||||
{
|
||||
animationAsset.position = track.infiniteClipOffsetPosition;
|
||||
animationAsset.eulerAngles = track.infiniteClipOffsetEulerAngles;
|
||||
|
||||
// going to / from infinite mode should reset this. infinite mode
|
||||
animationAsset.removeStartOffset = track.infiniteClipRemoveOffset;
|
||||
animationAsset.applyFootIK = track.infiniteClipApplyFootIK;
|
||||
animationAsset.loop = track.infiniteClipLoop;
|
||||
|
||||
track.infiniteClipOffsetPosition = Vector3.zero;
|
||||
track.infiniteClipOffsetEulerAngles = Vector3.zero;
|
||||
}
|
||||
|
||||
track.CalculateExtrapolationTimes();
|
||||
}
|
||||
|
||||
track.infiniteClip = null;
|
||||
|
||||
EditorUtility.SetDirty(track);
|
||||
}
|
||||
|
||||
internal static void ConvertFromClipMode(this AnimationTrack track, TimelineAsset timeline)
|
||||
{
|
||||
if (!track.CanConvertFromClipMode())
|
||||
return;
|
||||
|
||||
UndoExtensions.RegisterTrack(track, L10n.Tr("Convert From Clip"));
|
||||
|
||||
var clip = track.clips[0];
|
||||
var delta = (float)clip.start;
|
||||
track.infiniteClipTimeOffset = 0.0f;
|
||||
track.infiniteClipPreExtrapolation = clip.preExtrapolationMode;
|
||||
track.infiniteClipPostExtrapolation = clip.postExtrapolationMode;
|
||||
|
||||
var animAsset = clip.asset as AnimationPlayableAsset;
|
||||
if (animAsset)
|
||||
{
|
||||
track.infiniteClipOffsetPosition = animAsset.position;
|
||||
track.infiniteClipOffsetEulerAngles = animAsset.eulerAngles;
|
||||
track.infiniteClipRemoveOffset = animAsset.removeStartOffset;
|
||||
track.infiniteClipApplyFootIK = animAsset.applyFootIK;
|
||||
track.infiniteClipLoop = animAsset.loop;
|
||||
}
|
||||
|
||||
// clone it, it may not be in the same asset
|
||||
var animClip = clip.animationClip;
|
||||
|
||||
float scale = (float)clip.timeScale;
|
||||
if (!Mathf.Approximately(scale, 1.0f))
|
||||
{
|
||||
if (!Mathf.Approximately(scale, 0.0f))
|
||||
scale = 1.0f / scale;
|
||||
animClip.ScaleTime(scale);
|
||||
}
|
||||
|
||||
TimelineUndo.PushUndo(animClip, L10n.Tr("Convert From Clip"));
|
||||
animClip.ShiftBySeconds(delta);
|
||||
|
||||
// manually delete the clip
|
||||
var asset = clip.asset;
|
||||
clip.asset = null;
|
||||
|
||||
// Remove the clip, remove old assets
|
||||
ClipModifier.Delete(timeline, clip);
|
||||
TimelineUndo.PushDestroyUndo(null, track, asset);
|
||||
|
||||
track.infiniteClip = animClip;
|
||||
|
||||
EditorUtility.SetDirty(track);
|
||||
}
|
||||
|
||||
internal static bool CanConvertToClipMode(this AnimationTrack track)
|
||||
{
|
||||
if (track == null || track.inClipMode)
|
||||
return false;
|
||||
return (track.infiniteClip != null && !track.infiniteClip.empty);
|
||||
}
|
||||
|
||||
// Requirements to go from clip mode
|
||||
// - one clip, recordable, and animation clip belongs to the same asset as the track
|
||||
internal static bool CanConvertFromClipMode(this AnimationTrack track)
|
||||
{
|
||||
if ((track == null) ||
|
||||
(!track.inClipMode) ||
|
||||
(track.clips.Length != 1) ||
|
||||
(track.clips[0].start < 0) ||
|
||||
(!track.clips[0].recordable))
|
||||
return false;
|
||||
|
||||
var asset = track.clips[0].asset as AnimationPlayableAsset;
|
||||
if (asset == null)
|
||||
return false;
|
||||
|
||||
return TimelineHelpers.HaveSameContainerAsset(track, asset.clip);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a31542ccf4e8584ca4f60843e9d02d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b24618beecc3bf41acadfcf2246d772
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user