internal Playable CreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree, Playable timelinePlayable) { UpdateDuration(); var mixerPlayable = Playable.Null; if (CanCompileClipsRecursive()) { mixerPlayable = OnCreateClipPlayableGraph(graph, go, tree); } var notificationsPlayable = CreateNotificationsPlayable(graph, mixerPlayable, go, timelinePlayable); // clear the temporary build data to avoid holding references // case 1253974 s_BuildData.Clear(); if (!notificationsPlayable.IsValid() && !mixerPlayable.IsValid()) { Debug.LogErrorFormat("Track {0} of type {1} has no notifications and returns an invalid mixer Playable", name, GetType().FullName); return(Playable.Create(graph)); } return(notificationsPlayable.IsValid() ? notificationsPlayable : mixerPlayable); }
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); } if (!AnimatesRootTransform()) { return(mixer); } var rootTrack = isSubTrack ? (AnimationTrack)parent : this; return(rootTrack.ApplyTrackOffset(graph, mixer, go, mode)); }
internal Playable CreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree, Playable timelinePlayable) { var mixerPlayable = Playable.Null; if (CanCompileClipsRecursive()) { UpdateDuration(); mixerPlayable = OnCreateClipPlayableGraph(graph, go, tree); } var notificationsPlayable = CreateNotificationsPlayable(graph, mixerPlayable, go, timelinePlayable); if (!notificationsPlayable.IsValid() && !mixerPlayable.IsValid()) { Debug.LogErrorFormat("Track {0} of type {1} has no notifications and returns an invalid mixer Playable", name, GetType().FullName); return(Playable.Create(graph)); } var playableGraph = CanCompileNotifications() ? notificationsPlayable : mixerPlayable; ConfigureTrackAnimation(tree, go, playableGraph); return(playableGraph); }
internal void ConfigureTrackAnimation(IntervalTree<RuntimeElement> tree, GameObject go, Playable blend) { if (!hasCurves) return; blend.SetAnimatedProperties(m_Curves); tree.Add(new InfiniteRuntimeClip(blend)); if (OnTrackAnimationPlayableCreate != null) OnTrackAnimationPlayableCreate.Invoke(this, go, blend); }
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)); }
void InitializeClips(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree, AudioMixerPlayable clipBlender) { for (int i = 0; i < clips.Length; i++) { var c = clips[i]; var asset = c.asset as PlayableAsset; if (asset == null) { continue; } var buffer = 0.1f; var audioAsset = c.asset as AudioPlayableAsset; if (audioAsset != null) { buffer = audioAsset.bufferingTime; } var source = asset.CreatePlayable(graph, go); if (!source.IsValid()) { continue; } if (source.IsPlayableOfType <AudioClipPlayable>()) { // Enforce initial values on all clips var audioClipPlayable = (AudioClipPlayable)source; var audioClipProperties = audioClipPlayable.GetHandle().GetObject <AudioClipProperties>(); audioClipPlayable.SetVolume(Mathf.Clamp01(m_TrackProperties.volume * audioClipProperties.volume)); audioClipPlayable.SetStereoPan(Mathf.Clamp(m_TrackProperties.stereoPan, -1.0f, 1.0f)); audioClipPlayable.SetSpatialBlend(Mathf.Clamp01(m_TrackProperties.spatialBlend)); } tree.Add(new ScheduleRuntimeClip(c, source, clipBlender, buffer)); graph.Connect(source, 0, clipBlender, i); source.SetSpeed(c.timeScale); source.SetDuration(c.extrapolatedDuration); clipBlender.SetInputWeight(source, 1.0f); } }
public IntervalTreeRebalancer(IntervalTree <RuntimeElement> tree) { m_Tree = tree; }
private static Playable CreatePlayableGraph(PlayableGraph graph, TrackAsset asset, GameObject go, IntervalTree <RuntimeElement> tree, Playable timelinePlayable) { return(asset.CreatePlayableGraph(graph, go, tree, timelinePlayable)); }
internal virtual Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (tree == null) { throw new ArgumentException("IntervalTree argument cannot be null", "tree"); } if (go == null) { throw new ArgumentException("GameObject argument cannot be null", "go"); } s_BuildData.Clear(); GatherCompilableTracks(s_BuildData.trackList); // nothing to compile if (s_BuildData.trackList.Count == 0) { return(Playable.Null); } // check if layers are supported Playable layerMixer = Playable.Null; ILayerable layerable = this as ILayerable; if (layerable != null) { layerMixer = layerable.CreateLayerMixer(graph, go, s_BuildData.trackList.Count); } if (layerMixer.IsValid()) { for (int i = 0; i < s_BuildData.trackList.Count; i++) { var mixer = s_BuildData.trackList[i].CompileClips(graph, go, s_BuildData.trackList[i].clips, tree); if (mixer.IsValid()) { graph.Connect(mixer, 0, layerMixer, i); layerMixer.SetInputWeight(i, 1.0f); } } return(layerMixer); } // one track compiles. Add track mixer and clips if (s_BuildData.trackList.Count == 1) { return(s_BuildData.trackList[0].CompileClips(graph, go, s_BuildData.trackList[0].clips, tree)); } // no layer mixer provided. merge down all clips. for (int i = 0; i < s_BuildData.trackList.Count; i++) { s_BuildData.clipList.AddRange(s_BuildData.trackList[i].clips); } #if UNITY_EDITOR bool applyWarning = false; for (int i = 0; i < s_BuildData.trackList.Count; i++) { applyWarning |= i > 0 && s_BuildData.trackList[i].hasCurves; } if (applyWarning) { Debug.LogWarning("A layered track contains animated fields, but no layer mixer has been provided. Animated fields on layers will be ignored. Override CreateLayerMixer in " + s_BuildData.trackList[0].GetType().Name + " and return a valid playable to support animated fields on layered tracks."); } #endif // compile all the clips into a single mixer return(CompileClips(graph, go, s_BuildData.clipList, tree)); }
internal virtual Playable CompileClips(PlayableGraph graph, GameObject go, IList <TimelineClip> timelineClips, IntervalTree <RuntimeElement> tree) { var blend = CreateTrackMixer(graph, go, timelineClips.Count); for (var c = 0; c < timelineClips.Count; c++) { var source = CreatePlayable(graph, go, timelineClips[c]); if (source.IsValid()) { source.SetDuration(timelineClips[c].duration); var clip = new RuntimeClip(timelineClips[c], source, blend); tree.Add(clip); graph.Connect(source, 0, blend, c); blend.SetInputWeight(c, 0.0f); } } ConfigureTrackAnimation(tree, go, blend); return(blend); }
internal override Playable OnCreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (base.isSubTrack) { throw new InvalidOperationException("Nested animation tracks should never be asked to create a graph directly"); } List <AnimationTrack> list = new List <AnimationTrack>(); if (this.compilableIsolated) { list.Add(this); } foreach (TrackAsset trackAsset in base.GetChildTracks()) { AnimationTrack animationTrack = trackAsset as AnimationTrack; if (animationTrack != null && animationTrack.compilable) { list.Add(animationTrack); } } AnimationMotionXToDeltaPlayable animationMotionXToDeltaPlayable = AnimationMotionXToDeltaPlayable.Create(graph); AnimationLayerMixerPlayable animationLayerMixerPlayable = AnimationTrack.CreateGroupMixer(graph, go, list.Count); graph.Connect <AnimationLayerMixerPlayable, AnimationMotionXToDeltaPlayable>(animationLayerMixerPlayable, 0, animationMotionXToDeltaPlayable, 0); animationMotionXToDeltaPlayable.SetInputWeight(0, 1f); for (int i = 0; i < list.Count; i++) { Playable source = (!list[i].inClipMode) ? list[i].CreateInfiniteTrackPlayable(graph, go, tree) : this.CompileTrackPlayable(graph, list[i], go, tree); graph.Connect <Playable, AnimationLayerMixerPlayable>(source, 0, animationLayerMixerPlayable, i); animationLayerMixerPlayable.SetInputWeight(i, (float)((!list[i].inClipMode) ? 1 : 0)); if (list[i].applyAvatarMask && list[i].avatarMask != null) { animationLayerMixerPlayable.SetLayerMaskFromAvatarMask((uint)i, list[i].avatarMask); } } return(animationMotionXToDeltaPlayable); }
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); } } 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); }
public IntervalTreeRebalancer(IntervalTree <RuntimeElement> tree) { this.m_Tree = tree; this.m_Hash = this.m_Tree.GetHash(); }
internal override Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { var clipBlender = AudioMixerPlayable.Create(graph, clips.Length); // In the player, we do not add the AudioMixerProperties behaviour to the track if it contains no animation // however in the editor we want to always have it present to be able to preview changes live. #if UNITY_EDITOR clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone()); m_LiveMixerPlayable = clipBlender; #else if (hasCurves) { clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone()); } #endif InitializeClips(graph, go, tree, clipBlender); return(clipBlender); }
internal virtual Playable OnCreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (tree == null) { throw new ArgumentException("IntervalTree argument cannot be null", "tree"); } if (go == null) { throw new ArgumentException("GameObject argument cannot be null", "go"); } Playable playable = this.CreateTrackMixer(graph, go, this.clips.Length); for (int i = 0; i < this.clips.Length; i++) { Playable playable2 = this.CreatePlayable(graph, go, this.clips[i]); if (playable2.IsValid <Playable>()) { playable2.SetDuration(this.clips[i].duration); RuntimeClip item = new RuntimeClip(this.clips[i], playable2, playable); tree.Add(item); graph.Connect <Playable, Playable>(playable2, 0, playable, i); playable.SetInputWeight(i, 0f); } } return(playable); }
internal Playable CreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { this.UpdateDuration(); return(this.OnCreatePlayableGraph(graph, go, tree)); }
private Playable CreateInfiniteTrackPlayable(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { Playable result; if (base.animClip == null) { result = Playable.Null; } else { if (this.m_FakeAnimClip == null || this.m_AnimationPlayableAsset == null) { this.m_AnimationPlayableAsset = ScriptableObject.CreateInstance <AnimationPlayableAsset>(); this.m_FakeAnimClip = new TimelineClip(null) { asset = this.m_AnimationPlayableAsset, displayName = "Animation Clip", timeScale = 1.0, start = 0.0, postExtrapolationMode = TimelineClip.ClipExtrapolation.Hold, preExtrapolationMode = TimelineClip.ClipExtrapolation.Hold }; this.m_FakeAnimClip.SetPostExtrapolationTime(TimelineClip.kMaxTimeValue); } this.m_AnimationPlayableAsset.clip = base.animClip; this.m_AnimationPlayableAsset.position = this.m_OpenClipOffsetPosition; this.m_AnimationPlayableAsset.rotation = this.m_OpenClipOffsetRotation; this.m_AnimationPlayableAsset.removeStartOffset = false; this.m_FakeAnimClip.start = 0.0; this.m_FakeAnimClip.SetPreExtrapolationTime(0.0); this.m_FakeAnimClip.duration = (double)base.animClip.length; AnimationMixerPlayable animationMixerPlayable = AnimationMixerPlayable.Create(graph, 1, false); Playable playable = this.m_AnimationPlayableAsset.CreatePlayable(graph, go); if (playable.IsValid <Playable>()) { tree.Add(new RuntimeClip(this.m_FakeAnimClip, playable, animationMixerPlayable)); graph.Connect <Playable, AnimationMixerPlayable>(playable, 0, animationMixerPlayable, 0); animationMixerPlayable.SetInputWeight(0, 1f); } result = this.ApplyTrackOffset(graph, animationMixerPlayable); } return(result); }
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 CompileClips(PlayableGraph graph, GameObject go, IList <TimelineClip> timelineClips, IntervalTree <RuntimeElement> tree) { var clipBlender = AudioMixerPlayable.Create(graph, timelineClips.Count); #if UNITY_EDITOR clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone()); m_LiveMixerPlayable = clipBlender; #else if (hasCurves) { clipBlender.GetHandle().SetScriptInstance(m_TrackProperties.Clone()); } #endif for (int i = 0; i < timelineClips.Count; i++) { var c = timelineClips[i]; var asset = c.asset as PlayableAsset; if (asset == null) { continue; } var buffer = 0.1f; var audioAsset = c.asset as AudioPlayableAsset; if (audioAsset != null) { buffer = audioAsset.bufferingTime; } var source = asset.CreatePlayable(graph, go); if (!source.IsValid()) { continue; } if (source.IsPlayableOfType <AudioClipPlayable>()) { // Enforce initial values on all clips var audioClipPlayable = (AudioClipPlayable)source; var audioClipProperties = audioClipPlayable.GetHandle().GetObject <AudioClipProperties>(); audioClipPlayable.SetVolume(Mathf.Clamp01(m_TrackProperties.volume * audioClipProperties.volume)); audioClipPlayable.SetStereoPan(Mathf.Clamp(m_TrackProperties.stereoPan, -1.0f, 1.0f)); audioClipPlayable.SetSpatialBlend(Mathf.Clamp01(m_TrackProperties.spatialBlend)); } tree.Add(new ScheduleRuntimeClip(c, source, clipBlender, buffer)); graph.Connect(source, 0, clipBlender, i); source.SetSpeed(c.timeScale); source.SetDuration(c.extrapolatedDuration); clipBlender.SetInputWeight(source, 1.0f); } ConfigureTrackAnimation(tree, go, clipBlender); return(clipBlender); }
internal virtual Playable OnCreateClipPlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { if (tree == null) { throw new ArgumentException("IntervalTree argument cannot be null", "tree"); } if (go == null) { throw new ArgumentException("GameObject argument cannot be null", "go"); } var blend = CreateTrackMixer(graph, go, clips.Length); for (var c = 0; c < clips.Length; c++) { var source = CreatePlayable(graph, go, clips[c]); if (source.IsValid()) { source.SetDuration(clips[c].duration); var clip = new RuntimeClip(clips[c], source, blend); tree.Add(clip); graph.Connect(source, 0, blend, c); blend.SetInputWeight(c, 0.0f); } } return(blend); }
internal override Playable OnCreatePlayableGraph(PlayableGraph graph, GameObject go, IntervalTree <RuntimeElement> tree) { AudioMixerPlayable audioMixerPlayable = AudioMixerPlayable.Create(graph, base.clips.Length, false); for (int i = 0; i < base.clips.Length; i++) { TimelineClip timelineClip = base.clips[i]; PlayableAsset playableAsset = timelineClip.asset as PlayableAsset; if (!(playableAsset == null)) { float num = 0.1f; AudioPlayableAsset audioPlayableAsset = timelineClip.asset as AudioPlayableAsset; if (audioPlayableAsset != null) { num = audioPlayableAsset.bufferingTime; } Playable playable = playableAsset.CreatePlayable(graph, go); if (playable.IsValid <Playable>()) { tree.Add(new ScheduleRuntimeClip(timelineClip, playable, audioMixerPlayable, (double)num, 0.1)); graph.Connect <Playable, AudioMixerPlayable>(playable, 0, audioMixerPlayable, i); playable.SetSpeed(timelineClip.timeScale); playable.SetDuration(timelineClip.extrapolatedDuration); audioMixerPlayable.SetInputWeight(playable, 1f); } } } return(audioMixerPlayable); }
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); }
internal Playable CompileTrackPlayable(PlayableGraph graph, TrackAsset track, GameObject go, IntervalTree <RuntimeElement> tree) { AnimationMixerPlayable animationMixerPlayable = AnimationMixerPlayable.Create(graph, track.clips.Length, false); for (int i = 0; i < track.clips.Length; i++) { TimelineClip timelineClip = track.clips[i]; PlayableAsset playableAsset = timelineClip.asset as PlayableAsset; if (!(playableAsset == null)) { if (timelineClip.recordable) { AnimationPlayableAsset animationPlayableAsset = playableAsset as AnimationPlayableAsset; if (animationPlayableAsset != null) { animationPlayableAsset.removeStartOffset = !timelineClip.recordable; } } Playable playable = playableAsset.CreatePlayable(graph, go); if (playable.IsValid <Playable>()) { RuntimeClip item = new RuntimeClip(timelineClip, playable, animationMixerPlayable); tree.Add(item); graph.Connect <Playable, AnimationMixerPlayable>(playable, 0, animationMixerPlayable, i); animationMixerPlayable.SetInputWeight(i, 0f); } } } return(this.ApplyTrackOffset(graph, animationMixerPlayable)); }