private static bool ShouldApplyOffset(AppliedOffsetMode mode, AnimationClip clip) { if (mode == AppliedOffsetMode.NoRootTransform || mode == AppliedOffsetMode.SceneOffsetLegacy) { return(false); } return(HasRootTransforms(clip)); }
static bool UsesAbsoluteMotion(AppliedOffsetMode mode) { #if UNITY_EDITOR // in editor, previewing is always done in absolute motion if (!Application.isPlaying) { return(true); } #endif return(mode != AppliedOffsetMode.SceneOffset && mode != AppliedOffsetMode.SceneOffsetLegacy); }
bool RequiresMotionXPlayable(AppliedOffsetMode mode, GameObject gameObject) { if (mode == AppliedOffsetMode.NoRootTransform) { return(false); } if (mode == AppliedOffsetMode.SceneOffsetLegacy) { var animator = GetBinding(gameObject != null ? gameObject.GetComponent <PlayableDirector>() : null); return(animator != null && animator.hasRootMotion); } return(true); }
Playable ApplyTrackOffset(PlayableGraph graph, Playable root, GameObject go, AppliedOffsetMode mode) { #if UNITY_EDITOR m_ClipOffset = AnimationOffsetPlayable.Null; #endif // offsets don't apply in scene offset, or if there is no root transform (globally or on this track) if (mode == AppliedOffsetMode.SceneOffsetLegacy || mode == AppliedOffsetMode.SceneOffset || mode == AppliedOffsetMode.NoRootTransform || !AnimatesRootTransform() ) { return(root); } var pos = position; var rot = rotation; #if UNITY_EDITOR // in the editor use the preview position to playback from if available if (mode == AppliedOffsetMode.SceneOffsetEditor) { pos = m_SceneOffsetPosition; rot = Quaternion.Euler(m_SceneOffsetRotation); } #endif var offsetPlayable = AnimationOffsetPlayable.Create(graph, pos, rot, 1); #if UNITY_EDITOR m_ClipOffset = offsetPlayable; #endif graph.Connect(root, 0, offsetPlayable, 0); offsetPlayable.SetInputWeight(0, 1); return(offsetPlayable); }
Playable CreateInfiniteTrackPlayable(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree, AppliedOffsetMode mode) { if (m_InfiniteClip == null) { return(Playable.Null); } var mixer = AnimationMixerPlayable.Create(graph, 1); // In infinite mode, we always force the loop mode of the clip off because the clip keys are offset in infinite mode // which causes loop to behave different. // The inline curve editor never shows loops in infinite mode. var playable = AnimationPlayableAsset.CreatePlayable(graph, m_InfiniteClip, m_InfiniteClipOffsetPosition, m_InfiniteClipOffsetEulerAngles, false, mode, infiniteClipApplyFootIK, AnimationPlayableAsset.LoopMode.Off); if (playable.IsValid()) { tree.Add(new InfiniteRuntimeClip(playable)); graph.Connect(playable, 0, mixer, 0); mixer.SetInputWeight(0, 1.0f); } return(ApplyTrackOffset(graph, mixer, go, mode)); }
internal override Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (isSubTrack) { throw new InvalidOperationException("Nested animation tracks should never be asked to create a graph directly"); } List <AnimationTrack> flattenTracks = new List <AnimationTrack>(); if (CanCompileClips()) { flattenTracks.Add(this); } var genericRoot = GetGenericRootNode(go); var animatesRootTransformNoMask = AnimatesRootTransform(); var animatesRootTransform = animatesRootTransformNoMask && !IsRootTransformDisabledByMask(go, genericRoot); foreach (var subTrack in GetChildTracks()) { var child = subTrack as AnimationTrack; if (child != null && child.CanCompileClips()) { var childAnimatesRoot = child.AnimatesRootTransform(); animatesRootTransformNoMask |= child.AnimatesRootTransform(); animatesRootTransform |= (childAnimatesRoot && !child.IsRootTransformDisabledByMask(go, genericRoot)); flattenTracks.Add(child); } } // figure out which mode to apply AppliedOffsetMode mode = GetOffsetMode(go, animatesRootTransform); int defaultBlendCount = GetDefaultBlendCount(); var layerMixer = CreateGroupMixer(graph, go, flattenTracks.Count + defaultBlendCount); for (int c = 0; c < flattenTracks.Count; c++) { int blendIndex = c + defaultBlendCount; // if the child is masking the root transform, compile it as if we are non-root mode var childMode = mode; if (mode != AppliedOffsetMode.NoRootTransform && flattenTracks[c].IsRootTransformDisabledByMask(go, genericRoot)) { childMode = AppliedOffsetMode.NoRootTransform; } var compiledTrackPlayable = flattenTracks[c].inClipMode ? CompileTrackPlayable(graph, flattenTracks[c], go, tree, childMode) : flattenTracks[c].CreateInfiniteTrackPlayable(graph, go, tree, childMode); graph.Connect(compiledTrackPlayable, 0, layerMixer, blendIndex); layerMixer.SetInputWeight(blendIndex, flattenTracks[c].inClipMode ? 0 : 1); if (flattenTracks[c].applyAvatarMask && flattenTracks[c].avatarMask != null) { layerMixer.SetLayerMaskFromAvatarMask((uint)blendIndex, flattenTracks[c].avatarMask); } } var requiresMotionXPlayable = RequiresMotionXPlayable(mode, go); // In the editor, we may require the motion X playable if we are animating the root transform but it is masked out, because the default poses // need to properly update root motion requiresMotionXPlayable |= (defaultBlendCount > 0 && RequiresMotionXPlayable(GetOffsetMode(go, animatesRootTransformNoMask), go)); // Attach the default poses AttachDefaultBlend(graph, layerMixer, requiresMotionXPlayable); // motionX playable not required in scene offset mode, or root transform mode Playable mixer = layerMixer; if (requiresMotionXPlayable) { // If we are animating a root transform, add the motionX to delta playable as the root node var motionXToDelta = AnimationMotionXToDeltaPlayable.Create(graph); graph.Connect(mixer, 0, motionXToDelta, 0); motionXToDelta.SetInputWeight(0, 1.0f); motionXToDelta.SetAbsoluteMotion(UsesAbsoluteMotion(mode)); mixer = (Playable)motionXToDelta; } #if UNITY_EDITOR if (!Application.isPlaying) { var animator = GetBinding(go != null ? go.GetComponent <PlayableDirector>() : null); if (animator != null) { GameObject targetGO = animator.gameObject; IAnimationWindowPreview[] previewComponents = targetGO.GetComponents <IAnimationWindowPreview>(); m_HasPreviewComponents = previewComponents.Length > 0; if (m_HasPreviewComponents) { foreach (var component in previewComponents) { mixer = component.BuildPreviewGraph(graph, mixer); } } } } #endif return(mixer); }
Playable CompileTrackPlayable(PlayableGraph graph, TrackAsset track, GameObject go, IntervalTree <RuntimeElement> tree, AppliedOffsetMode mode) { var mixer = AnimationMixerPlayable.Create(graph, track.clips.Length); for (int i = 0; i < track.clips.Length; i++) { var c = track.clips[i]; var asset = c.asset as PlayableAsset; if (asset == null) { continue; } var animationAsset = asset as AnimationPlayableAsset; if (animationAsset != null) { animationAsset.appliedOffsetMode = mode; } var source = asset.CreatePlayable(graph, go); if (source.IsValid()) { var clip = new RuntimeClip(c, source, mixer); tree.Add(clip); graph.Connect(source, 0, mixer, i); mixer.SetInputWeight(i, 0.0f); } } return(ApplyTrackOffset(graph, mixer, go, mode)); }
internal override Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (isSubTrack) { throw new InvalidOperationException("Nested animation tracks should never be asked to create a graph directly"); } List <AnimationTrack> flattenTracks = new List <AnimationTrack>(); if (CanCompileClips()) { flattenTracks.Add(this); } bool animatesRootTransform = AnimatesRootTransform(); foreach (var subTrack in GetChildTracks()) { var child = subTrack as AnimationTrack; if (child != null && child.CanCompileClips()) { animatesRootTransform |= child.AnimatesRootTransform(); flattenTracks.Add(child); } } // figure out which mode to apply AppliedOffsetMode mode = GetOffsetMode(go, animatesRootTransform); var layerMixer = CreateGroupMixer(graph, go, flattenTracks.Count); for (int c = 0; c < flattenTracks.Count; c++) { var compiledTrackPlayable = flattenTracks[c].inClipMode ? CompileTrackPlayable(graph, flattenTracks[c], go, tree, mode) : flattenTracks[c].CreateInfiniteTrackPlayable(graph, go, tree, mode); graph.Connect(compiledTrackPlayable, 0, layerMixer, c); layerMixer.SetInputWeight(c, flattenTracks[c].inClipMode ? 0 : 1); if (flattenTracks[c].applyAvatarMask && flattenTracks[c].avatarMask != null) { layerMixer.SetLayerMaskFromAvatarMask((uint)c, flattenTracks[c].avatarMask); } } bool requiresMotionXPlayable = RequiresMotionXPlayable(mode, go); Playable mixer = layerMixer; mixer = CreateDefaultBlend(graph, go, mixer, requiresMotionXPlayable); // motionX playable not required in scene offset mode, or root transform mode if (requiresMotionXPlayable) { // If we are animating a root transform, add the motionX to delta playable as the root node var motionXToDelta = AnimationMotionXToDeltaPlayable.Create(graph); graph.Connect(mixer, 0, motionXToDelta, 0); motionXToDelta.SetInputWeight(0, 1.0f); motionXToDelta.SetAbsoluteMotion(UsesAbsoluteMotion(mode)); mixer = (Playable)motionXToDelta; } #if UNITY_EDITOR if (!Application.isPlaying) { var animator = GetBinding(go != null ? go.GetComponent <PlayableDirector>() : null); if (animator != null) { GameObject targetGO = animator.gameObject; IAnimationWindowPreview[] previewComponents = targetGO.GetComponents <IAnimationWindowPreview>(); m_HasPreviewComponents = previewComponents.Length > 0; if (m_HasPreviewComponents) { foreach (var component in previewComponents) { mixer = component.BuildPreviewGraph(graph, mixer); } } } } #endif return(mixer); }
internal override Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (isSubTrack) { throw new InvalidOperationException("Nested animation tracks should never be asked to create a graph directly"); } List <AnimationTrack> flattenTracks = new List <AnimationTrack>(); if (CanCompileClips()) { flattenTracks.Add(this); } bool animatesRootTransform = AnimatesRootTransform(); foreach (var subTrack in GetChildTracks()) { var child = subTrack as AnimationTrack; if (child != null && child.CanCompileClips()) { animatesRootTransform |= child.AnimatesRootTransform(); flattenTracks.Add(child); } } // figure out which mode to apply AppliedOffsetMode mode = GetOffsetMode(go, animatesRootTransform); var layerMixer = CreateGroupMixer(graph, go, flattenTracks.Count); for (int c = 0; c < flattenTracks.Count; c++) { var compiledTrackPlayable = flattenTracks[c].inClipMode ? CompileTrackPlayable(graph, flattenTracks[c], go, tree, mode) : flattenTracks[c].CreateInfiniteTrackPlayable(graph, go, tree, mode); graph.Connect(compiledTrackPlayable, 0, layerMixer, c); layerMixer.SetInputWeight(c, flattenTracks[c].inClipMode ? 0 : 1); if (flattenTracks[c].applyAvatarMask && flattenTracks[c].avatarMask != null) { layerMixer.SetLayerMaskFromAvatarMask((uint)c, flattenTracks[c].avatarMask); } } Playable mixer = (Playable)layerMixer; #if UNITY_EDITOR if (!Application.isPlaying) { var motionXMixer = AnimationMixerPlayable.Create(graph, 2); var animator = GetBinding(go != null ? go.GetComponent <PlayableDirector>() : null); Playable blendDefault = Playable.Null; if (animator != null && animator.isHuman) { blendDefault = AnimationClipPlayable.Create(graph, GetDefaultHumanoidClip()); } else { blendDefault = AnimationPosePlayable.Create(graph); ((AnimationPosePlayable)blendDefault).SetReadDefaultPose(true); } var offsetPlayable = AnimationOffsetPlayable.Create(graph, m_SceneOffsetPosition, Quaternion.Euler(m_SceneOffsetRotation), 1); graph.Connect(blendDefault, 0, offsetPlayable, 0); graph.Connect(layerMixer, 0, motionXMixer, 0); graph.Connect(offsetPlayable, 0, motionXMixer, 1); offsetPlayable.SetInputWeight(0, 1.0f); motionXMixer.SetInputWeight(1, 1.0f); motionXMixer.SetInputWeight(0, 0.0f); mixer = (Playable)motionXMixer; } #endif // motionX playable not required in scene offset mode, or root transform mode if (!RequiresMotionXPlayable(mode, go)) { return(mixer); } // If we are animating a root transform, add the motionX to delta playable as the root node var motionXToDelta = AnimationMotionXToDeltaPlayable.Create(graph); graph.Connect(mixer, 0, motionXToDelta, 0); motionXToDelta.SetInputWeight(0, 1.0f); motionXToDelta.SetAbsoluteMotion(UsesAbsoluteMotion(mode)); return(motionXToDelta); }
private static bool ShouldApplyScaleRemove(AppliedOffsetMode mode) { return(mode == AppliedOffsetMode.SceneOffsetLegacyEditor || mode == AppliedOffsetMode.SceneOffsetLegacy || mode == AppliedOffsetMode.TransformOffsetLegacy); }
internal static Playable CreatePlayable(PlayableGraph graph, AnimationClip clip, Vector3 positionOffset, Vector3 eulerOffset, bool removeStartOffset, AppliedOffsetMode mode, bool applyFootIK, LoopMode loop) { if (clip == null || clip.legacy) { return(Playable.Null); } var clipPlayable = AnimationClipPlayable.Create(graph, clip); clipPlayable.SetRemoveStartOffset(removeStartOffset); clipPlayable.SetApplyFootIK(applyFootIK); clipPlayable.SetOverrideLoopTime(loop != LoopMode.UseSourceAsset); clipPlayable.SetLoopTime(loop == LoopMode.On); Playable root = clipPlayable; if (ShouldApplyScaleRemove(mode)) { var removeScale = AnimationRemoveScalePlayable.Create(graph, 1); graph.Connect(root, 0, removeScale, 0); removeScale.SetInputWeight(0, 1.0f); root = removeScale; } if (ShouldApplyOffset(mode, clip)) { var offsetPlayable = AnimationOffsetPlayable.Create(graph, positionOffset, Quaternion.Euler(eulerOffset), 1); graph.Connect(root, 0, offsetPlayable, 0); offsetPlayable.SetInputWeight(0, 1.0F); root = offsetPlayable; } return(root); }
Playable CreateInfiniteTrackPlayable(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree, AppliedOffsetMode mode) { if (m_InfiniteClip == null) { return(Playable.Null); } var mixer = AnimationMixerPlayable.Create(graph, 1); var playable = AnimationPlayableAsset.CreatePlayable(graph, m_InfiniteClip, m_InfiniteClipOffsetPosition, m_InfiniteClipOffsetEulerAngles, false, mode, infiniteClipApplyFootIK); if (playable.IsValid()) { tree.Add(new InfiniteRuntimeClip(playable)); graph.Connect(playable, 0, mixer, 0); mixer.SetInputWeight(0, 1.0f); } return(ApplyTrackOffset(graph, mixer, go, mode)); }