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 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("Setting frame to: " + 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); Debug.Log($"Setting {container.name} to frame {previousFrame.FrameNum}({nextFrame.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().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); }, () =>{} ); } }