using UnityEngine;
using System.Linq;
using System;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
///
/// A VFXToolboxCanvas component used in Image Sequencer
///
public class ImageSequencerCanvas : VFXToolboxCanvas
{
internal bool showExtraInfo
{
get
{
return m_bShowExtraInfo;
}
set
{
m_bShowExtraInfo = value;
}
}
internal int numFrames
{
get
{
return m_PreviewSequence.length;
}
}
internal ProcessingFrameSequence sequence
{
get
{
return m_PreviewSequence;
}
set
{
m_PreviewSequence = value;
}
}
internal ProcessingFrame currentFrame
{
get {
return m_ProcessingFrame;
}
set {
m_ProcessingFrame = value;
if(value != null)
InvalidateRenderTarget();
}
}
internal int currentFrameIndex
{
get
{
return m_CurrentFrame;
}
set
{
if (m_CurrentFrame != value)
{
m_CurrentFrame = value;
UpdateCanvasSequence();
}
}
}
internal bool isPlaying
{
get
{
return m_IsPlaying;
}
}
private bool m_bShowExtraInfo = false;
private int m_CurrentFrame = 0;
private ProcessingFrameSequence m_PreviewSequence;
private ProcessingFrame m_ProcessingFrame;
private Rect m_PlayControlsRect;
private ImageSequencer m_ImageSequencerWindow;
private bool m_IsPlaying = false;
private float m_PlayFramerate = 30.0f;
private float m_PlayTime = 0.0f;
private double m_EditorTime;
private bool m_IsScrobbing;
internal ImageSequencerCanvas(Rect displayRect, ImageSequencer editorWindow)
:base(displayRect)
{
m_ImageSequencerWindow = editorWindow;
m_IsScrobbing = false;
m_PlayControlsRect = new Rect(16, 16, 420, 26);
}
internal override void Invalidate(bool needRedraw)
{
base.Invalidate(needRedraw);
m_ImageSequencerWindow.Invalidate();
}
///
/// As texture display is handled by the processing stack, this function is not used.
///
/// A texture
protected override sealed void SetTexture(Texture tex)
{
// Should Never Happen
throw new NotImplementedException();
}
///
/// Gets the current frame from the current processing node in the processing stack.
///
/// Currently displayed texture
protected override sealed Texture GetTexture()
{
if (currentFrame != null)
{
return currentFrame.texture;
}
else
return null;
}
///
/// Returns how many mip-maps are stored in the currently displayed texture object.
///
/// mip map count
protected override sealed int GetMipMapCount()
{
if (currentFrame != null)
return currentFrame.mipmapCount;
else
return 0;
}
///
/// Handles all the Common VFXToolboxCanvas Keyboard events plus all required ones from Image Sequencer
///
protected override sealed void HandleKeyboardEvents()
{
base.HandleKeyboardEvents();
if(Event.current.type == EventType.KeyDown)
{
switch(Event.current.keyCode)
{
// Viewport Toggles
case KeyCode.H:
showExtraInfo = !showExtraInfo;
break;
// Play Controls
case KeyCode.Space:
TogglePlaySequence();
break;
case KeyCode.LeftArrow:
if (Event.current.shift)
FirstFrame();
else
PreviousFrame();
break;
case KeyCode.RightArrow:
if (Event.current.shift)
LastFrame();
else
NextFrame();
break;
default:
return; // Return without using event.
}
Invalidate(false);
Event.current.Use();
}
}
///
/// Draws the grid around the texture, and all Flipbook frame subdivisions
///
protected override sealed void DrawGrid()
{
int GridNumU = m_PreviewSequence.numU;
int GridNumV = m_PreviewSequence.numV;
Vector2 src, dst;
float v;
Texture texture = currentFrame.texture;
if(BackgroundBrightness < 0.5f)
Handles.color = new Color(1.0f,1.0f,1.0f,0.33333f);
else
Handles.color = new Color(0.0f,0.0f,0.0f,0.66666f);
for(int i = 0; i <= GridNumV; i++)
{
v = -(texture.height * 0.5f) + (float)i / GridNumV * texture.height;
src = CanvasToScreen(new Vector2(-texture.width * 0.5f, v));
dst = CanvasToScreen(new Vector2(texture.width * 0.5f, v));
Handles.DrawLine(src, dst);
}
for(int j = 0; j <= GridNumU; j++)
{
v = -(texture.width * 0.5f) + (float)j / GridNumU * texture.width;
src = CanvasToScreen(new Vector2(v,-texture.height * 0.5f));
dst = CanvasToScreen(new Vector2(v, texture.height * 0.5f));
Handles.DrawLine(src, dst);
}
Handles.color = Color.white;
}
internal void OnGUI(ImageSequencer editor)
{
OnGUI();
// Processor extra info
GUI.BeginGroup(displayRect);
if (editor.currentProcessingNode != null && editor.currentProcessingNode.Enabled && m_bShowExtraInfo && editor.sidePanelViewMode == ImageSequencer.SidePanelMode.Processors)
editor.currentProcessingNode.OnCanvasGUI(this);
GUI.EndGroup();
// Everytime text
string procName = (editor.sidePanelViewMode == ImageSequencer.SidePanelMode.Export) ? "Export" : (sequence.processingNode == null ? "Input Frames" : sequence.processingNode.ToString());
GUI.Label(new RectOffset(24,24,24,24).Remove(displayRect), procName , styles.largeLabel);
GUI.Label(new RectOffset(24,24,64,24).Remove(displayRect), GetDebugInfoString() , styles.label);
//EditorGUI.DrawRect(m_Rect, Color.red);
// Play Controls
if(sequence != null && sequence.length > 1)
{
DrawSequenceControls(displayRect, editor);
}
if (m_IsPlaying)
UpdatePlay();
}
private GUIContent GetDebugInfoString()
{
string output = "";
if(m_ProcessingFrame != null)
{
output += "Frame Size : " + m_ProcessingFrame.texture.width + " x " + m_ProcessingFrame.texture.height;
output += "\nMipMaps : " + m_ProcessingFrame.mipmapCount;
}
return new GUIContent(output);
}
internal void DrawSequenceControls(Rect ViewportArea, ImageSequencer editor)
{
m_PlayControlsRect = new Rect(ViewportArea.x , (ViewportArea.y + ViewportArea.height), ViewportArea.width , 100);
using (new GUILayout.AreaScope(m_PlayControlsRect, GUIContent.none, ImageSequencer.styles.playbackControlWindow))
{
Rect area = new Rect(16,16,m_PlayControlsRect.width-32,m_PlayControlsRect.height-32);
//GUILayout.BeginArea(area);
using (new GUILayout.VerticalScope())
{
// TRACKBAR
int count = sequence.length;
GUILayout.Space(16); // Reserve Layout for labels
Rect bar_rect = GUILayoutUtility.GetRect(area.width, 16);
EditorGUIUtility.AddCursorRect(bar_rect, MouseCursor.ResizeHorizontal);
if(Event.current.type == EventType.MouseDown && bar_rect.Contains(Event.current.mousePosition))
{
m_IsScrobbing = true;
}
if(Event.current.type == EventType.MouseUp || Event.current.rawType == EventType.MouseUp)
{
m_IsScrobbing = false;
}
if(m_IsScrobbing && (Event.current.type == EventType.MouseDrag || Event.current.type == EventType.MouseDown))
{
float pos = (Event.current.mousePosition.x - bar_rect.x) / bar_rect.width;
int frame = (int)Mathf.Round(pos * numFrames);
if (frame != currentFrameIndex)
{
currentFrameIndex = frame;
Invalidate(true);
}
}
EditorGUI.DrawRect(bar_rect, ImageSequencer.styles.CookBarDirty);
float width = bar_rect.width / count;
Rect textpos;
for (int i = 0; i < count; i++)
{
if(!sequence.frames[i].dirty)
{
Rect cell = new Rect(bar_rect.x + i * width, bar_rect.y, width, bar_rect.height);
EditorGUI.DrawRect(cell, ImageSequencer.styles.CookBarCooked);
}
if(i == currentFrameIndex)
{
Rect cursor = new Rect(bar_rect.x + i * width, bar_rect.y, width, bar_rect.height);
EditorGUI.DrawRect(cursor, new Color(1.0f,1.0f,1.0f,0.5f));
}
// Labels : Every multiple of 10 based on homemade formula
int step = 10 * (int)Mathf.Max(1,Mathf.Floor(8*(float)count / bar_rect.width));
if( ((i+1) % step) == 0 )
{
textpos = new Rect(bar_rect.x + i * width, bar_rect.y - 16, 32, 16);
GUI.Label(textpos, (i+1).ToString(), EditorStyles.largeLabel);
Rect cursor = new Rect(bar_rect.x + i * width, bar_rect.y, 1, bar_rect.height);
EditorGUI.DrawRect(cursor, new Color(1.0f,1.0f,1.0f,0.2f));
}
}
// Labels : First
textpos = new Rect(bar_rect.x, bar_rect.y - 16, 32, 16);
GUI.Label(textpos, VFXToolboxGUIUtility.Get("1"), EditorStyles.largeLabel);
GUILayout.Space(16);
// PLAY CONTROLS
bool lastplay;
using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
{
lastplay = m_IsPlaying;
if(GUILayout.Button(ImageSequencer.styles.iconFirst, VFXToolboxStyles.toolbarButton, GUILayout.Width(32)))
{
FirstFrame();
}
if(GUILayout.Button(ImageSequencer.styles.iconBack, VFXToolboxStyles.toolbarButton, GUILayout.Width(24)))
{
PreviousFrame();
}
bool playing = GUILayout.Toggle(m_IsPlaying,ImageSequencer.styles.iconPlay, VFXToolboxStyles.toolbarButton, GUILayout.Width(24));
if(m_IsPlaying != playing)
{
TogglePlaySequence();
}
if(GUILayout.Button(ImageSequencer.styles.iconForward, VFXToolboxStyles.toolbarButton, GUILayout.Width(24)))
{
NextFrame();
}
if(GUILayout.Button(ImageSequencer.styles.iconLast, VFXToolboxStyles.toolbarButton, GUILayout.Width(32)))
{
LastFrame();
}
if (lastplay != m_IsPlaying)
{
m_EditorTime = EditorApplication.timeSinceStartup;
}
GUILayout.FlexibleSpace();
GUILayout.Label(VFXToolboxGUIUtility.GetTextAndIcon("Frame : ","Profiler.Record"), VFXToolboxStyles.toolbarButton);
m_CurrentFrame = Mathf.Clamp(EditorGUILayout.IntField(m_CurrentFrame+1, VFXToolboxStyles.toolbarTextField, GUILayout.Width(42))-1,0,numFrames-1);
GUILayout.Label(" on " + numFrames + " ( TCR : " + GetTCR(m_CurrentFrame, (int)m_PlayFramerate) + " ) " , VFXToolboxStyles.toolbarButton);
GUILayout.FlexibleSpace();
ShowFrameratePopup();
}
}
//GUILayout.EndArea();
}
}
internal void UpdateCanvasSequence()
{
int length;
if (sequence.processingNode != null)
length = sequence.processingNode.GetProcessorSequenceLength();
else
length = sequence.length;
if (length > 0)
{
currentFrameIndex = Mathf.Clamp(currentFrameIndex, 0, length - 1);
currentFrame = sequence.RequestFrame(currentFrameIndex);
}
else
currentFrame = null;
}
#region PLAY CONTROLS
private void TogglePlaySequence()
{
if(m_IsPlaying)
StopSequence();
else
PlaySequence();
}
private void PlaySequence()
{
m_IsPlaying = true;
m_PlayTime = currentFrameIndex / m_PlayFramerate;
m_EditorTime = EditorApplication.timeSinceStartup;
}
private void StopSequence()
{
m_IsPlaying = false;
}
private void NextFrame()
{
int frame = currentFrameIndex + 1;
if (frame >= numFrames) frame = 0;
currentFrameIndex = frame;
}
private void PreviousFrame()
{
int frame = currentFrameIndex - 1;
if (frame < 0) frame = numFrames - 1;
currentFrameIndex = frame;
}
private void FirstFrame()
{
currentFrameIndex = 0;
m_PlayTime = 0;
}
private void LastFrame()
{
currentFrameIndex = numFrames - 1;
m_PlayTime = (numFrames - 1) / m_PlayFramerate ;
}
private string GetTCR(int frame, int framerate)
{
int frames = frame % framerate;
int seconds = frame / framerate;
int minutes = seconds / 60;
seconds %= 60;
minutes %= 60;
int numbering = (int)Mathf.Max(2,Mathf.Floor(Mathf.Log10(framerate))+1); // Minimum 2 digits
return minutes.ToString("D2") + ":" + seconds.ToString("D2") + ":" + frames.ToString("D"+numbering);
}
private void ShowFrameratePopup()
{
if(GUILayout.Button(VFXToolboxGUIUtility.GetTextAndIcon("Speed","SpeedScale"),EditorStyles.toolbarDropDown))
{
GenericMenu menu = new GenericMenu();
menu.AddItem(VFXToolboxGUIUtility.Get("5 fps"),false, () =>{ m_PlayFramerate = 5; });
menu.AddItem(VFXToolboxGUIUtility.Get("10 fps"),false, () =>{ m_PlayFramerate = 10; });
menu.AddItem(VFXToolboxGUIUtility.Get("15 fps"),false, () =>{ m_PlayFramerate = 15; });
menu.AddItem(VFXToolboxGUIUtility.Get("20 fps"),false, () =>{ m_PlayFramerate = 20; });
menu.AddItem(VFXToolboxGUIUtility.Get("24 fps (Cine)"),false, () =>{ m_PlayFramerate = 24; });
menu.AddItem(VFXToolboxGUIUtility.Get("25 fps (PAL)"),false, () =>{ m_PlayFramerate = 25; });
menu.AddItem(VFXToolboxGUIUtility.Get("29.97 fps (NTSC)"),false, () =>{ m_PlayFramerate = 29.97f; });
menu.AddItem(VFXToolboxGUIUtility.Get("30 fps"),false, () =>{ m_PlayFramerate = 30; });
menu.AddItem(VFXToolboxGUIUtility.Get("50 fps"),false, () =>{ m_PlayFramerate = 50; });
menu.AddItem(VFXToolboxGUIUtility.Get("60 fps"),false, () =>{ m_PlayFramerate = 60; });
menu.ShowAsContext();
}
m_PlayFramerate = EditorGUILayout.FloatField(m_PlayFramerate, VFXToolboxStyles.toolbarTextField,GUILayout.Width(24));
EditorGUILayout.LabelField(VFXToolboxGUIUtility.Get("fps"),GUILayout.Width(24));
}
private void UpdatePlay()
{
double deltaTime = EditorApplication.timeSinceStartup - m_EditorTime;
m_PlayTime += (float)deltaTime;
m_PlayTime = m_PlayTime % ((1.0f / m_PlayFramerate) * numFrames);
currentFrameIndex = (int)Mathf.Floor(m_PlayTime*m_PlayFramerate);
m_EditorTime = EditorApplication.timeSinceStartup;
}
#endregion
#region STYLES
internal new class Styles
{
public GUIStyle miniLabel
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabel : m_ViewportMiniLabelDark; }
}
public GUIStyle miniLabelRight
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabelRight : m_ViewportMiniLabelRightDark; }
}
public GUIStyle miniLabelCenter
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabelCenter : m_ViewportMiniLabelCenterDark; }
}
public GUIStyle label
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportLabel : m_ViewportLabelDark; }
}
public GUIStyle largeLabel
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportLargeLabel : m_ViewportLargeLabelDark; }
}
public Color backgroundPanelColor
{
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_BackgroundPanelColor : m_BackgroundPanelColorDark; }
}
public Color red { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 0, 0, 1) : new Color(0.7f, 0, 0, 1); } }
public Color green { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 1, 0, 1) : new Color(0, 0.5f, 0, 1); } }
public Color blue { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 0, 1, 1) : new Color(0, 0, 0.5f, 1); } }
public Color white { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 1, 1, 1) : new Color(0, 0, 0, 1); } }
public Color black { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 0, 0, 1) : new Color(1, 1, 1, 1); } }
public Color yellow { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1.0f, 0.8f, 0.25f) : new Color(0.5f, 0.4f, 0.1f); } }
public Color cyan { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0.25f, 0.8f, 1.0f) : new Color(0.1f, 0.4f, 0.5f); } }
public Color fadewhite { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 1, 1, 0.25f) : new Color(0, 0, 0, 0.25f); } }
private GUIStyle m_ViewportMiniLabel;
private GUIStyle m_ViewportMiniLabelDark;
private GUIStyle m_ViewportMiniLabelRight;
private GUIStyle m_ViewportMiniLabelRightDark;
private GUIStyle m_ViewportMiniLabelCenter;
private GUIStyle m_ViewportMiniLabelCenterDark;
private GUIStyle m_ViewportLabel;
private GUIStyle m_ViewportLabelDark;
private GUIStyle m_ViewportLargeLabel;
private GUIStyle m_ViewportLargeLabelDark;
private ImageSequencerCanvas m_Canvas;
private Color m_BackgroundPanelColor;
private Color m_BackgroundPanelColorDark;
public Styles(ImageSequencerCanvas canvas)
{
m_Canvas = canvas;
Color lightGray = new Color(0.8f, 0.8f, 0.8f, 1.0f);
Color darkGray = new Color(0.2f, 0.2f, 0.2f, 1.0f);
m_ViewportMiniLabel = new GUIStyle(EditorStyles.miniLabel);
m_ViewportMiniLabel.normal.textColor = lightGray;
m_ViewportMiniLabelDark = new GUIStyle(EditorStyles.miniLabel);
m_ViewportMiniLabelDark.normal.textColor = darkGray;
m_ViewportMiniLabelRight = new GUIStyle(m_ViewportMiniLabel);
m_ViewportMiniLabelRight.alignment = TextAnchor.MiddleRight;
m_ViewportMiniLabelRightDark = new GUIStyle(m_ViewportMiniLabelDark);
m_ViewportMiniLabelRightDark.alignment = TextAnchor.MiddleRight;
m_ViewportMiniLabelCenter = new GUIStyle(m_ViewportMiniLabel);
m_ViewportMiniLabelCenter.alignment = TextAnchor.MiddleCenter;
m_ViewportMiniLabelCenterDark = new GUIStyle(m_ViewportMiniLabelDark);
m_ViewportMiniLabelCenterDark.alignment = TextAnchor.MiddleCenter;
m_ViewportLabel = new GUIStyle(EditorStyles.largeLabel);
m_ViewportLabel.normal.textColor = lightGray;
m_ViewportLabelDark = new GUIStyle(EditorStyles.largeLabel);
m_ViewportLabelDark.normal.textColor = darkGray;
m_ViewportLargeLabel = new GUIStyle(EditorStyles.largeLabel);
m_ViewportLargeLabel.fontSize = 24;
m_ViewportLargeLabel.normal.textColor = lightGray;
m_ViewportLargeLabelDark = new GUIStyle(EditorStyles.largeLabel);
m_ViewportLargeLabelDark.fontSize = 24;
m_ViewportLargeLabelDark.normal.textColor = darkGray;
m_BackgroundPanelColor = new Color(0.02f, 0.02f, 0.02f, 0.85f);
m_BackgroundPanelColorDark = new Color(0.5f, 0.5f, 0.5f, 0.5f);
}
}
#endregion
}
}