Add example Unity Project

This commit is contained in:
Michał Gdula 2023-04-26 01:55:33 +01:00
parent fda7ff28dd
commit e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions

View file

@ -0,0 +1,46 @@
using System;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// Runtime clip customized for 'infinite' tracks playables.
/// Used for clips whose time needs to match the timelines exactly
/// </summary>
class InfiniteRuntimeClip : RuntimeElement
{
private Playable m_Playable;
private static readonly Int64 kIntervalEnd = DiscreteTime.GetNearestTick(TimelineClip.kMaxTimeValue);
public InfiniteRuntimeClip(Playable playable)
{
m_Playable = playable;
}
public override Int64 intervalStart
{
get { return 0; }
}
public override Int64 intervalEnd
{
get { return kIntervalEnd; }
}
public override bool enable
{
set
{
if (value)
m_Playable.Play();
else
m_Playable.Pause();
}
}
public override void EvaluateAt(double localTime, FrameData frameData)
{
m_Playable.SetTime(localTime);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b5abcb38bac0c54794ad732a3fa0de3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.Timeline
{
interface IInterval
{
Int64 intervalStart { get; }
Int64 intervalEnd { get; }
}
struct IntervalTreeNode // interval node,
{
public Int64 center; // midpoint for this node
public int first; // index of first element of this node in m_Entries
public int last; // index of the last element of this node in m_Entries
public int left; // index in m_Nodes of the left subnode
public int right; // index in m_Nodes of the right subnode
}
class IntervalTree<T> where T : IInterval
{
internal struct Entry
{
public Int64 intervalStart;
public Int64 intervalEnd;
public T item;
}
const int kMinNodeSize = 10; // the minimum number of entries to have subnodes
const int kInvalidNode = -1;
const Int64 kCenterUnknown = Int64.MaxValue; // center hasn't been calculated. indicates no children
readonly List<Entry> m_Entries = new List<Entry>();
readonly List<IntervalTreeNode> m_Nodes = new List<IntervalTreeNode>();
/// <summary>
/// Whether the tree will be rebuilt on the next query
/// </summary>
public bool dirty { get; internal set; }
/// <summary>
/// Add an IInterval to the tree
/// </summary>
public void Add(T item)
{
if (item == null)
return;
m_Entries.Add(
new Entry()
{
intervalStart = item.intervalStart,
intervalEnd = item.intervalEnd,
item = item
}
);
dirty = true;
}
/// <summary>
/// Query the tree at a particular time
/// </summary>
/// <param name="value"></param>
/// <param name="results"></param>
public void IntersectsWith(Int64 value, List<T> results)
{
if (m_Entries.Count == 0)
return;
if (dirty)
{
Rebuild();
dirty = false;
}
if (m_Nodes.Count > 0)
Query(m_Nodes[0], value, results);
}
/// <summary>
/// Query the tree at a particular range of time
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="results"></param>
public void IntersectsWithRange(Int64 start, Int64 end, List<T> results)
{
if (start > end)
return;
if (m_Entries.Count == 0)
return;
if (dirty)
{
Rebuild();
dirty = false;
}
if (m_Nodes.Count > 0)
QueryRange(m_Nodes[0], start, end, results);
}
/// <summary>
/// Updates the intervals from their source. Use this to detect if the data in the tree
/// has changed.
/// </summary>
public void UpdateIntervals()
{
bool isDirty = false;
for (int i = 0; i < m_Entries.Count; i++)
{
var n = m_Entries[i];
var s = n.item.intervalStart;
var e = n.item.intervalEnd;
isDirty |= n.intervalStart != s;
isDirty |= n.intervalEnd != e;
m_Entries[i] = new Entry()
{
intervalStart = s,
intervalEnd = e,
item = n.item
};
}
dirty |= isDirty;
}
private void Query(IntervalTreeNode intervalTreeNode, Int64 value, List<T> results)
{
for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++)
{
var entry = m_Entries[i];
if (value >= entry.intervalStart && value < entry.intervalEnd)
{
results.Add(entry.item);
}
}
if (intervalTreeNode.center == kCenterUnknown)
return;
if (intervalTreeNode.left != kInvalidNode && value < intervalTreeNode.center)
Query(m_Nodes[intervalTreeNode.left], value, results);
if (intervalTreeNode.right != kInvalidNode && value > intervalTreeNode.center)
Query(m_Nodes[intervalTreeNode.right], value, results);
}
private void QueryRange(IntervalTreeNode intervalTreeNode, Int64 start, Int64 end, List<T> results)
{
for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++)
{
var entry = m_Entries[i];
if (end >= entry.intervalStart && start < entry.intervalEnd)
{
results.Add(entry.item);
}
}
if (intervalTreeNode.center == kCenterUnknown)
return;
if (intervalTreeNode.left != kInvalidNode && start < intervalTreeNode.center)
QueryRange(m_Nodes[intervalTreeNode.left], start, end, results);
if (intervalTreeNode.right != kInvalidNode && end > intervalTreeNode.center)
QueryRange(m_Nodes[intervalTreeNode.right], start, end, results);
}
private void Rebuild()
{
m_Nodes.Clear();
m_Nodes.Capacity = m_Entries.Capacity;
Rebuild(0, m_Entries.Count - 1);
}
private int Rebuild(int start, int end)
{
IntervalTreeNode intervalTreeNode = new IntervalTreeNode();
// minimum size, don't subdivide
int count = end - start + 1;
if (count < kMinNodeSize)
{
intervalTreeNode = new IntervalTreeNode() {center = kCenterUnknown, first = start, last = end, left = kInvalidNode, right = kInvalidNode};
m_Nodes.Add(intervalTreeNode);
return m_Nodes.Count - 1;
}
var min = Int64.MaxValue;
var max = Int64.MinValue;
for (int i = start; i <= end; i++)
{
var o = m_Entries[i];
min = Math.Min(min, o.intervalStart);
max = Math.Max(max, o.intervalEnd);
}
var center = (max + min) / 2;
intervalTreeNode.center = center;
// first pass, put every thing left of center, left
int x = start;
int y = end;
while (true)
{
while (x <= end && m_Entries[x].intervalEnd < center)
x++;
while (y >= start && m_Entries[y].intervalEnd >= center)
y--;
if (x > y)
break;
var nodeX = m_Entries[x];
var nodeY = m_Entries[y];
m_Entries[y] = nodeX;
m_Entries[x] = nodeY;
}
intervalTreeNode.first = x;
// second pass, put every start passed the center right
y = end;
while (true)
{
while (x <= end && m_Entries[x].intervalStart <= center)
x++;
while (y >= start && m_Entries[y].intervalStart > center)
y--;
if (x > y)
break;
var nodeX = m_Entries[x];
var nodeY = m_Entries[y];
m_Entries[y] = nodeX;
m_Entries[x] = nodeY;
}
intervalTreeNode.last = y;
// reserve a place
m_Nodes.Add(new IntervalTreeNode());
int index = m_Nodes.Count - 1;
intervalTreeNode.left = kInvalidNode;
intervalTreeNode.right = kInvalidNode;
if (start < intervalTreeNode.first)
intervalTreeNode.left = Rebuild(start, intervalTreeNode.first - 1);
if (end > intervalTreeNode.last)
intervalTreeNode.right = Rebuild(intervalTreeNode.last + 1, end);
m_Nodes[index] = intervalTreeNode;
return index;
}
public void Clear()
{
m_Entries.Clear();
m_Nodes.Clear();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f74c99a65464bb4b86ccb314ee95a7f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,110 @@
using UnityEngine;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
// The RuntimeClip wraps a single clip in an instantiated sequence.
// It supports the IInterval interface so that it can be stored in the interval tree
// It is this class that is returned by an interval tree query.
class RuntimeClip : RuntimeClipBase
{
TimelineClip m_Clip;
Playable m_Playable;
Playable m_ParentMixer;
public override double start
{
get { return m_Clip.extrapolatedStart; }
}
public override double duration
{
get { return m_Clip.extrapolatedDuration; }
}
public RuntimeClip(TimelineClip clip, Playable clipPlayable, Playable parentMixer)
{
Create(clip, clipPlayable, parentMixer);
}
void Create(TimelineClip clip, Playable clipPlayable, Playable parentMixer)
{
m_Clip = clip;
m_Playable = clipPlayable;
m_ParentMixer = parentMixer;
clipPlayable.Pause();
}
public TimelineClip clip
{
get { return m_Clip; }
}
public Playable mixer
{
get { return m_ParentMixer; }
}
public Playable playable
{
get { return m_Playable; }
}
public override bool enable
{
set
{
if (value && m_Playable.GetPlayState() != PlayState.Playing)
{
m_Playable.Play();
SetTime(m_Clip.clipIn);
}
else if (!value && m_Playable.GetPlayState() != PlayState.Paused)
{
m_Playable.Pause();
if (m_ParentMixer.IsValid())
m_ParentMixer.SetInputWeight(m_Playable, 0.0f);
}
}
}
public void SetTime(double time)
{
m_Playable.SetTime(time);
}
public void SetDuration(double duration)
{
m_Playable.SetDuration(duration);
}
public override void EvaluateAt(double localTime, FrameData frameData)
{
enable = true;
float weight = 1.0f;
if (clip.IsPreExtrapolatedTime(localTime))
weight = clip.EvaluateMixIn((float)clip.start);
else if (clip.IsPostExtrapolatedTime(localTime))
weight = clip.EvaluateMixOut((float)clip.end);
else
weight = clip.EvaluateMixIn(localTime) * clip.EvaluateMixOut(localTime);
if (mixer.IsValid())
mixer.SetInputWeight(playable, weight);
// localTime of the sequence to localtime of the clip
double clipTime = clip.ToLocalTime(localTime);
if (clipTime >= -DiscreteTime.tickValue/2 )
{
SetTime(clipTime);
}
SetDuration(clip.extrapolatedDuration);
}
public override void Reset()
{
SetTime(m_Clip.clipIn);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70a190a1b304d1e43995af35d09231d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
using System;
using UnityEngine;
namespace UnityEngine.Timeline
{
internal abstract class RuntimeClipBase : RuntimeElement
{
public abstract double start { get; }
public abstract double duration { get; }
public override Int64 intervalStart
{
get { return DiscreteTime.GetNearestTick(start); }
}
public override Int64 intervalEnd
{
get { return DiscreteTime.GetNearestTick(start + duration); }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70f955bbb437a494888ef54d97abb474
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
using System;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
abstract class RuntimeElement : IInterval
{
public abstract Int64 intervalStart { get; }
public abstract Int64 intervalEnd { get; }
public int intervalBit { get; set; }
public abstract bool enable { set; }
public abstract void EvaluateAt(double localTime, FrameData frameData);
public virtual void Reset() {}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76b6bf32a6fcf934aab8c529bddccc81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,111 @@
using System;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
// Special runtime clip implementation that handles playables that use a scheduling system
// such as Audio
internal class ScheduleRuntimeClip : RuntimeClipBase
{
private TimelineClip m_Clip;
private Playable m_Playable;
private Playable m_ParentMixer;
private double m_StartDelay;
private double m_FinishTail;
private bool m_Started = false;
// represents the start point when we want to start getting updated
public override double start
{
get { return Math.Max(0, m_Clip.start - m_StartDelay); }
}
public override double duration
{
get { return m_Clip.duration + m_FinishTail + m_Clip.start - start; }
}
public void SetTime(double time)
{
m_Playable.SetTime(time);
}
public TimelineClip clip { get { return m_Clip; } }
public Playable mixer { get { return m_ParentMixer; } }
public Playable playable { get { return m_Playable; } }
public ScheduleRuntimeClip(TimelineClip clip, Playable clipPlayable,
Playable parentMixer, double startDelay = 0.2, double finishTail = 0.1)
{
Create(clip, clipPlayable, parentMixer, startDelay, finishTail);
}
private void Create(TimelineClip clip, Playable clipPlayable, Playable parentMixer,
double startDelay, double finishTail)
{
m_Clip = clip;
m_Playable = clipPlayable;
m_ParentMixer = parentMixer;
m_StartDelay = startDelay;
m_FinishTail = finishTail;
clipPlayable.Pause();
}
public override bool enable
{
set
{
if (value && m_Playable.GetPlayState() != PlayState.Playing)
{
m_Playable.Play();
}
else if (!value && m_Playable.GetPlayState() != PlayState.Paused)
{
m_Playable.Pause();
if (m_ParentMixer.IsValid())
m_ParentMixer.SetInputWeight(m_Playable, 0.0f);
}
m_Started &= value;
}
}
public override void EvaluateAt(double localTime, FrameData frameData)
{
if (frameData.timeHeld)
{
enable = false;
return;
}
bool forceSeek = frameData.seekOccurred || frameData.timeLooped || frameData.evaluationType == FrameData.EvaluationType.Evaluate;
// If we are in the tail region of the clip, then dont do anything
if (localTime > start + duration - m_FinishTail)
return;
// this may set the weight to 1 in a delay, but it will avoid missing the start
float weight = clip.EvaluateMixIn(localTime) * clip.EvaluateMixOut(localTime);
if (mixer.IsValid())
mixer.SetInputWeight(playable, weight);
// localTime of the sequence to localtime of the clip
if (!m_Started || forceSeek)
{
// accounts for clip in and speed
double clipTime = clip.ToLocalTime(Math.Max(localTime, clip.start));
// multiply by the time scale so the delay is local to the clip
// Audio will rescale based on it's effective time scale (which includes the parent)
double startDelay = Math.Max(clip.start - localTime, 0) * clip.timeScale;
double durationLocal = m_Clip.duration * clip.timeScale;
if (m_Playable.IsPlayableOfType<AudioClipPlayable>())
((AudioClipPlayable)m_Playable).Seek(clipTime, startDelay, durationLocal);
m_Started = true;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b250be9db55288b48ac121c074d795e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: