Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,516 @@
|
|||
//#define PERF_PROFILE
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
using UnityEngine.Playables;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
[CustomEditor(typeof(AnimationTrack)), CanEditMultipleObjects]
|
||||
class AnimationTrackInspector : TrackAssetInspector
|
||||
{
|
||||
static class Styles
|
||||
{
|
||||
public static GUIContent MatchTargetFieldsTitle = L10n.TextContent("Default Offset Match Fields", "Fields to apply when matching offsets on clips. These are the defaults, and can be overridden for each clip.");
|
||||
public static readonly GUIContent PositionIcon = EditorGUIUtility.IconContent("MoveTool");
|
||||
public static readonly GUIContent RotationIcon = EditorGUIUtility.IconContent("RotateTool");
|
||||
|
||||
public static GUIContent XTitle = EditorGUIUtility.TextContent("X");
|
||||
public static GUIContent YTitle = EditorGUIUtility.TextContent("Y");
|
||||
public static GUIContent ZTitle = EditorGUIUtility.TextContent("Z");
|
||||
public static GUIContent PositionTitle = L10n.TextContent("Position");
|
||||
public static GUIContent RotationTitle = L10n.TextContent("Rotation");
|
||||
|
||||
public static readonly GUIContent OffsetModeTitle = L10n.TextContent("Track Offsets");
|
||||
public static readonly string TransformOffsetInfo = L10n.Tr("Transform offsets are applied to the entire track. Use this mode to play the animation track at a fixed position and rotation.");
|
||||
public static readonly string SceneOffsetInfo = L10n.Tr("Scene offsets will use the existing transform as initial offsets. Use this to play the track from the gameObjects current position and rotation.");
|
||||
public static readonly string AutoOffsetInfo = L10n.Tr("Auto will apply scene offsets if there is a controller attached to the animator and transform offsets otherwise.");
|
||||
public static readonly string AutoOffsetWarning = L10n.Tr("This mode is deprecated may be removed in a future release.");
|
||||
public static readonly string InheritedFromParent = L10n.Tr("Inherited");
|
||||
public static readonly string InheritedToolTip = L10n.Tr("This value is inherited from it's parent track.");
|
||||
|
||||
public static readonly string AvatarMaskWarning = L10n.Tr("Applying an Avatar Mask to the base track may not properly mask Root Motion or Humanoid bones from an Animator Controller or other Timeline track.");
|
||||
|
||||
public static readonly GUIContent RecordingOffsets = L10n.TextContent("Recorded Offsets", "Offsets applied to recorded position and rotation keys");
|
||||
public static readonly GUIContent RecordingIkApplied = L10n.TextContent("Apply Foot IK", "Applies Foot IK to recorded Animation.");
|
||||
|
||||
public static readonly GUIContent[] OffsetContents;
|
||||
public static readonly GUIContent[] OffsetInheritContents;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
var values = Enum.GetValues(typeof(TrackOffset));
|
||||
OffsetContents = new GUIContent[values.Length];
|
||||
OffsetInheritContents = new GUIContent[values.Length];
|
||||
for (var index = 0; index < values.Length; index++)
|
||||
{
|
||||
var offset = (TrackOffset)index;
|
||||
var name = ObjectNames.NicifyVariableName(L10n.Tr(offset.ToString()));
|
||||
var memInfo = typeof(TrackOffset).GetMember(offset.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
name = ((DescriptionAttribute)attributes[0]).Description;
|
||||
}
|
||||
|
||||
OffsetContents[index] = new GUIContent(name);
|
||||
OffsetInheritContents[index] = new GUIContent(string.Format("{0} ({1})", InheritedFromParent, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimelineAnimationUtilities.OffsetEditMode m_OffsetEditMode = TimelineAnimationUtilities.OffsetEditMode.None;
|
||||
|
||||
SerializedProperty m_MatchFieldsProperty;
|
||||
SerializedProperty m_TrackPositionProperty;
|
||||
SerializedProperty m_TrackRotationProperty;
|
||||
SerializedProperty m_AvatarMaskProperty;
|
||||
SerializedProperty m_ApplyAvatarMaskProperty;
|
||||
SerializedProperty m_TrackOffsetProperty;
|
||||
|
||||
SerializedProperty m_RecordedOffsetPositionProperty;
|
||||
SerializedProperty m_RecordedOffsetEulerProperty;
|
||||
SerializedProperty m_RecordedApplyFootIK;
|
||||
|
||||
Vector3 m_lastPosition;
|
||||
Vector3 m_lastRotation;
|
||||
|
||||
GUIContent m_TempContent = new GUIContent();
|
||||
|
||||
|
||||
void Evaluate()
|
||||
{
|
||||
if (timelineWindow.state != null && timelineWindow.state.editSequence.director != null)
|
||||
{
|
||||
// force the update immediately, the deferred doesn't always work with the inspector
|
||||
timelineWindow.state.editSequence.director.Evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
void RebuildGraph()
|
||||
{
|
||||
TimelineEditor.Refresh(RefreshReason.ContentsModified);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(IsTrackLocked()))
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
DrawRootTransformOffset();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
DrawRecordedProperties();
|
||||
DrawAvatarProperties();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
RebuildGraph();
|
||||
|
||||
DrawMatchFieldsGUI();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimatesRootTransform()
|
||||
{
|
||||
return targets.OfType<AnimationTrack>().All(t => t.AnimatesRootTransform());
|
||||
}
|
||||
|
||||
bool ShouldDrawOffsets()
|
||||
{
|
||||
bool hasMultiple;
|
||||
var offsetMode = GetOffsetMode(out hasMultiple);
|
||||
if (hasMultiple)
|
||||
return false;
|
||||
|
||||
if (offsetMode == TrackOffset.ApplySceneOffsets)
|
||||
return false;
|
||||
|
||||
if (offsetMode == TrackOffset.ApplyTransformOffsets)
|
||||
return true;
|
||||
|
||||
// Auto mode.
|
||||
PlayableDirector director = this.m_Context as PlayableDirector;
|
||||
if (director == null)
|
||||
return false;
|
||||
|
||||
// If any bound animators have controllers don't show
|
||||
foreach (var track in targets.OfType<AnimationTrack>())
|
||||
{
|
||||
var animator = track.GetBinding(director);
|
||||
if (animator != null && animator.runtimeAnimatorController != null)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawRootTransformOffset()
|
||||
{
|
||||
if (!AnimatesRootTransform())
|
||||
return;
|
||||
|
||||
bool showWarning = SetupOffsetTooltip();
|
||||
DrawRootTransformDropDown();
|
||||
|
||||
if (ShouldDrawOffsets())
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
DrawRootMotionToolBar();
|
||||
DrawRootMotionOffsetFields();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
if (showWarning)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.HelpBox(Styles.AutoOffsetWarning, MessageType.Warning, true);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
bool SetupOffsetTooltip()
|
||||
{
|
||||
Styles.OffsetModeTitle.tooltip = string.Empty;
|
||||
bool hasMultiple;
|
||||
var offsetMode = GetOffsetMode(out hasMultiple);
|
||||
bool showWarning = false;
|
||||
if (!hasMultiple)
|
||||
{
|
||||
if (offsetMode == TrackOffset.ApplyTransformOffsets)
|
||||
Styles.OffsetModeTitle.tooltip = Styles.TransformOffsetInfo;
|
||||
else if (offsetMode == TrackOffset.ApplySceneOffsets)
|
||||
Styles.OffsetModeTitle.tooltip = Styles.SceneOffsetInfo;
|
||||
else if (offsetMode == TrackOffset.Auto)
|
||||
{
|
||||
Styles.OffsetModeTitle.tooltip = Styles.AutoOffsetInfo;
|
||||
showWarning = true;
|
||||
}
|
||||
}
|
||||
|
||||
return showWarning;
|
||||
}
|
||||
|
||||
void DrawRootTransformDropDown()
|
||||
{
|
||||
bool anySubTracks = targets.OfType<AnimationTrack>().Any(t => t.isSubTrack);
|
||||
bool allSubTracks = targets.OfType<AnimationTrack>().All(t => t.isSubTrack);
|
||||
|
||||
bool mixed;
|
||||
var rootOffsetMode = GetOffsetMode(out mixed);
|
||||
|
||||
// if we are showing subtracks, we need to show the current mode from the parent
|
||||
// BUT keep it disabled
|
||||
if (anySubTracks)
|
||||
{
|
||||
m_TempContent.tooltip = string.Empty;
|
||||
if (mixed)
|
||||
m_TempContent.text = EditorGUI.mixedValueContent.text;
|
||||
else if (!allSubTracks)
|
||||
m_TempContent.text = Styles.OffsetContents[(int)rootOffsetMode].text;
|
||||
else
|
||||
{
|
||||
m_TempContent.text = Styles.OffsetInheritContents[(int)rootOffsetMode].text;
|
||||
m_TempContent.tooltip = Styles.InheritedToolTip;
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
EditorGUILayout.LabelField(Styles.OffsetModeTitle, m_TempContent, EditorStyles.popup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We use an enum popup explicitly because it will handle the description attribute on the enum
|
||||
using (new GUIMixedValueScope(mixed))
|
||||
{
|
||||
var rect = EditorGUILayout.GetControlRect(true, EditorGUI.kSingleLineHeight);
|
||||
EditorGUI.BeginProperty(rect, Styles.OffsetModeTitle, m_TrackOffsetProperty);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var result = (TrackOffset)EditorGUI.EnumPopup(rect, Styles.OffsetModeTitle, (TrackOffset)m_TrackOffsetProperty.intValue);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
m_TrackOffsetProperty.enumValueIndex = (int)result;
|
||||
|
||||
// this property changes the recordable state of the objects, so auto disable recording
|
||||
if (TimelineWindow.instance != null)
|
||||
{
|
||||
if (TimelineWindow.instance.state != null)
|
||||
TimelineWindow.instance.state.recording = false;
|
||||
RebuildGraph();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawMatchFieldsGUI()
|
||||
{
|
||||
if (!AnimatesRootTransform())
|
||||
return;
|
||||
|
||||
m_MatchFieldsProperty.isExpanded = EditorGUILayout.Foldout(m_MatchFieldsProperty.isExpanded, Styles.MatchTargetFieldsTitle, true);
|
||||
if (m_MatchFieldsProperty.isExpanded)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
MatchTargetsFieldGUI(m_MatchFieldsProperty);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRootMotionOffsetFields()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_TrackPositionProperty);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_TrackRotationProperty, Styles.RotationTitle);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
UpdateOffsets();
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRootMotionToolBar()
|
||||
{
|
||||
bool disable = targets.Length > 1;
|
||||
bool changed = false;
|
||||
|
||||
if (!disable)
|
||||
{
|
||||
// detects external changes
|
||||
changed |= m_lastPosition != m_TrackPositionProperty.vector3Value || m_lastRotation != m_TrackRotationProperty.vector3Value;
|
||||
m_lastPosition = m_TrackPositionProperty.vector3Value;
|
||||
m_lastRotation = m_TrackRotationProperty.vector3Value;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
using (new EditorGUI.DisabledScope(disable))
|
||||
ShowMotionOffsetEditModeToolbar(ref m_OffsetEditMode);
|
||||
changed |= EditorGUI.EndChangeCheck();
|
||||
|
||||
if (changed)
|
||||
{
|
||||
UpdateOffsets();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOffsets()
|
||||
{
|
||||
foreach (var track in targets.OfType<AnimationTrack>())
|
||||
track.UpdateClipOffsets();
|
||||
Evaluate();
|
||||
}
|
||||
|
||||
void DrawAvatarProperties()
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_ApplyAvatarMaskProperty);
|
||||
if (m_ApplyAvatarMaskProperty.hasMultipleDifferentValues || m_ApplyAvatarMaskProperty.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(m_AvatarMaskProperty);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
if (targets.OfType<AnimationTrack>().Any(x => !x.isSubTrack))
|
||||
EditorGUILayout.HelpBox(Styles.AvatarMaskWarning, MessageType.Warning);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
public static void ShowMotionOffsetEditModeToolbar(ref TimelineAnimationUtilities.OffsetEditMode motionOffset)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
int newMotionOffsetMode = GUILayout.Toolbar((int)motionOffset, new[] { Styles.PositionIcon, Styles.RotationIcon });
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
if ((int)motionOffset == newMotionOffsetMode) //untoggle the button
|
||||
motionOffset = TimelineAnimationUtilities.OffsetEditMode.None;
|
||||
else
|
||||
motionOffset = (TimelineAnimationUtilities.OffsetEditMode)newMotionOffsetMode;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.Space(3);
|
||||
}
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
|
||||
m_MatchFieldsProperty = serializedObject.FindProperty("m_MatchTargetFields");
|
||||
m_TrackPositionProperty = serializedObject.FindProperty("m_Position");
|
||||
m_TrackRotationProperty = serializedObject.FindProperty("m_EulerAngles");
|
||||
m_TrackOffsetProperty = serializedObject.FindProperty("m_TrackOffset");
|
||||
m_AvatarMaskProperty = serializedObject.FindProperty("m_AvatarMask");
|
||||
m_ApplyAvatarMaskProperty = serializedObject.FindProperty("m_ApplyAvatarMask");
|
||||
m_RecordedOffsetPositionProperty = serializedObject.FindProperty("m_InfiniteClipOffsetPosition");
|
||||
m_RecordedOffsetEulerProperty = serializedObject.FindProperty("m_InfiniteClipOffsetEulerAngles");
|
||||
m_RecordedApplyFootIK = serializedObject.FindProperty("m_InfiniteClipApplyFootIK");
|
||||
|
||||
m_lastPosition = m_TrackPositionProperty.vector3Value;
|
||||
m_lastRotation = m_TrackRotationProperty.vector3Value;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
}
|
||||
|
||||
void OnSceneGUI(SceneView sceneView)
|
||||
{
|
||||
DoOffsetManipulator();
|
||||
}
|
||||
|
||||
void DoOffsetManipulator()
|
||||
{
|
||||
if (targets.Length > 1) //do not edit the track offset on a multiple selection
|
||||
return;
|
||||
|
||||
if (timelineWindow == null || timelineWindow.state == null || timelineWindow.state.editSequence.director == null)
|
||||
return;
|
||||
|
||||
AnimationTrack animationTrack = target as AnimationTrack;
|
||||
if (animationTrack != null && (animationTrack.trackOffset == TrackOffset.ApplyTransformOffsets) && m_OffsetEditMode != TimelineAnimationUtilities.OffsetEditMode.None)
|
||||
{
|
||||
var boundObject = TimelineUtility.GetSceneGameObject(timelineWindow.state.editSequence.director, animationTrack);
|
||||
var boundObjectTransform = boundObject != null ? boundObject.transform : null;
|
||||
|
||||
var offsets = TimelineAnimationUtilities.GetTrackOffsets(animationTrack, boundObjectTransform);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
switch (m_OffsetEditMode)
|
||||
{
|
||||
case TimelineAnimationUtilities.OffsetEditMode.Translation:
|
||||
offsets.position = Handles.PositionHandle(offsets.position, (Tools.pivotRotation == PivotRotation.Global)
|
||||
? Quaternion.identity
|
||||
: offsets.rotation);
|
||||
break;
|
||||
case TimelineAnimationUtilities.OffsetEditMode.Rotation:
|
||||
offsets.rotation = Handles.RotationHandle(offsets.rotation, offsets.position);
|
||||
break;
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
UndoExtensions.RegisterTrack(animationTrack, L10n.Tr("Inspector"));
|
||||
TimelineAnimationUtilities.UpdateTrackOffset(animationTrack, boundObjectTransform, offsets);
|
||||
Evaluate();
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawRecordedProperties()
|
||||
{
|
||||
// only show if this applies to all targets
|
||||
foreach (var track in targets)
|
||||
{
|
||||
var animationTrack = track as AnimationTrack;
|
||||
if (animationTrack == null || animationTrack.inClipMode || animationTrack.infiniteClip == null || animationTrack.infiniteClip.empty)
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Label(Styles.RecordingOffsets);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_RecordedOffsetPositionProperty, Styles.PositionTitle);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(m_RecordedOffsetEulerProperty, Styles.RotationTitle);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(m_RecordedApplyFootIK, Styles.RecordingIkApplied);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
public static void MatchTargetsFieldGUI(SerializedProperty property)
|
||||
{
|
||||
const float ToggleWidth = 20;
|
||||
int value = 0;
|
||||
|
||||
MatchTargetFields enumValue = (MatchTargetFields)property.intValue;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Rect rect = EditorGUILayout.GetControlRect(false, kLineHeight * 2);
|
||||
Rect itemRect = new Rect(rect.x, rect.y, rect.width, kLineHeight);
|
||||
EditorGUI.BeginProperty(rect, Styles.MatchTargetFieldsTitle, property);
|
||||
float minWidth = 0, maxWidth = 0;
|
||||
EditorStyles.label.CalcMinMaxWidth(Styles.XTitle, out minWidth, out maxWidth);
|
||||
float width = minWidth + ToggleWidth;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
Rect r = EditorGUI.PrefixLabel(itemRect, Styles.PositionTitle);
|
||||
int oldIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
r.width = width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.XTitle, enumValue.HasAny(MatchTargetFields.PositionX)) ? (int)MatchTargetFields.PositionX : 0;
|
||||
r.x += width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.YTitle, enumValue.HasAny(MatchTargetFields.PositionY)) ? (int)MatchTargetFields.PositionY : 0;
|
||||
r.x += width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.ZTitle, enumValue.HasAny(MatchTargetFields.PositionZ)) ? (int)MatchTargetFields.PositionZ : 0;
|
||||
EditorGUI.indentLevel = oldIndent;
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
itemRect.y += kLineHeight;
|
||||
r = EditorGUI.PrefixLabel(itemRect, Styles.RotationTitle);
|
||||
EditorGUI.indentLevel = 0;
|
||||
r.width = width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.XTitle, enumValue.HasAny(MatchTargetFields.RotationX)) ? (int)MatchTargetFields.RotationX : 0;
|
||||
r.x += width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.YTitle, enumValue.HasAny(MatchTargetFields.RotationY)) ? (int)MatchTargetFields.RotationY : 0;
|
||||
r.x += width;
|
||||
value |= EditorGUI.ToggleLeft(r, Styles.ZTitle, enumValue.HasAny(MatchTargetFields.RotationZ)) ? (int)MatchTargetFields.RotationZ : 0;
|
||||
EditorGUI.indentLevel = oldIndent;
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
property.intValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
static TrackOffset GetOffsetMode(AnimationTrack track)
|
||||
{
|
||||
if (track.isSubTrack)
|
||||
{
|
||||
var parent = track.parent as AnimationTrack;
|
||||
if (parent != null) // fallback to the current track if there is an error
|
||||
track = parent;
|
||||
}
|
||||
|
||||
return track.trackOffset;
|
||||
}
|
||||
|
||||
// gets the current mode,
|
||||
TrackOffset GetOffsetMode(out bool hasMultiple)
|
||||
{
|
||||
var rootOffsetMode = GetOffsetMode(target as AnimationTrack);
|
||||
hasMultiple = targets.OfType<AnimationTrack>().Any(t => GetOffsetMode(t) != rootOffsetMode);
|
||||
return rootOffsetMode;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue