From c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Sun, 19 Apr 2020 17:19:32 -0400 Subject: Inital commit --- .../Runtime/Control/ControlPlayableAsset.cs | 406 +++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Control/ControlPlayableAsset.cs (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Control/ControlPlayableAsset.cs') diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Control/ControlPlayableAsset.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Control/ControlPlayableAsset.cs new file mode 100644 index 0000000..fd806f6 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Control/ControlPlayableAsset.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Playables; + +namespace UnityEngine.Timeline +{ + /// + /// Playable Asset that generates playables for controlling time-related elements on a GameObject. + /// + [Serializable] + [NotKeyable] + public class ControlPlayableAsset : PlayableAsset, IPropertyPreview, ITimelineClipAsset + { + const int k_MaxRandInt = 10000; + static readonly List k_EmptyDirectorsList = new List(0); + static readonly List k_EmptyParticlesList = new List(0); + + /// + /// GameObject in the scene to control, or the parent of the instantiated prefab. + /// + [SerializeField] public ExposedReference sourceGameObject; + + /// + /// Prefab object that will be instantiated. + /// + [SerializeField] public GameObject prefabGameObject; + + /// + /// Indicates whether Particle Systems will be controlled. + /// + [SerializeField] public bool updateParticle = true; + + /// + /// Random seed to supply particle systems that are set to use autoRandomSeed + /// + /// + /// This is used to maintain determinism when playing back in timeline. Sub emitters will be assigned incrementing random seeds to maintain determinism and distinction. + /// + [SerializeField] public uint particleRandomSeed; + + /// + /// Indicates whether playableDirectors are controlled. + /// + [SerializeField] public bool updateDirector = true; + + /// + /// Indicates whether Monobehaviours implementing ITimeControl will be controlled. + /// + [SerializeField] public bool updateITimeControl = true; + + /// + /// Indicates whether to search the entire hierarchy for controllable components. + /// + [SerializeField] public bool searchHierarchy = false; + + /// + /// Indicate whether GameObject activation is controlled + /// + [SerializeField] public bool active = true; + + /// + /// Indicates the active state of the GameObject when Timeline is stopped. + /// + [SerializeField] public ActivationControlPlayable.PostPlaybackState postPlayback = ActivationControlPlayable.PostPlaybackState.Revert; + + PlayableAsset m_ControlDirectorAsset; + double m_Duration = PlayableBinding.DefaultDuration; + bool m_SupportLoop; + + private static HashSet s_ProcessedDirectors = new HashSet(); + private static HashSet s_CreatedPrefabs = new HashSet(); + + // does the last instance created control directors and/or particles + internal bool controllingDirectors { get; private set; } + internal bool controllingParticles { get; private set; } + + /// + /// This function is called when the object is loaded. + /// + public void OnEnable() + { + // can't be set in a constructor + if (particleRandomSeed == 0) + particleRandomSeed = (uint)Random.Range(1, k_MaxRandInt); + } + + /// + /// Returns the duration in seconds needed to play the underlying director or particle system exactly once. + /// + public override double duration { get { return m_Duration; } } + + /// + /// Returns the capabilities of TimelineClips that contain a ControlPlayableAsset + /// + public ClipCaps clipCaps + { + get { return ClipCaps.ClipIn | ClipCaps.SpeedMultiplier | (m_SupportLoop ? ClipCaps.Looping : ClipCaps.None); } + } + + /// + /// Creates the root of a Playable subgraph to control the contents of the game object. + /// + /// PlayableGraph that will own the playable + /// The GameObject that triggered the graph build + /// The root playable of the subgraph + public override Playable CreatePlayable(PlayableGraph graph, GameObject go) + { + // case 989856 + if (prefabGameObject != null) + { + if (s_CreatedPrefabs.Contains(prefabGameObject)) + { + Debug.LogWarningFormat("Control Track Clip ({0}) is causing a prefab to instantiate itself recursively. Aborting further instances.", name); + return Playable.Create(graph); + } + s_CreatedPrefabs.Add(prefabGameObject); + } + + Playable root = Playable.Null; + var playables = new List(); + + GameObject sourceObject = sourceGameObject.Resolve(graph.GetResolver()); + if (prefabGameObject != null) + { + Transform parenTransform = sourceObject != null ? sourceObject.transform : null; + var controlPlayable = PrefabControlPlayable.Create(graph, prefabGameObject, parenTransform); + + sourceObject = controlPlayable.GetBehaviour().prefabInstance; + playables.Add(controlPlayable); + } + + m_Duration = PlayableBinding.DefaultDuration; + m_SupportLoop = false; + + controllingParticles = false; + controllingDirectors = false; + + if (sourceObject != null) + { + var directors = updateDirector ? GetComponent(sourceObject) : k_EmptyDirectorsList; + var particleSystems = updateParticle ? GetParticleSystemRoots(sourceObject) : k_EmptyParticlesList; + + // update the duration and loop values (used for UI purposes) here + // so they are tied to the latest gameObject bound + UpdateDurationAndLoopFlag(directors, particleSystems); + + var director = go.GetComponent(); + if (director != null) + m_ControlDirectorAsset = director.playableAsset; + + if (go == sourceObject && prefabGameObject == null) + { + Debug.LogWarningFormat("Control Playable ({0}) is referencing the same PlayableDirector component than the one in which it is playing.", name); + active = false; + if (!searchHierarchy) + updateDirector = false; + } + + if (active) + CreateActivationPlayable(sourceObject, graph, playables); + + if (updateDirector) + SearchHierarchyAndConnectDirector(directors, graph, playables, prefabGameObject != null); + + if (updateParticle) + SearchHiearchyAndConnectParticleSystem(particleSystems, graph, playables); + + if (updateITimeControl) + SearchHierarchyAndConnectControlableScripts(GetControlableScripts(sourceObject), graph, playables); + + // Connect Playables to Generic to Mixer + root = ConnectPlayablesToMixer(graph, playables); + } + + if (prefabGameObject != null) + s_CreatedPrefabs.Remove(prefabGameObject); + + if (!root.IsValid()) + root = Playable.Create(graph); + + return root; + } + + static Playable ConnectPlayablesToMixer(PlayableGraph graph, List playables) + { + var mixer = Playable.Create(graph, playables.Count); + + for (int i = 0; i != playables.Count; ++i) + { + ConnectMixerAndPlayable(graph, mixer, playables[i], i); + } + + mixer.SetPropagateSetTime(true); + + return mixer; + } + + void CreateActivationPlayable(GameObject root, PlayableGraph graph, + List outplayables) + { + var activation = ActivationControlPlayable.Create(graph, root, postPlayback); + if (activation.IsValid()) + outplayables.Add(activation); + } + + void SearchHiearchyAndConnectParticleSystem(IEnumerable particleSystems, PlayableGraph graph, + List outplayables) + { + foreach (var particleSystem in particleSystems) + { + if (particleSystem != null) + { + controllingParticles = true; + outplayables.Add(ParticleControlPlayable.Create(graph, particleSystem, particleRandomSeed)); + } + } + } + + void SearchHierarchyAndConnectDirector(IEnumerable directors, PlayableGraph graph, + List outplayables, bool disableSelfReferences) + { + foreach (var director in directors) + { + if (director != null) + { + if (director.playableAsset != m_ControlDirectorAsset) + { + outplayables.Add(DirectorControlPlayable.Create(graph, director)); + controllingDirectors = true; + } + // if this self references, disable the director. + else if (disableSelfReferences) + { + director.enabled = false; + } + } + } + } + + static void SearchHierarchyAndConnectControlableScripts(IEnumerable controlableScripts, PlayableGraph graph, List outplayables) + { + foreach (var script in controlableScripts) + { + outplayables.Add(TimeControlPlayable.Create(graph, (ITimeControl)script)); + } + } + + static void ConnectMixerAndPlayable(PlayableGraph graph, Playable mixer, Playable playable, + int portIndex) + { + graph.Connect(playable, 0, mixer, portIndex); + mixer.SetInputWeight(playable, 1.0f); + } + + internal IList GetComponent(GameObject gameObject) + { + var components = new List(); + if (gameObject != null) + { + if (searchHierarchy) + { + gameObject.GetComponentsInChildren(true, components); + } + else + { + gameObject.GetComponents(components); + } + } + return components; + } + + static IEnumerable GetControlableScripts(GameObject root) + { + if (root == null) + yield break; + + foreach (var script in root.GetComponentsInChildren()) + { + if (script is ITimeControl) + yield return script; + } + } + + internal void UpdateDurationAndLoopFlag(IList directors, IList particleSystems) + { + if (directors.Count == 0 && particleSystems.Count == 0) + return; + + const double invalidDuration = double.NegativeInfinity; + + var maxDuration = invalidDuration; + var supportsLoop = false; + + foreach (var director in directors) + { + if (director.playableAsset != null) + { + var assetDuration = director.playableAsset.duration; + + if (director.playableAsset is TimelineAsset && assetDuration > 0.0) + // Timeline assets report being one tick shorter than they actually are, unless they are empty + assetDuration = (double)((DiscreteTime)assetDuration).OneTickAfter(); + + maxDuration = Math.Max(maxDuration, assetDuration); + supportsLoop = supportsLoop || director.extrapolationMode == DirectorWrapMode.Loop; + } + } + + foreach (var particleSystem in particleSystems) + { + maxDuration = Math.Max(maxDuration, particleSystem.main.duration); + supportsLoop = supportsLoop || particleSystem.main.loop; + } + + m_Duration = double.IsNegativeInfinity(maxDuration) ? PlayableBinding.DefaultDuration : maxDuration; + m_SupportLoop = supportsLoop; + } + + IList GetParticleSystemRoots(GameObject go) + { + if (searchHierarchy) + { + // We only want the parent systems as they will handle all the child systems. + var roots = new List(); + GetParticleSystemRoots(go.transform, roots); + return roots; + } + return GetComponent(go); + } + + static void GetParticleSystemRoots(Transform t, ICollection roots) + { + var ps = t.GetComponent(); + if (ps != null) + { + // its a root + roots.Add(ps); + return; + } + + for (int i = 0; i < t.childCount; ++i) + { + GetParticleSystemRoots(t.GetChild(i), roots); + } + } + + /// + public void GatherProperties(PlayableDirector director, IPropertyCollector driver) + { + if (director == null) + return; + + // prevent infinite recursion + if (s_ProcessedDirectors.Contains(director)) + return; + s_ProcessedDirectors.Add(director); + + var gameObject = sourceGameObject.Resolve(director); + if (gameObject != null) + { + if (updateParticle) + { + // case 1076850 -- drive all emitters, not just roots. + foreach (var ps in gameObject.GetComponentsInChildren(true)) + { + driver.AddFromName(ps.gameObject, "randomSeed"); + driver.AddFromName(ps.gameObject, "autoRandomSeed"); + } + } + + if (active) + { + driver.AddFromName(gameObject, "m_IsActive"); + } + + if (updateITimeControl) + { + foreach (var script in GetControlableScripts(gameObject)) + { + var propertyPreview = script as IPropertyPreview; + if (propertyPreview != null) + propertyPreview.GatherProperties(director, driver); + else + driver.AddFromComponent(script.gameObject, script); + } + } + + if (updateDirector) + { + foreach (var childDirector in GetComponent(gameObject)) + { + if (childDirector == null) + continue; + + var timeline = childDirector.playableAsset as TimelineAsset; + if (timeline == null) + continue; + + timeline.GatherProperties(childDirector, driver); + } + } + } + s_ProcessedDirectors.Remove(director); + } + } +} -- cgit v1.2.3