using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; using System.IO; using System.Diagnostics; using Toolbox.Library.Forms; using Toolbox.Library.Animations; namespace Toolbox.Library { //Thanks to forge! Based on // https://github.com/jam1garner/Smash-Forge/blob/52844da94c7bed830d841e0d7e5d49c3f2c69471/Smash%20Forge/GUI/ModelViewport.cs public partial class AnimationPanel : STUserControl { public PlayerState AnimationPlayerState = PlayerState.Stop; public enum PlayerState { Playing, Pause, Stop, } public bool IsLooping { get { return loopChkBox.Checked; } set { loopChkBox.Checked = value; } } public bool IsPlaying { get { return AnimationPlayerState == PlayerState.Playing; } } private static AnimationPanel _instance; public static AnimationPanel Instance { get { return _instance == null ? _instance = new AnimationPanel() : _instance; } } //Animation Functions public int AnimationSpeed = 60; public float Frame = 0; // Frame rate control private Thread renderThread; private bool renderThreadIsUpdating = false; public bool isOpen = true; private STAnimation stCurrentAnimation; public STAnimation CurrentSTAnimation { get { return stCurrentAnimation; } set { if (value == null) return; int frameCount = 1; if (value.FrameCount != 0) frameCount = (int)value.FrameCount; ResetModels(); stCurrentAnimation = value; totalFrame.Maximum = frameCount; totalFrame.Value = frameCount; currentFrameUpDown.Maximum = frameCount; animationTrackBar.FrameCount = frameCount; currentFrameUpDown.Value = 0; SetAnimationsToFrame(0); UpdateViewport(); } } private Animation currentAnimation; public Animation CurrentAnimation { get { return currentAnimation; } set { if (value == null) return; int frameCount = 1; if (value.FrameCount != 0) frameCount = value.FrameCount; ResetModels(); currentAnimation = value; totalFrame.Maximum = frameCount; totalFrame.Value = frameCount; currentFrameUpDown.Maximum = frameCount; animationTrackBar.FrameCount = frameCount; currentFrameUpDown.Value = 0; SetAnimationsToFrame(0); UpdateViewport(); } } public void ResetModels() { var viewport = LibraryGUI.GetActiveViewport(); if (viewport == null) return; if (viewport.scene == null) return; foreach (var drawable in viewport.scene.objects) { if (drawable is STSkeleton) { ((STSkeleton)drawable).reset(); } } } public AnimationPanel() { InitializeComponent(); BackColor = FormThemes.BaseTheme.FormBackColor; ForeColor = FormThemes.BaseTheme.FormForeColor; animationTrackBar.BackColor = FormThemes.BaseTheme.FormBackColor; animationTrackBar.ForeColor = FormThemes.BaseTheme.FormForeColor; animationTrackBar.FrameChanged += new EventHandler(animationTrackBar_ValueChanged); /* animationTrackBar.ThumbInnerColor = FormThemes.BaseTheme.TimelineThumbColor; animationTrackBar.ThumbOuterColor = FormThemes.BaseTheme.TimelineThumbColor; this.animationTrackBar.BarInnerColor = FormThemes.BaseTheme.FormBackColor; this.animationTrackBar.BarPenColorBottom = FormThemes.BaseTheme.FormBackColor; this.animationTrackBar.BarPenColorTop = FormThemes.BaseTheme.FormBackColor; this.animationTrackBar.ElapsedInnerColor = FormThemes.BaseTheme.FormBackColor; this.animationTrackBar.ElapsedPenColorBottom = FormThemes.BaseTheme.FormBackColor; this.animationTrackBar.ElapsedPenColorTop = FormThemes.BaseTheme.FormBackColor; */ panel1.BackColor = FormThemes.BaseTheme.FormBackColor; animationPlayBtn.BackColor = FormThemes.BaseTheme.FormBackColor; button2.BackColor = FormThemes.BaseTheme.FormBackColor; animationPlayBtn.ForeColor = FormThemes.BaseTheme.FormForeColor; button2.ForeColor = FormThemes.BaseTheme.FormForeColor; totalFrame.ForeColor = FormThemes.BaseTheme.FormForeColor; totalFrame.BackColor = FormThemes.BaseTheme.FormBackColor; currentFrameUpDown.ForeColor = FormThemes.BaseTheme.FormForeColor; currentFrameUpDown.BackColor = FormThemes.BaseTheme.FormBackColor; this.LostFocus += new System.EventHandler(AnimationPanel_LostFocus); } private void Play() { AnimationPlayerState = PlayerState.Playing; UpdateAnimationUI(); animationTrackBar.Play(); } private void Pause() { AnimationPlayerState = PlayerState.Stop; UpdateAnimationUI(); animationTrackBar.Stop(); } private void Stop() { currentFrameUpDown.Value = 0; AnimationPlayerState = PlayerState.Stop; UpdateAnimationUI(); } private void UpdateAnimationUI() { animationPlayBtn.BackgroundImage = IsPlaying ? Properties.Resources.stop : Properties.Resources.arrowR; } private void UpdateAnimationFrame() { if (IsPlaying) { if (currentFrameUpDown.InvokeRequired) { currentFrameUpDown.BeginInvoke((Action)(() => { AdvanceNextFrame(); })); } else { AdvanceNextFrame(); } } } private void AdvanceNextFrame() { if (animationTrackBar.CurrentFrame == animationTrackBar.FrameCount - 1) { if (IsLooping) currentFrameUpDown.Value = 0; else Stop(); } else if (!animationTrackBar.Locked) { if (currentFrameUpDown.Value < totalFrame.Value) currentFrameUpDown.Value++; } currentFrameUpDown.Refresh(); totalFrame.Refresh(); } private void animationPlayBtn_Click(object sender, EventArgs e) { if (currentAnimation?.FrameCount <= 0 && stCurrentAnimation?.FrameCount <= 0) return; if (AnimationPlayerState == PlayerState.Playing) Pause(); else Play(); } private void totalFrame_ValueChanged(object sender, EventArgs e) { if (currentAnimation == null && stCurrentAnimation == null) return; if (totalFrame.Value < 1) { totalFrame.Value = 1; } else { if (stCurrentAnimation != null) { stCurrentAnimation.FrameCount = (int)totalFrame.Value; animationTrackBar.CurrentFrame = 0; animationTrackBar.FrameCount = stCurrentAnimation.FrameCount; } else { if (currentAnimation.Tag is Animation) ((Animation)currentAnimation.Tag).FrameCount = (int)totalFrame.Value; currentAnimation.FrameCount = (int)totalFrame.Value; animationTrackBar.CurrentFrame = 0; animationTrackBar.FrameCount = currentAnimation.FrameCount; } } } private void UpdateViewport() { if (IsDisposed) return; Viewport viewport = LibraryGUI.GetActiveViewport(); if (viewport == null) return; if (viewport.GL_ControlLegacy != null && !viewport.GL_ControlLegacy.IsDisposed) { if (viewport.GL_ControlLegacy.InvokeRequired) { viewport.GL_ControlLegacy.Invoke((MethodInvoker)delegate { // Running on the UI thread viewport.GL_ControlLegacy.Invalidate(); }); } else { viewport.GL_ControlLegacy.Invalidate(); } } else { if (viewport.GL_ControlModern == null || viewport.GL_ControlModern.IsDisposed || viewport.GL_ControlModern.Disposing) return; if (viewport.GL_ControlModern.InvokeRequired) { viewport.GL_ControlModern.Invoke((MethodInvoker)delegate { // Running on the UI thread viewport.GL_ControlModern.Invalidate(); }); } else { viewport.GL_ControlModern.Invalidate(); } } } private void nextButton_Click(object sender, EventArgs e) { if (animationTrackBar.CurrentFrame < animationTrackBar.FrameCount) animationTrackBar.CurrentFrame++; } private void prevButton_Click(object sender, EventArgs e) { if (animationTrackBar.CurrentFrame > 0) animationTrackBar.CurrentFrame--; } private void animationTrackBar_Scroll(object sender, EventArgs e) { } private void animationTrackBar_ValueChanged(object sender, EventArgs e) { if (currentAnimation == null && stCurrentAnimation == null || totalFrame.Value <= 0) return; currentFrameUpDown.Value = (decimal)animationTrackBar.CurrentFrame; } private void OnFrameAdvanced() { UpdateViewport(); SetAnimationsToFrame(animationTrackBar.CurrentFrame); if (!renderThreadIsUpdating || !IsPlaying) UpdateViewport(); } private void SetAnimationsToFrame(float frameNum) { if (currentAnimation == null) return; var viewport = LibraryGUI.GetActiveViewport(); if (viewport == null || viewport.scene == null) return; if (stCurrentAnimation != null) { if (frameNum > stCurrentAnimation.FrameCount) return; float animFrameNum = frameNum; stCurrentAnimation.SetFrame(animFrameNum); stCurrentAnimation.NextFrame(); //Add frames to the playing animation stCurrentAnimation.Frame += frameNum; //Reset it when it reaches the total frame count if (stCurrentAnimation.Frame >= stCurrentAnimation.FrameCount && stCurrentAnimation.Loop) { stCurrentAnimation.Frame = 0; } } else { var anim = currentAnimation.Tag; float animFrameNum = frameNum; if (anim is MaterialAnimation) { ((MaterialAnimation)anim).SetFrame(animFrameNum); ((MaterialAnimation)anim).NextFrame(viewport); } else if (anim is VisibilityAnimation) { ((VisibilityAnimation)anim).SetFrame(animFrameNum); ((VisibilityAnimation)anim).NextFrame(viewport); } else if (anim is CameraAnimation) { ((CameraAnimation)anim).SetFrame(animFrameNum); ((CameraAnimation)anim).NextFrame(viewport); } else if (anim is LightAnimation) { ((LightAnimation)anim).SetFrame(animFrameNum); ((LightAnimation)anim).NextFrame(viewport); } else if (anim is FogAnimation) { ((FogAnimation)anim).SetFrame(animFrameNum); ((FogAnimation)anim).NextFrame(viewport); } else //Play a skeletal animation if it's not the other types { foreach (var drawable in viewport.scene.objects) { if (drawable is STSkeleton) { currentAnimation.SetFrame(animFrameNum); currentAnimation.NextFrame((STSkeleton)drawable); } } } //Add frames to the playing animation currentAnimation.Frame += frameNum; //Reset it when it reaches the total frame count if (currentAnimation.Frame >= currentAnimation.FrameCount) { currentAnimation.Frame = 0; } } } private void currentFrameUpDown_ValueChanged(object sender, EventArgs e) { if (currentFrameUpDown.Value > totalFrame.Value) currentFrameUpDown.Value = totalFrame.Value; //Check locked state current frame will change during playing if (!animationTrackBar.Locked) { animationTrackBar.CurrentFrame = (int)currentFrameUpDown.Value; } OnFrameAdvanced(); } public void AnimationPanel_FormClosed() { isOpen = false; Dispose(); } private void AnimationPanel_Load(object sender, EventArgs e) { Viewport viewport = LibraryGUI.GetActiveViewport(); if (viewport != null) { if (viewport.GL_ControlLegacy != null) viewport.GL_ControlLegacy.VSync = Runtime.enableVSync; else viewport.GL_ControlModern.VSync = Runtime.enableVSync; } renderThread = new Thread(new ThreadStart(RenderAndAnimationLoop)); renderThread.Start(); } private void RenderAndAnimationLoop() { if (IsDisposed) return; // TODO: We don't really need two timers. Stopwatch animationStopwatch = Stopwatch.StartNew(); // Wait for UI to load before triggering paint events. // int waitTimeMs = 500; // Thread.Sleep(waitTimeMs); // UpdateViewport(); int frameUpdateInterval = 5; int animationUpdateInterval = 16; while (isOpen) { // Always refresh the viewport when animations are playing. if (renderThreadIsUpdating || IsPlaying) { if (animationStopwatch.ElapsedMilliseconds > animationUpdateInterval) { UpdateAnimationFrame(); animationStopwatch.Restart(); } } else { // Avoid wasting the CPU if we don't need to render anything. Thread.Sleep(1); } } } private void AnimationPanel_Enter(object sender, EventArgs e) { } private void AnimationPanel_LostFocus(object sender, EventArgs e) { renderThreadIsUpdating = false; } private void AnimationPanel_Click(object sender, EventArgs e) { renderThreadIsUpdating = true; } private void AnimationPanel_Leave(object sender, EventArgs e) { renderThreadIsUpdating = false; } public void ClosePanel() { ResetModels(); renderThread.Abort(); renderThreadIsUpdating = false; stCurrentAnimation = null; currentAnimation = null; isOpen = false; Dispose(); } private void panel1_Paint(object sender, PaintEventArgs e) { } private void colorSlider1_Scroll(object sender, ScrollEventArgs e) { } } }