Firstborn/Library/PackageCache/com.unity.cinemachine@2.8.9/Runtime/Timeline/CinemachineMixer.cs

301 lines
12 KiB
C#
Raw Normal View History

2023-03-28 13:24:16 -04:00
#if !UNITY_2019_1_OR_NEWER
#define CINEMACHINE_TIMELINE
#endif
#if CINEMACHINE_TIMELINE
using UnityEngine;
using UnityEngine.Playables;
using Cinemachine;
using System.Collections.Generic;
//namespace Cinemachine.Timeline
//{
internal sealed class CinemachineMixer : PlayableBehaviour
{
public delegate PlayableDirector MasterDirectorDelegate();
static public MasterDirectorDelegate GetMasterPlayableDirector;
// The brain that this track controls
private CinemachineBrain mBrain;
private int mBrainOverrideId = -1;
private bool mPreviewPlay;
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
class ScrubbingCacheHelper
{
// Remember the active clips of the previous frame so we can track camera cuts
public int ActivePlayableA;
public int ActivePlayableB;
struct ClipObjects
{
public List<List<CinemachineVirtualCameraBase>> Cameras;
public float MaxDampTime;
}
List<ClipObjects> CachedObjects;
static List<CinemachineVirtualCameraBase> scratch = new List<CinemachineVirtualCameraBase>();
public void Init(Playable playable)
{
// Build our vcam registry for scrubbing updates
CachedObjects = new List<ClipObjects>(playable.GetInputCount());
for (int i = 0; i < playable.GetInputCount(); ++i)
{
var cs = new ClipObjects
{
Cameras = new List<List<CinemachineVirtualCameraBase>>(),
};
var clip = (ScriptPlayable<CinemachineShotPlayable>)playable.GetInput(i);
CinemachineShotPlayable shot = clip.GetBehaviour();
if (shot != null && shot.IsValid)
{
var mainVcam = shot.VirtualCamera;
cs.Cameras.Add(new List<CinemachineVirtualCameraBase>());
// Add all child cameras
scratch.Clear();
mainVcam.GetComponentsInChildren(scratch);
for (int j = 0; j < scratch.Count; ++j)
{
var vcam = scratch[j];
int nestLevel = 0;
for (ICinemachineCamera p = vcam.ParentCamera;
p != null && p != (ICinemachineCamera)mainVcam; p = p.ParentCamera)
{
++nestLevel;
}
while (cs.Cameras.Count <= nestLevel)
cs.Cameras.Add(new List<CinemachineVirtualCameraBase>());
cs.Cameras[nestLevel].Add(vcam);
cs.MaxDampTime = Mathf.Max(cs.MaxDampTime, vcam.GetMaxDampTime());
}
}
CachedObjects.Add(cs);
}
}
public void ScrubToHere(float currentTime, int playableIndex, bool isCut, float timeInClip, Vector3 up)
{
TargetPositionCache.CurrentTime = currentTime;
if (TargetPositionCache.CacheMode == TargetPositionCache.Mode.Record)
{
// If the clip is newly activated, force the time to clip start,
// in case timeline skipped some frames. This will avoid target lerps between shots.
if (Time.frameCount != TargetPositionCache.CurrentFrame)
TargetPositionCache.IsCameraCut = false;
TargetPositionCache.CurrentFrame = Time.frameCount;
if (isCut)
TargetPositionCache.IsCameraCut = true;
return;
}
if (!TargetPositionCache.HasCurrentTime)
return;
var cs = CachedObjects[playableIndex];
float stepSize = TargetPositionCache.CacheStepSize;
// Impose upper limit on damping time, to avoid simulating too many frames
float maxDampTime = Mathf.Max(0, timeInClip - stepSize);
maxDampTime = Mathf.Min(cs.MaxDampTime, Mathf.Min(maxDampTime, 4.0f));
var endTime = TargetPositionCache.CurrentTime;
var startTime = Mathf.Max(
TargetPositionCache.CacheTimeRange.Start + stepSize, endTime - maxDampTime);
var numSteps = Mathf.FloorToInt((endTime - startTime) / stepSize);
for (int step = numSteps; step >= 0; --step)
{
var t = Mathf.Max(startTime, endTime - step * stepSize);
TargetPositionCache.CurrentTime = t;
var deltaTime = (step == numSteps) ? -1
: (t - startTime < stepSize ? t - startTime : stepSize);
// Update all relevant vcams, leaf-most first
for (int i = cs.Cameras.Count - 1; i >= 0; --i)
{
var sublist = cs.Cameras[i];
for (int j = sublist.Count - 1; j >= 0; --j)
{
var vcam = sublist[j];
if (deltaTime < 0)
vcam.ForceCameraPosition(
TargetPositionCache.GetTargetPosition(vcam.transform),
TargetPositionCache.GetTargetRotation(vcam.transform));
vcam.InternalUpdateCameraState(up, deltaTime);
}
}
}
}
}
ScrubbingCacheHelper m_ScrubbingCacheHelper;
#endif
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
public override void OnGraphStart(Playable playable)
{
base.OnGraphStart(playable);
m_ScrubbingCacheHelper = null;
}
#endif
public override void OnPlayableDestroy(Playable playable)
{
if (mBrain != null)
mBrain.ReleaseCameraOverride(mBrainOverrideId); // clean up
mBrainOverrideId = -1;
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
m_ScrubbingCacheHelper = null;
#endif
}
public override void PrepareFrame(Playable playable, FrameData info)
{
mPreviewPlay = false;
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
var cacheMode = TargetPositionCache.Mode.Disabled;
if (!Application.isPlaying)
{
if (GetMasterPlayableDirector != null)
{
var d = GetMasterPlayableDirector();
if (d != null && d.playableGraph.IsValid())
mPreviewPlay = GetMasterPlayableDirector().playableGraph.IsPlaying();
}
if (TargetPositionCache.UseCache)
{
cacheMode = mPreviewPlay ? TargetPositionCache.Mode.Record : TargetPositionCache.Mode.Playback;
if (m_ScrubbingCacheHelper == null)
{
m_ScrubbingCacheHelper = new ScrubbingCacheHelper();
m_ScrubbingCacheHelper.Init(playable);
}
}
}
TargetPositionCache.CacheMode = cacheMode;
#endif
}
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
base.ProcessFrame(playable, info, playerData);
// Get the brain that this track controls.
// Older versions of timeline sent the gameObject by mistake.
GameObject go = playerData as GameObject;
if (go == null)
mBrain = (CinemachineBrain)playerData;
else
mBrain = go.GetComponent<CinemachineBrain>();
if (mBrain == null)
return;
// Find which clips are active. We can process a maximum of 2.
// In the case that the weights don't add up to 1, the outgoing weight
// will be calculated as the inverse of the incoming weight.
int activeInputs = 0;
int clipIndexA = -1;
int clipIndexB = -1;
bool incomingIsA = false; // Assume that incoming clip is clip B
float weightB = 1;
for (int i = 0; i < playable.GetInputCount(); ++i)
{
float weight = playable.GetInputWeight(i);
var clip = (ScriptPlayable<CinemachineShotPlayable>)playable.GetInput(i);
CinemachineShotPlayable shot = clip.GetBehaviour();
if (shot != null && shot.IsValid
&& playable.GetPlayState() == PlayState.Playing
&& weight > 0)
{
clipIndexA = clipIndexB;
clipIndexB = i;
weightB = weight;
if (++activeInputs == 2)
{
// Deduce which clip is incoming (timeline doesn't know)
var clipA = playable.GetInput(clipIndexA);
// Incoming has later start time (therefore earlier current time)
incomingIsA = clip.GetTime() >= clipA.GetTime();
// If same start time, longer clip is incoming
if (clip.GetTime() == clipA.GetTime())
incomingIsA = clip.GetDuration() < clipA.GetDuration();
break;
}
}
}
// Special case: check for only one clip that's fading out - it must be outgoing
if (activeInputs == 1 && weightB < 1
&& playable.GetInput(clipIndexB).GetTime() > playable.GetInput(clipIndexB).GetDuration() / 2)
{
incomingIsA = true;
}
if (incomingIsA)
{
(clipIndexA, clipIndexB) = (clipIndexB, clipIndexA);
weightB = 1 - weightB;
}
ICinemachineCamera camA = null;
if (clipIndexA >= 0)
{
CinemachineShotPlayable shot
= ((ScriptPlayable<CinemachineShotPlayable>)playable.GetInput(clipIndexA)).GetBehaviour();
camA = shot.VirtualCamera;
}
ICinemachineCamera camB = null;
if (clipIndexB >= 0)
{
CinemachineShotPlayable shot
= ((ScriptPlayable<CinemachineShotPlayable>)playable.GetInput(clipIndexB)).GetBehaviour();
camB = shot.VirtualCamera;
}
// Override the Cinemachine brain with our results
mBrainOverrideId = mBrain.SetCameraOverride(
mBrainOverrideId, camA, camB, weightB, GetDeltaTime(info.deltaTime));
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
if (m_ScrubbingCacheHelper != null && TargetPositionCache.CacheMode != TargetPositionCache.Mode.Disabled)
{
bool isNewB = (m_ScrubbingCacheHelper.ActivePlayableA != clipIndexB
&& m_ScrubbingCacheHelper.ActivePlayableB != clipIndexB);
m_ScrubbingCacheHelper.ActivePlayableA = clipIndexA;
m_ScrubbingCacheHelper.ActivePlayableB = clipIndexB;
if (clipIndexA >= 0)
m_ScrubbingCacheHelper.ScrubToHere(
(float)GetMasterPlayableDirector().time, clipIndexA, false,
(float)playable.GetInput(clipIndexA).GetTime(), mBrain.DefaultWorldUp);
if (clipIndexB >= 0)
m_ScrubbingCacheHelper.ScrubToHere(
(float)GetMasterPlayableDirector().time, clipIndexB, isNewB && weightB > 0.99f,
(float)playable.GetInput(clipIndexB).GetTime(), mBrain.DefaultWorldUp);
}
#endif
}
float GetDeltaTime(float deltaTime)
{
if (mPreviewPlay || Application.isPlaying)
return deltaTime;
// We're scrubbing or paused
if (TargetPositionCache.CacheMode == TargetPositionCache.Mode.Playback
&& TargetPositionCache.HasCurrentTime)
{
return 0;
}
return -1;
}
}
//}
#endif