321 lines
8.9 KiB
C#
321 lines
8.9 KiB
C#
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Globalization;
|
||
|
using System.Linq;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.UI;
|
||
|
using Object = UnityEngine.Object;
|
||
|
|
||
|
public class TimelineController : MonoBehaviour
|
||
|
{
|
||
|
[System.Serializable]
|
||
|
public class CopyBufferData
|
||
|
{
|
||
|
public ObjectContainer TrackedObject;
|
||
|
public FrameContent Content;
|
||
|
}
|
||
|
|
||
|
public static TimelineController Instance;
|
||
|
|
||
|
public TMPro.TMP_InputField FrameCountInput;
|
||
|
public TMPro.TMP_Text SelectedObjectLabel;
|
||
|
public TMPro.TMP_InputField
|
||
|
InputFrameRate,
|
||
|
InputFrameDelay;
|
||
|
|
||
|
public int FrameCount;
|
||
|
public int CurrentFrame;
|
||
|
public ScrollRect Timeline;
|
||
|
public ScrollRect ObjectsList;
|
||
|
public Slider TimelineSlider;
|
||
|
public CopyBufferData CopyBuffer = null;
|
||
|
|
||
|
public List<TimelineFrameContainer> Frames;
|
||
|
|
||
|
public bool Play;
|
||
|
public float FrameDelayMS = 33;
|
||
|
public float FrameRate = 30;
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
Instance = this;
|
||
|
}
|
||
|
|
||
|
IEnumerator Start()
|
||
|
{
|
||
|
while(ModelViewerMain.GetInstance() == null)
|
||
|
{
|
||
|
yield return 0;
|
||
|
}
|
||
|
|
||
|
SetFrameRate("30");
|
||
|
FrameCount = (int)float.Parse(FrameCountInput.text, CultureInfo.InvariantCulture);
|
||
|
FillTimeline(FrameCount);
|
||
|
UpdateTimeline();
|
||
|
|
||
|
StartCoroutine(UpdateLoop());
|
||
|
}
|
||
|
|
||
|
IEnumerator UpdateLoop()
|
||
|
{
|
||
|
while (true)
|
||
|
{
|
||
|
if (Play)
|
||
|
{
|
||
|
ChangeCurrentFrame(1, false);
|
||
|
}
|
||
|
yield return new WaitForSeconds(FrameDelayMS/1000);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void TogglePlay()
|
||
|
{
|
||
|
Play = !Play;
|
||
|
}
|
||
|
|
||
|
public void ChangeCurrentFrame(int frameChange)
|
||
|
{
|
||
|
TimelineController.ChangeCurrentFrame(frameChange, true);
|
||
|
}
|
||
|
|
||
|
public static void ChangeCurrentFrame(int frameChange, bool stopPlayback)
|
||
|
{
|
||
|
int frame = Instance.CurrentFrame + frameChange;
|
||
|
if (frame < 0)
|
||
|
frame = Instance.FrameCount - 1;
|
||
|
if (frame >= Instance.FrameCount)
|
||
|
frame = 0;
|
||
|
|
||
|
if (stopPlayback)
|
||
|
Instance.Play = false;
|
||
|
|
||
|
SetCurrentFrame(frame);
|
||
|
}
|
||
|
|
||
|
public static void SetCurrentFrame(int frameIndex)
|
||
|
{
|
||
|
var main = ModelViewerMain.GetInstance();
|
||
|
Instance.CurrentFrame = frameIndex;
|
||
|
//Debug.Log(frameIndex);
|
||
|
|
||
|
foreach (var container in main.CurrentScene.AllObjects)
|
||
|
{
|
||
|
container.GetClosestFrames(frameIndex, out var previousFrame, out var nextFrame);
|
||
|
var blendAmount = previousFrame == nextFrame? 0 : 1 - (float)(nextFrame.FrameNum - frameIndex) / (nextFrame.FrameNum - previousFrame.FrameNum);
|
||
|
|
||
|
container.Lerp(previousFrame.ObjectData, nextFrame.ObjectData, blendAmount);
|
||
|
}
|
||
|
UpdateTimeline();
|
||
|
}
|
||
|
|
||
|
public static void SetCurrentFrame(ObjectContainer container)
|
||
|
{
|
||
|
int frameIndex = Instance.CurrentFrame;
|
||
|
|
||
|
container.GetClosestFrames(frameIndex, out var previousFrame, out var nextFrame);
|
||
|
var blendAmount = previousFrame == nextFrame ? 0 : 1 - (float)(nextFrame.FrameNum - frameIndex) / (nextFrame.FrameNum - previousFrame.FrameNum);
|
||
|
|
||
|
container.Lerp(previousFrame.ObjectData, nextFrame.ObjectData, blendAmount);
|
||
|
}
|
||
|
|
||
|
public void FillTimeline(int frames)
|
||
|
{
|
||
|
if (frames > Frames.Count)
|
||
|
{
|
||
|
int diff = frames - Frames.Count;
|
||
|
for (int i = 0; i < diff; i++)
|
||
|
{
|
||
|
var frame = Instantiate(SharedResources.Instance.TimelineFrame, Timeline.content).SetNum(Frames.Count);
|
||
|
frame.name = Frames.Count.ToString();
|
||
|
Frames.Add(frame);
|
||
|
}
|
||
|
}
|
||
|
foreach (var frame in Frames)
|
||
|
{
|
||
|
frame.gameObject.SetActive(frame.FrameIndex < frames);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void OnSliderScroll()
|
||
|
{
|
||
|
var sliderPos = TimelineSlider.handleRect.position;
|
||
|
var newFrame = Frames.Where(f => f.gameObject.activeInHierarchy).OrderBy(frame => Vector3.Distance(sliderPos, frame.GetComponent<RectTransform>().position)).First();
|
||
|
if (CurrentFrame != newFrame.FrameIndex)
|
||
|
{
|
||
|
SetCurrentFrame(newFrame.FrameIndex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void SetFrameCount(string text)
|
||
|
{
|
||
|
FrameCount = int.Parse(text);
|
||
|
FillTimeline(FrameCount);
|
||
|
UpdateTimeline();
|
||
|
}
|
||
|
|
||
|
public void SetFrameDelay(string text)
|
||
|
{
|
||
|
FrameDelayMS = (int)float.Parse(text, CultureInfo.InvariantCulture);
|
||
|
|
||
|
if (FrameDelayMS == 0)
|
||
|
{
|
||
|
FrameDelayMS = 1;
|
||
|
}
|
||
|
|
||
|
FrameRate = 1 / (FrameDelayMS / 1000);
|
||
|
InputFrameRate.SetTextWithoutNotify(Mathf.Round(FrameRate).ToString());
|
||
|
InputFrameDelay.SetTextWithoutNotify(Mathf.Round(FrameDelayMS).ToString());
|
||
|
}
|
||
|
|
||
|
public void SetFrameRate(string text)
|
||
|
{
|
||
|
FrameRate = (int)float.Parse(text, CultureInfo.InvariantCulture);
|
||
|
|
||
|
if(FrameRate == 0)
|
||
|
{
|
||
|
FrameRate = 1;
|
||
|
}
|
||
|
|
||
|
FrameDelayMS = (1 / FrameRate) * 1000;
|
||
|
InputFrameRate.SetTextWithoutNotify(Mathf.Round(FrameRate).ToString());
|
||
|
InputFrameDelay.SetTextWithoutNotify(Mathf.Round(FrameDelayMS).ToString());
|
||
|
}
|
||
|
|
||
|
public static void SwapFrames(int frameNum1, int frameNum2)
|
||
|
{
|
||
|
if (frameNum1 < 0 || frameNum1 >= Instance.FrameCount) return;
|
||
|
if (frameNum2 < 0 || frameNum2 >= Instance.FrameCount) return;
|
||
|
|
||
|
var selectedObject = GetCurrentObject();
|
||
|
var frames = selectedObject.Frames;
|
||
|
|
||
|
var frame1 = selectedObject.TryGetFrame(frameNum1);
|
||
|
var frame2 = selectedObject.TryGetFrame(frameNum2);
|
||
|
|
||
|
if (frame1 != null)
|
||
|
{
|
||
|
frame1.FrameNum = frameNum2;
|
||
|
}
|
||
|
|
||
|
if (frame2 != null)
|
||
|
{
|
||
|
frame2.FrameNum = frameNum1;
|
||
|
}
|
||
|
|
||
|
selectedObject.Frames = selectedObject.Frames.OrderBy(f => f.FrameNum).ToList();
|
||
|
|
||
|
SetCurrentFrame(Instance.CurrentFrame);
|
||
|
}
|
||
|
|
||
|
public static void UpdateTimeline()
|
||
|
{
|
||
|
var container = GetCurrentObject();
|
||
|
|
||
|
Instance.SelectedObjectLabel.text = container.name;
|
||
|
|
||
|
var frames = Instance.Frames.ToList();
|
||
|
|
||
|
for (int i = 0; i < container.Frames.Count; i++)
|
||
|
{
|
||
|
var frameNum = container.Frames[i].FrameNum;
|
||
|
if (frameNum >= frames.Count) return;
|
||
|
|
||
|
var frame = frames[frameNum];
|
||
|
frame.UpdateContent(container, container.Frames[i]);
|
||
|
frames[frameNum] = null;
|
||
|
}
|
||
|
|
||
|
foreach(var frame in frames)
|
||
|
{
|
||
|
if (frame == null) continue;
|
||
|
|
||
|
frame.UpdateContent(container, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static ObjectContainer GetCurrentObject()
|
||
|
{
|
||
|
ObjectContainer container = ModelViewerMain.GetInstance().SelectedObject;
|
||
|
|
||
|
if (container == null)
|
||
|
{
|
||
|
container = ModelViewerMain.GetInstance().MainCameraOrbit;
|
||
|
}
|
||
|
|
||
|
return container;
|
||
|
}
|
||
|
|
||
|
public void OnButtonSet()
|
||
|
{
|
||
|
GetCurrentObject().SetKeyframe(CurrentFrame);
|
||
|
}
|
||
|
|
||
|
public void OnButtonDelete()
|
||
|
{
|
||
|
Frames[CurrentFrame].DeleteFrame();
|
||
|
}
|
||
|
|
||
|
public void OnButtonCopy()
|
||
|
{
|
||
|
Frames[CurrentFrame].CopyFrame();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void OnButtonPaste()
|
||
|
{
|
||
|
var data = CopyBuffer;
|
||
|
if (data == null) return;
|
||
|
|
||
|
if (GetCurrentObject() == data.TrackedObject)
|
||
|
{
|
||
|
if (Input.GetKey(KeyCode.LeftShift)) PasteStep0(data);
|
||
|
else PasteStep1(data, new PoseLoadOptions(true));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UIPopupMessage.Create("You're pasting from a different object. Continue?",
|
||
|
() =>
|
||
|
{
|
||
|
if (Input.GetKey(KeyCode.LeftShift)) PasteStep0(data);
|
||
|
else PasteStep1(data, new PoseLoadOptions(true));
|
||
|
},
|
||
|
() => { }
|
||
|
);
|
||
|
}
|
||
|
|
||
|
private void PasteStep0(CopyBufferData data)
|
||
|
{
|
||
|
UIPopupPastePanel.Create(
|
||
|
(pasteOptions) => { PasteStep1(data, pasteOptions); Debug.Log(pasteOptions.Root); },
|
||
|
() => { });
|
||
|
}
|
||
|
|
||
|
private void PasteStep1(CopyBufferData data, PoseLoadOptions options)
|
||
|
{
|
||
|
int frameNum = CurrentFrame;
|
||
|
var trackedObject = data.TrackedObject;
|
||
|
if (trackedObject == null)
|
||
|
{
|
||
|
Error.Log(Color.red, "Object has been deleted?");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!trackedObject.GetCurrentFrame(frameNum, out var currentFrame))
|
||
|
{
|
||
|
trackedObject.PastePose(data.Content.ObjectData, options);
|
||
|
currentFrame.SetObjectData(trackedObject.SerializeFrame());
|
||
|
SetCurrentFrame(trackedObject);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
UIPopupMessage.Create($"Replace existing data for {trackedObject.name}?",
|
||
|
() => {
|
||
|
trackedObject.PastePose(data.Content.ObjectData, options);
|
||
|
currentFrame.SetObjectData(trackedObject.SerializeFrame());
|
||
|
SetCurrentFrame(trackedObject);
|
||
|
},
|
||
|
() =>{}
|
||
|
);
|
||
|
}
|
||
|
}
|