浏览代码

Merge pull request #11 from Unity-Technologies/refactor/simplify-processors

Refactor/Simplify Processor API + Class Accessibility + Package conformance
/main
GitHub 5 年前
当前提交
7c92124c
共有 161 个文件被更改,包括 8021 次插入362 次删除
  1. 4
      LICENSE.md
  2. 97
      Editor/Common/Canvas/VFXToolboxCanvas.cs
  3. 2
      Editor/Common/External/CurveEditor.cs
  4. 2
      Editor/Common/Utility/VFXToolboxUtility.cs
  5. 2
      Editor/Common/Utility/VFXToolboxStyles.cs
  6. 2
      Editor/Common/Utility/VFXToolboxGUIUtility.cs
  7. 2
      Editor/Common/Utility/Splitter.cs
  8. 2
      Editor/Common/Utility/FloatSliderPropertyDrawer.cs
  9. 4
      Editor/Common/Utility/FilterPopupWindow.cs
  10. 2
      Editor/Common/Utility/CurveToTextureUtility.cs
  11. 2
      Editor/Common/Utility/CurveDrawer.cs
  12. 2
      Editor/Common/PropertyAttributes/FloatSliderAttribute.cs
  13. 175
      Editor/ImageSequencer/ProcessingNode.cs
  14. 12
      Editor/ImageSequencer/Serialization/ProcessorInfo.cs
  15. 9
      Editor/ImageSequencer/Serialization/ImageSequence.cs
  16. 36
      Editor/ImageSequencer/ProcessingFrameSequence.cs
  17. 26
      Editor/ImageSequencer/ProcessingFrame.cs
  18. 80
      Editor/ImageSequencer/ImageSequencerCanvas.cs
  19. 101
      Editor/ImageSequencer/ImageSequencer.cs
  20. 6
      Editor/ImageSequencer/ImageSequencer.Styles.cs
  21. 71
      Editor/ImageSequencer/ImageSequencer.Processors.cs
  22. 28
      Editor/ImageSequencer/ImageSequencer.InputFrames.cs
  23. 55
      Editor/ImageSequencer/ImageSequencer.GUI.cs
  24. 23
      Editor/ImageSequencer/ImageSequencer.Export.cs
  25. 4
      Editor/ImageSequencer/ImageSequenceAssetFactory.cs
  26. 4
      Editor/ImageSequencer/ImageSequenceAssetEditor.cs
  27. 25
      Editor/ImageSequencer/FilterPopup/ProcessorDataProvider.cs
  28. 6
      Editor/ImageSequencer/Exporters/MiniTGA.cs
  29. 8
      Editor/ImageSequencer/Exporters/MiniEXR.cs
  30. 22
      Editor/ImageSequencer/Attributes/ProcessorAttribute.cs
  31. 9
      CHANGELOG.md
  32. 7
      CHANGELOG.md.meta
  33. 8
      Editor/Common.meta
  34. 4
      Documentation~/TableOfContents.md
  35. 12
      Documentation~/index.md
  36. 15
      Documentation~/DCCTools.md
  37. 22
      Documentation~/images/ImageSequenceAssets.png
  38. 571
      Documentation~/images/ImageSequenceInspector.png
  39. 86
      Documentation~/images/ImageSequencer-AddProcessorMenu.png
  40. 21
      Documentation~/images/ImageSequencer-AlphaFromRGB.png
  41. 49
      Documentation~/images/ImageSequencer-AssembleFlipbook.png
  42. 14
      Documentation~/images/ImageSequencer-BreakFlipbook.png
  43. 10
      Documentation~/images/ImageSequencer-ClearProcessors.png
  44. 76
      Documentation~/images/ImageSequencer-ColorCorrection.png
  45. 120
      Documentation~/images/ImageSequencer-Crop.png
  46. 86
      Documentation~/images/ImageSequencer-CustomMaterial.png
  47. 19
      Documentation~/images/ImageSequencer-Decimate.png
  48. 58
      Documentation~/images/ImageSequencer-Export.png
  49. 56
      Documentation~/images/ImageSequencer-Fade.png
  50. 25
      Documentation~/images/ImageSequencer-FixBorders.png
  51. 49
      Documentation~/images/ImageSequencer-FrameProcessorList.png
  52. 9
      Documentation~/images/ImageSequencer-InheritProcessors.png
  53. 42
      Documentation~/images/ImageSequencer-InputFrames.png
  54. 34
      Documentation~/images/ImageSequencer-Looping.png
  55. 66
      Documentation~/images/ImageSequencer-OptionsPopup.png
  56. 6
      Documentation~/images/ImageSequencer-PremultiplyAlpha.png
  57. 19
      Documentation~/images/ImageSequencer-ProcessorPreview.png
  58. 18
      Documentation~/images/ImageSequencer-ProcessorPreviewLock.png
  59. 131
      Documentation~/images/ImageSequencer-Processors.png
  60. 76
      Documentation~/images/ImageSequencer-RemapColor.png
  61. 89
      Documentation~/images/ImageSequencer-RemoveBackground.png
  62. 53
      Documentation~/images/ImageSequencer-Resize.png
  63. 94
      Documentation~/images/ImageSequencer-Retime.png
  64. 6
      Documentation~/images/ImageSequencer-Rotate.png
  65. 10
      Documentation~/images/ImageSequencer-Tabs.png
  66. 18
      Documentation~/images/ImageSequencer-Toolbar.png
  67. 10
      Documentation~/images/ImageSequencer-UpdateButton.png
  68. 940
      Documentation~/images/ImageSequencer.png
  69. 11
      Documentation~/images/ImageSequencerNoAsset.png
  70. 649
      Documentation~/images/ImageSequencerWindow.png
  71. 317
      Documentation~/images/ImageSequencerWorkflow.png
  72. 854
      Documentation~/ImageSequencer.md
  73. 157
      Editor/ImageSequencer/ProcessingNodeStack.Serialization.cs
  74. 149
      Editor/ImageSequencer/ProcessingNodeStack.cs
  75. 203
      Editor/ImageSequencer/Serialization/ProcessorBase.cs
  76. 48
      Editor/ImageSequencer/Serialization/Processors/AlphaFromRGBProcessor.cs
  77. 319
      Editor/ImageSequencer/Serialization/Processors/AssembleProcessor.cs
  78. 101
      Editor/ImageSequencer/Serialization/Processors/BreakFlipbookProcessor.cs
  79. 113
      Editor/ImageSequencer/Serialization/Processors/ColorCorrectionProcessor.cs
  80. 230
      Editor/ImageSequencer/Serialization/Processors/CropProcessor.cs
  81. 84
      Editor/ImageSequencer/Serialization/Processors/CustomMaterialProcessor.cs
  82. 55
      Editor/ImageSequencer/Serialization/Processors/DecimateProcessor.cs
  83. 101
      Editor/ImageSequencer/Serialization/Processors/FadeProcessor.cs
  84. 137
      Editor/ImageSequencer/Serialization/Processors/FixBordersProcessor.cs
  85. 301
      Editor/ImageSequencer/Serialization/Processors/LoopingProcessor.cs
  86. 50
      Editor/ImageSequencer/Serialization/Processors/PremultiplyAlphaProcessor.cs
  87. 85
      Editor/ImageSequencer/Serialization/Processors/RemapColorProcessor.cs
  88. 80
      Editor/ImageSequencer/Serialization/Processors/RemoveBackgroundProcessor.cs
  89. 144
      Editor/ImageSequencer/Serialization/Processors/ResizeProcessor.cs
  90. 140
      Editor/ImageSequencer/Serialization/Processors/RetimeProcessor.cs
  91. 77
      Editor/ImageSequencer/Serialization/Processors/RotateProcessor.cs
  92. 4
      Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png
  93. 103
      Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png.meta
  94. 7
      Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png
  95. 103
      Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png.meta
  96. 9
      Editor/Examples.meta

4
LICENSE.md


Copyright © 2018 Unity Technologies ApS
com.unity.vfx-toolbox copyright © 2020 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

97
Editor/Common/Canvas/VFXToolboxCanvas.cs


using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
internal abstract class VFXToolboxCanvas
/// <summary>
/// An Editor UI Canvas that enables previsualizing an image. Derive from this class to implement your own.
/// </summary>
public abstract class VFXToolboxCanvas
public Rect displayRect
internal Rect displayRect
public bool maskR
internal bool maskR
{
get { return m_Material.GetColor("_RGBAMask").r == 1.0f; }
set

}
}
public bool maskG
internal bool maskG
{
get { return m_Material.GetColor("_RGBAMask").g == 1.0f; }
set

}
}
public bool maskB
internal bool maskB
{
get { return m_Material.GetColor("_RGBAMask").b == 1.0f; }
set

}
public bool maskA
internal bool maskA
{
get { return m_Material.GetColor("_RGBAMask").a == 1.0f; }
set

}
}
public bool filter
internal bool filter
{
get { return m_bFilter; }
set {

}
}
public int mipMap
internal int mipMap
{
get {
return m_MipMap;

}
}
public int mipMapCount
internal int mipMapCount
public bool showGrid
internal bool showGrid
{
get
{

}
}
public float BackgroundBrightness
internal float BackgroundBrightness
{
get
{

}
}
public Styles styles
internal Styles styles
{
get
{

}
}
/// <summary>
/// The current Zoom Level
/// </summary>
public float zoom
{
get

private int m_MipMap = 0;
private bool m_bShowGrid = true;
protected Rect m_Rect;
private Rect m_Rect;
private Shader m_Shader;
private Material m_Material;

private bool m_bNeedRedraw;
private float m_bgBrightness = -1.0f;
/// <summary>
/// The currently previewed Texture
/// </summary>
public Texture texture
{
get { return GetTexture(); }

public CanvasGUIDelegate onCanvasGUI;
public delegate void CanvasGUIDelegate();
internal CanvasGUIDelegate onCanvasGUI;
internal delegate void CanvasGUIDelegate();
public VFXToolboxCanvas(Rect displayRect, string shaderName = "Packages/com.unity.vfx-toolbox/Editor/Canvas/Shaders/VFXToolboxCanvas.shader")
internal VFXToolboxCanvas(Rect displayRect, string shaderName = "Packages/com.unity.vfx-toolbox/Editor/Common/Canvas/Shaders/VFXToolboxCanvas.shader")
{
m_Rect = displayRect;

m_RenderTexture = RenderTexture.GetTemporary(1,1,0);
}
public virtual void Invalidate(bool needRedraw)
internal virtual void Invalidate(bool needRedraw)
/// <summary>
/// Handle Setting the Texture to the canvas
/// </summary>
/// <param name="tex">The texture to display</param>
/// <summary>
/// Handle Getting the Texture displayed on the canvas
/// </summary>
/// <returns>The displayed texture</returns>
/// <summary>
/// Returns the count of the currently set texture
/// </summary>
/// <returns></returns>
protected virtual int GetMipMapCount()
{
if (texture != null)

return 0;
}
public void InvalidateRenderTarget()
internal void InvalidateRenderTarget()
private void UpdateRenderTarget()
internal void UpdateRenderTarget()
{
int width = Mathf.Max(1, texture.width / (int)Mathf.Pow(2, (mipMap)));
int height = Mathf.Max(1, texture.height / (int)Mathf.Pow(2, (mipMap)));

Invalidate(true);
}
public void Recenter(bool Refit)
internal void Recenter(bool Refit)
{
m_CameraPosition = Vector2.zero;
if(Refit)

}
}
/// <summary>
/// Handles common Keyboard events to perform navigation
/// </summary>
protected virtual void HandleKeyboardEvents()
{
if(Event.current.type == EventType.KeyDown)

ScaleMode.ScaleToFit
);
}
/// <summary>
/// Helper Function that converts a Canvas Position to a Screen Position based on the current Canvas Position and Zoom
/// </summary>
/// <param name="Position">Position in Canvas Space</param>
/// <returns>Position in Screen Space</returns>
public Vector2 CanvasToScreen(Vector2 Position)
{
return new Vector2(

}
/// <summary>
/// Draws a line grid around the texture
/// </summary>
protected virtual void DrawGrid()
{
Vector2 src, dst;

// Restore GUI RenderTarget
RenderTexture.active = oldrendertarget;
}
public virtual void OnGUI()
internal virtual void OnGUI()
{
if(m_bgBrightness < 0.0f)

#region BRIGHTNESS CONTROLS
public Texture2D BackgroundTexture { get { return m_BackgroundTexture; } }
internal Texture2D BackgroundTexture { get { return m_BackgroundTexture; } }
public void SetBGBrightness(float value)
internal void SetBGBrightness(float value)
public void ResetBrightness()
internal void ResetBrightness()
{
if (EditorGUIUtility.isProSkin)
BackgroundBrightness = 0.2f;

public void BrightnessUp(float value)
internal void BrightnessUp(float value)
public void BrightnessDown(float value)
internal void BrightnessDown(float value)
{
BackgroundBrightness = Mathf.Max(0.0f, BackgroundBrightness - value);
}

#region STYLES
public class Styles
internal class Styles
{
public GUIStyle miniLabel
{

2
Editor/Common/External/CurveEditor.cs


using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal sealed class CurveEditor
{

2
Editor/Common/Utility/VFXToolboxUtility.cs


using System.IO;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal class VFXToolboxUtility
{

2
Editor/Common/Utility/VFXToolboxStyles.cs


using UnityEngine;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal static class VFXToolboxStyles
{

2
Editor/Common/Utility/VFXToolboxGUIUtility.cs


using System;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal class VFXToolboxGUIUtility
{

2
Editor/Common/Utility/Splitter.cs


using System.IO;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal class Splitter
{

2
Editor/Common/Utility/FloatSliderPropertyDrawer.cs


using UnityEngine;
using UnityEngine.VFXToolbox;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
[CustomPropertyDrawer(typeof(FloatSliderAttribute))]
internal class FloatSliderPropertyDrawer : PropertyDrawer

4
Editor/Common/Utility/FilterPopupWindow.cs


using UnityEditor;
using UnityEngine;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal interface IProvider
{

}
public struct DisabledScope : IDisposable
internal struct DisabledScope : IDisposable
{
private static Stack<bool> s_EnabledStack = new Stack<bool>();
bool m_Disposed;

2
Editor/Common/Utility/CurveToTextureUtility.cs


using UnityEngine;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal class CurveToTextureUtility
{

2
Editor/Common/Utility/CurveDrawer.cs


using UnityEngine.VFXToolbox;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox
namespace UnityEditor.Experimental.VFX.Toolbox
{
internal class CurveDrawer
{

2
Editor/Common/PropertyAttributes/FloatSliderAttribute.cs


namespace UnityEngine.VFXToolbox
{
public class FloatSliderAttribute : PropertyAttribute
internal class FloatSliderAttribute : PropertyAttribute
{
public float m_ValueMin;
public float m_ValueMax;

175
Editor/ImageSequencer/ProcessingNode.cs


using UnityEngine;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
internal abstract class FrameProcessor
internal class ProcessingNode
{
public int OutputWidth
{

{
get {
if (Enabled)
return GetNumU();
return m_Processor.numU;
else
return InputSequence.numU;
}

get {
if (Enabled)
return GetNumV();
return m_Processor.numV;
else
return InputSequence.numV;
}

public ProcessingFrameSequence InputSequence
{
get { return m_ProcessorStack.GetInputSequence(this); }
get { return m_ProcessingNodeStack.GetInputSequence(this); }
}
public ProcessingFrameSequence OutputSequence
{

get { return m_ProcessorInfo; }
}
protected FrameProcessorStack m_ProcessorStack;
protected ProcessingFrameSequence m_OutputSequence;
private ProcessingNodeStack m_ProcessingNodeStack;
private ProcessingFrameSequence m_OutputSequence;
protected bool m_bEnabled;
private bool m_bEnabled;
protected int m_OutputWidth;
protected int m_OutputHeight;
private int m_OutputWidth;
private int m_OutputHeight;
protected ProcessorInfo m_ProcessorInfo;
public ProcessorBase processor { get { return m_Processor; } private set { m_Processor = value; m_SerializedObject = new SerializedObject(m_Processor); } }
public FrameProcessor(FrameProcessorStack processorStack, ProcessorInfo info)
private SerializedObject m_SerializedObject;
private ProcessorBase m_Processor;
private ProcessorInfo m_ProcessorInfo;
public Shader shader { get; private set; }
public Material material { get; private set; }
public bool isCurrentlyPreviewed => m_ProcessingNodeStack.imageSequencer.previewCanvas.sequence.processingNode == this;
public int previewCurrentFrame => m_ProcessingNodeStack.imageSequencer.previewCanvas.currentFrameIndex;
public int previewSequenceLength => m_ProcessingNodeStack.imageSequencer.previewCanvas.numFrames;
public ProcessingNode(ProcessingNodeStack processorStack, ProcessorInfo info)
m_ProcessorInfo.ProcessorName = GetName();
m_ProcessorStack = processorStack;
m_ProcessingNodeStack = processorStack;
processor = m_ProcessorInfo.Settings;
shader = AssetDatabase.LoadAssetAtPath<Shader>(processor.shaderPath);
material = new Material(shader) { hideFlags = HideFlags.DontSave };
material.hideFlags = HideFlags.DontSave;
processor.AttachTo(this);
}
public void SetEnabled(bool value)

info.ApplyModifiedProperties();
}
public virtual void Dispose()
public void Dispose()
Material.DestroyImmediate(material);
m_OutputSequence.Dispose();
}

Enabled = m_ProcessorInfo.Enabled;
UpdateSequenceLength();
UpdateOutputSize();
}
protected virtual void UpdateOutputSize()
{
SetOutputSize(InputSequence.width, InputSequence.height);
m_Processor.UpdateOutputSize();
UpdateOutputSize();
m_Processor.UpdateOutputSize();
UpdateOutputSize();
m_Processor.UpdateOutputSize();
public void SetOutputSize(int width, int height)
{
if(m_OutputWidth != width || m_OutputHeight != height)

}
}
protected abstract int GetNumU();
protected abstract int GetNumV();
protected int GetNumU()
{
if (InputSequence.processingNode == null)
return 1;
return InputSequence.numU;
}
protected int GetNumV()
{
if (InputSequence.processingNode == null)
return 1;
return InputSequence.numV;
}
protected bool DrawSidePanelHeader()
{
bool bHasChanged = false;

SerializedObject o = new SerializedObject(m_ProcessorInfo);
o.FindProperty("Enabled").boolValue = Enabled;
o.ApplyModifiedProperties();
m_ProcessorStack.Invalidate(this);
m_ProcessingNodeStack.Invalidate(this);
public bool OnSidePanelGUI(ImageSequence asset, int ProcessorIndex)
{
bool bHasChanged = DrawSidePanelHeader();
protected abstract bool DrawSidePanelContent(bool hasChanged);
using (new EditorGUI.DisabledScope(!Enabled))
{
m_SerializedObject.Update();
bHasChanged = m_Processor.OnInspectorGUI(bHasChanged, m_SerializedObject);
m_SerializedObject.ApplyModifiedProperties();
}
public abstract bool OnSidePanelGUI(ImageSequence asset, int ProcessorIndex);
public abstract bool OnCanvasGUI(ImageSequencerCanvas canvas);
public virtual void RequestProcessOneFrame(int currentFrame)
return bHasChanged;
}
public bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
return m_Processor.OnCanvasGUI(canvas);
}
public void RequestProcessOneFrame(int currentFrame)
{
int length = OutputSequence.length;

i %= length;
}
}
public abstract bool Process(int frame);
public virtual int GetProcessorSequenceLength()
public bool Process(int frame)
return InputSequence.length;
return m_Processor.Process(frame);
public void ExecuteShaderAndDump(int outputframe, Texture mainTex)
{
ExecuteShaderAndDump(outputframe, mainTex, material);
}
public void ExecuteShaderAndDump(int outputframe, Texture mainTex, Material material)
{
RenderTexture backup = RenderTexture.active;
Graphics.Blit(mainTex, (RenderTexture)m_OutputSequence.frames[outputframe].texture, material);
RenderTexture.active = backup;
}
public int GetProcessorSequenceLength()
{
return m_Processor.sequenceLength;
}
public void UpdateSequenceLength()
{
int currentCount = m_OutputSequence.frames.Count;

}
}
}
public virtual void Invalidate()
public void Invalidate()
FrameProcessor next = m_ProcessorStack.GetNextProcessor(this);
ProcessingNode next = m_ProcessingNodeStack.GetNextProcessor(this);
public abstract string GetName();
public virtual string GetLabel()
public string GetName()
{
return m_Processor.processorName;
}
public string GetLabel()
return GetName();
return m_Processor.label;
public abstract ProcessorSettingsBase GetSettingsAbstract();
}
internal abstract class FrameProcessor<T> : FrameProcessor where T : ProcessorSettingsBase
{
public T settings { get { return m_Settings; } private set { m_Settings = value; m_SerializedObject = new SerializedObject(m_Settings); } }
private T m_Settings;
protected SerializedObject m_SerializedObject;
public FrameProcessor(FrameProcessorStack stack, ProcessorInfo info) : base(stack, info)
public ProcessorBase GetSettingsAbstract()
m_ProcessorInfo = info;
settings = (T)m_ProcessorInfo.Settings;
return processor;
public override bool OnSidePanelGUI(ImageSequence asset, int ProcessorIndex)
{
bool bHasChanged = DrawSidePanelHeader();
using (new EditorGUI.DisabledScope(!Enabled))
{
m_SerializedObject.Update();
bHasChanged = DrawSidePanelContent(bHasChanged);
m_SerializedObject.ApplyModifiedProperties();
}
return bHasChanged;
}
public sealed override ProcessorSettingsBase GetSettingsAbstract()
{
return settings;
}
}
}

12
Editor/ImageSequencer/Serialization/ProcessorInfo.cs


using UnityEngine;
using System;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
public class ProcessorInfo : ScriptableObject
internal class ProcessorInfo : ScriptableObject
public string ProcessorName;
public ProcessorSettingsBase Settings;
public ProcessorBase Settings;
p.ProcessorName = name;
p.Settings = ScriptableObject.CreateInstance(type) as ProcessorSettingsBase;
p.Settings = ScriptableObject.CreateInstance(type) as ProcessorBase;
p.Settings.Default();
return p;
}

return ProcessorName + (Enabled ? "" : "Disabled") ;
return Settings.label + (Enabled ? "" : "Disabled") ;
}
}

9
Editor/ImageSequencer/Serialization/ImageSequence.cs


using UnityEngine;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
public class ImageSequence : ScriptableObject
internal class ImageSequence : ScriptableObject
{
public List<string> inputFrameGUIDs = new List<string>();
public List<ProcessorInfo> processorInfos = new List<ProcessorInfo>();

NormalMap = 1,
NormalMapFromGrayscale = 2,
Sprite = 3
}
private void OnEnable()
{
ImageSequencer.CleanupAsset(this);
}
}

36
Editor/ImageSequencer/ProcessingFrameSequence.cs


using UnityEngine;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal class ProcessingFrameSequence
{

{
if (m_Processor != null)
return m_Processor.NumU;
if (m_ProcessingNode != null)
return m_ProcessingNode.NumU;
else
return 1;
}

{
get
{
if (m_Processor != null)
return m_Processor.NumV;
if (m_ProcessingNode != null)
return m_ProcessingNode.NumV;
else
return 1;
}

}
}
public FrameProcessor processor
public ProcessingNode processingNode
return m_Processor;
return m_ProcessingNode;
}
}

{
if (m_Processor == null)
if (m_ProcessingNode == null)
{
if(m_Frames.Count > 0)
return m_Frames[0].texture.width;

else
{
return m_Processor.OutputWidth;
return m_ProcessingNode.OutputWidth;
}
}

{
get
{
if (m_Processor == null)
if (m_ProcessingNode == null)
{
if(m_Frames.Count > 0)
return m_Frames[0].texture.height;

else
{
return m_Processor.OutputHeight;
return m_ProcessingNode.OutputHeight;
}
}

private List<ProcessingFrame> m_Frames;
private FrameProcessor m_Processor;
private ProcessingNode m_ProcessingNode;
public ProcessingFrameSequence(FrameProcessor processor)
public ProcessingFrameSequence(ProcessingNode node)
m_Processor = processor;
m_ProcessingNode = node;
}
public void InvalidateAll()

public ProcessingFrame RequestFrame(int index)
{
if (m_Processor == null)
if (m_ProcessingNode == null)
if(m_Processor.Enabled)
if(m_ProcessingNode.Enabled)
m_Processor.UpdateSequenceLength();
m_ProcessingNode.UpdateSequenceLength();
if (m_Frames[index].dirty)
{

}
else
{
return m_Processor.InputSequence.RequestFrame(index);
return m_ProcessingNode.InputSequence.RequestFrame(index);
}
}

26
Editor/ImageSequencer/ProcessingFrame.cs


using UnityEngine;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal class ProcessingFrame
{

if (m_Texture == null)
{
if(m_Processor == null) // For input frames, either our input asset has been deleted, or something went wrong with the meta's, let's replace by a dummy
if(m_ProcessingNode == null) // For input frames, either our input asset has been deleted, or something went wrong with the meta's, let's replace by a dummy
{
m_Texture = Missing.texture;
m_Texture.name = @"/!\ MISSING /!\";

public bool isInputFrame
{
get { return m_Processor == null; }
get { return m_ProcessingNode == null; }
}
public int mipmapCount

public bool dirty;
private Texture m_Texture;
private FrameProcessor m_Processor;
private ProcessingNode m_ProcessingNode;
m_Processor = null;
m_ProcessingNode = null;
public ProcessingFrame(FrameProcessor processor)
public ProcessingFrame(ProcessingNode node)
m_Processor = processor;
m_ProcessingNode = node;
if(texture.width != m_Processor.OutputWidth || texture.height != m_Processor.OutputHeight )
if(texture.width != m_ProcessingNode.OutputWidth || texture.height != m_ProcessingNode.OutputHeight )
{
ResetTexture();
}

{
if(m_Texture == null || (m_Processor != null && (m_Texture.width != m_Processor.OutputWidth || m_Texture.height != m_Processor.OutputHeight)))
if(m_Texture == null || (m_ProcessingNode != null && (m_Texture.width != m_ProcessingNode.OutputWidth || m_Texture.height != m_ProcessingNode.OutputHeight)))
m_Texture = new RenderTexture(m_Processor.OutputWidth, m_Processor.OutputHeight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
m_Texture = new RenderTexture(m_ProcessingNode.OutputWidth, m_ProcessingNode.OutputHeight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
((RenderTexture)m_Texture).autoGenerateMips = true;
}
}

if(dirty && m_Processor != null)
if(dirty && m_ProcessingNode != null)
if(m_Processor.Process(this))
if(m_ProcessingNode.Process(this))
{
dirty = false;
return true;

{
if(s_Missing == null)
{
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.vfx-toolbox/Editor/Resources/MissingTexture.png");
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.vfx-toolbox/Editor/Common/Textures/MissingTexture.png");
if(t == null)
{
Debug.LogError("Could not find VFXToolbox Missing Texture, using white texture instead. Make sure you imported all the VFXToolbox files.");

80
Editor/ImageSequencer/ImageSequencerCanvas.cs


using System.Linq;
using System;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
internal class ImageSequencerCanvas : VFXToolboxCanvas
/// <summary>
/// A VFXToolboxCanvas component used in Image Sequencer
/// </summary>
public class ImageSequencerCanvas : VFXToolboxCanvas
public bool showExtraInfo
internal bool showExtraInfo
{
get
{

}
}
public int numFrames
internal int numFrames
{
get
{

public ProcessingFrameSequence sequence
internal ProcessingFrameSequence sequence
{
get
{

}
}
public ProcessingFrame currentFrame
internal ProcessingFrame currentFrame
{
get {
return m_ProcessingFrame;

}
}
public int currentFrameIndex
internal int currentFrameIndex
{
get
{

}
}
public bool isPlaying
internal bool isPlaying
{
get
{

private bool m_IsScrobbing;
public ImageSequencerCanvas(Rect displayRect, ImageSequencer editorWindow)
internal ImageSequencerCanvas(Rect displayRect, ImageSequencer editorWindow)
:base(displayRect)
{
m_ImageSequencerWindow = editorWindow;

}
public override void Invalidate(bool needRedraw)
internal override void Invalidate(bool needRedraw)
protected override void SetTexture(Texture tex)
/// <summary>
/// As texture display is handled by the processing stack, this function is not used.
/// </summary>
/// <param name="tex">A texture</param>
protected override sealed void SetTexture(Texture tex)
// Never should Happen
// Should Never Happen
protected override Texture GetTexture()
/// <summary>
/// Gets the current frame from the current processing node in the processing stack.
/// </summary>
/// <returns>Currently displayed texture</returns>
protected override sealed Texture GetTexture()
{
if (currentFrame != null)
{

return null;
}
protected override int GetMipMapCount()
/// <summary>
/// Returns how many mip-maps are stored in the currently displayed texture object.
/// </summary>
/// <returns>mip map count</returns>
protected override sealed int GetMipMapCount()
{
if (currentFrame != null)
return currentFrame.mipmapCount;

protected override void HandleKeyboardEvents()
/// <summary>
/// Handles all the Common VFXToolboxCanvas Keyboard events plus all required ones from Image Sequencer
/// </summary>
protected override sealed void HandleKeyboardEvents()
{
base.HandleKeyboardEvents();

}
}
protected override void DrawGrid()
/// <summary>
/// Draws the grid around the texture, and all Flipbook frame subdivisions
/// </summary>
protected override sealed void DrawGrid()
{
int GridNumU = m_PreviewSequence.numU;
int GridNumV = m_PreviewSequence.numV;

Handles.color = Color.white;
}
public void OnGUI(ImageSequencer editor)
internal void OnGUI(ImageSequencer editor)
GUI.BeginGroup(m_Rect);
if (editor.currentProcessor != null && editor.currentProcessor.Enabled && m_bShowExtraInfo && editor.sidePanelViewMode == ImageSequencer.SidePanelMode.Processors)
editor.currentProcessor.OnCanvasGUI(this);
GUI.BeginGroup(displayRect);
if (editor.currentProcessingNode != null && editor.currentProcessingNode.Enabled && m_bShowExtraInfo && editor.sidePanelViewMode == ImageSequencer.SidePanelMode.Processors)
editor.currentProcessingNode.OnCanvasGUI(this);
string procName = (editor.sidePanelViewMode == ImageSequencer.SidePanelMode.Export) ? "Export" : (sequence.processor == null ? "Input Frames" : sequence.processor.ToString());
GUI.Label(new RectOffset(24,24,24,24).Remove(m_Rect), procName , styles.largeLabel);
GUI.Label(new RectOffset(24,24,64,24).Remove(m_Rect), GetDebugInfoString() , styles.label);
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

}
public void DrawSequenceControls(Rect ViewportArea, ImageSequencer editor)
internal void DrawSequenceControls(Rect ViewportArea, ImageSequencer editor)
{
m_PlayControlsRect = new Rect(ViewportArea.x , (ViewportArea.y + ViewportArea.height), ViewportArea.width , 100);

}
}
public void UpdateCanvasSequence()
internal void UpdateCanvasSequence()
if (sequence.processor != null)
length = sequence.processor.GetProcessorSequenceLength();
if (sequence.processingNode != null)
length = sequence.processingNode.GetProcessorSequenceLength();
else
length = sequence.length;

#region STYLES
public new class Styles
internal new class Styles
{
public GUIStyle miniLabel
{

101
Editor/ImageSequencer/ImageSequencer.cs


using UnityEngine.Rendering;
using UnityEditorInternal;
using System.Collections.Generic;
using System.Linq;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
[MenuItem("Window/Visual Effects/Image Sequencer")]
[MenuItem("Window/Visual Effects/Image Sequencer", priority = 3031)]
public static void OpenEditor()
{
GetWindow(typeof(ImageSequencer));

}
}
public FrameProcessor currentProcessor
public ProcessingNode currentProcessingNode
return m_CurrentProcessor;
return m_CurrentProcessingNode;
}
}

private bool m_IgnoreInheritSettings;
private ImageSequencerCanvas m_PreviewCanvas;
private FrameProcessorStack m_processorStack;
private ProcessingNodeStack m_ProcessingNodeStack;
private FrameProcessor m_CurrentProcessor;
private FrameProcessor m_LockedPreviewProcessor;
private ProcessingNode m_CurrentProcessingNode;
private ProcessingNode m_LockedPreviewProcessor;
private GraphicsDeviceType m_CurrentGraphicsAPI;
private ColorSpace m_CurrentColorSpace;

if (m_InputFramesHashCode != hash || m_CurrentAsset.inheritSettingsReference != null)
LoadAsset(m_CurrentAsset);
m_processorStack.LoadProcessorsFromAsset(m_CurrentAsset);
m_ProcessingNodeStack.LoadProcessorsFromAsset(m_CurrentAsset);
RestoreProcessorView();
if (m_CurrentAsset.inputFrameGUIDs.Count > 0)

foreach (FrameProcessor p in m_processorStack.processors)
foreach (ProcessingNode n in m_ProcessingNodeStack.nodes)
p.Refresh();
p.Invalidate();
n.Refresh();
n.Invalidate();
}
Repaint();

m_InputFramesReorderableList = null;
m_ProcessorsReorderableList = null;
m_LockedPreviewProcessor = null;
m_CurrentProcessor = null;
m_CurrentProcessingNode = null;
if(m_processorStack != null)
m_processorStack.Dispose();
if(m_ProcessingNodeStack != null)
m_ProcessingNodeStack.Dispose();
m_processorStack = new FrameProcessorStack(new ProcessingFrameSequence(null), this);
m_ProcessingNodeStack = new ProcessingNodeStack(new ProcessingFrameSequence(null), this);
m_CurrentAssetSerializedObject = new SerializedObject(m_CurrentAsset);

VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Loading Frames", 0.333333f);
m_processorStack.LoadFramesFromAsset(m_CurrentAsset);
m_ProcessingNodeStack.LoadFramesFromAsset(m_CurrentAsset);
m_InputFramesReorderableList = new ReorderableList(m_processorStack.inputSequence.frames, typeof(Texture2D),true,false,true,true);
m_InputFramesReorderableList = new ReorderableList(m_ProcessingNodeStack.inputSequence.frames, typeof(Texture2D),true,false,true,true);
m_InputFramesReorderableList.onAddCallback = AddInputFrame;
m_InputFramesReorderableList.onRemoveCallback = RemoveInputFrame;
m_InputFramesReorderableList.onReorderCallback = ReorderInputFrame;

}
}
m_processorStack.LoadProcessorsFromAsset(inheritedSettingReference);
m_ProcessorDataProvider = new ProcessorDataProvider(m_processorStack, m_CurrentAsset);
m_ProcessingNodeStack.LoadProcessorsFromAsset(inheritedSettingReference);
m_ProcessorDataProvider = new ProcessorDataProvider(m_ProcessingNodeStack, m_CurrentAsset);
// Construct the RList
if (m_CurrentAsset.inheritSettingsReference == null)

m_ProcessorsReorderableList.onSelectCallback = MenuSelectProcessor;
}
m_PreviewCanvas.sequence = m_processorStack.inputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
if(m_PreviewCanvas.sequence.length > 0)
m_PreviewCanvas.currentFrameIndex = 0;
else

m_processorStack.InvalidateAll();
m_ProcessingNodeStack.InvalidateAll();
RestoreProcessorView();
EditorUtility.ClearProgressBar();

/// </summary>
public void DefaultView()
{
if (m_processorStack.processors.Count == 0 || m_processorStack.inputSequence.frames.Count == 0)
if (m_ProcessingNodeStack.nodes.Count == 0 || m_ProcessingNodeStack.inputSequence.frames.Count == 0)
{
m_SidePanelViewMode = SidePanelMode.InputFrames;
m_ProcessorsReorderableList.index = -1;

{
if(m_CurrentAsset.inheritSettingsReference != null && !m_IgnoreInheritSettings)
{
if(m_processorStack.processors.Count > 0)
if(m_ProcessingNodeStack.nodes.Count > 0)
m_CurrentProcessor = m_processorStack.processors[m_CurrentAsset.editSettings.selectedProcessor];
m_CurrentProcessingNode = m_ProcessingNodeStack.nodes[m_CurrentAsset.editSettings.selectedProcessor];
m_ProcessorsReorderableList.index = m_CurrentAsset.editSettings.selectedProcessor;
}
}

// index Checks
m_CurrentAsset.editSettings.lockedProcessor = Mathf.Clamp(m_CurrentAsset.editSettings.lockedProcessor, -1, m_processorStack.processors.Count - 1);
m_CurrentAsset.editSettings.selectedProcessor = Mathf.Clamp(m_CurrentAsset.editSettings.selectedProcessor, -1, m_processorStack.processors.Count - 1);
m_CurrentAsset.editSettings.lockedProcessor = Mathf.Clamp(m_CurrentAsset.editSettings.lockedProcessor, -1, m_ProcessingNodeStack.nodes.Count - 1);
m_CurrentAsset.editSettings.selectedProcessor = Mathf.Clamp(m_CurrentAsset.editSettings.selectedProcessor, -1, m_ProcessingNodeStack.nodes.Count - 1);
m_LockedPreviewProcessor = m_processorStack.processors[m_CurrentAsset.editSettings.lockedProcessor];
m_CurrentProcessor = m_processorStack.processors[m_CurrentAsset.editSettings.lockedProcessor];
m_LockedPreviewProcessor = m_ProcessingNodeStack.nodes[m_CurrentAsset.editSettings.lockedProcessor];
m_CurrentProcessingNode = m_ProcessingNodeStack.nodes[m_CurrentAsset.editSettings.lockedProcessor];
}
else
m_LockedPreviewProcessor = null;

m_ProcessorsReorderableList.index = m_CurrentAsset.editSettings.selectedProcessor;
if (m_CurrentAsset.editSettings.lockedProcessor != -1)
m_CurrentProcessor = m_processorStack.processors[m_CurrentAsset.editSettings.lockedProcessor];
m_CurrentProcessingNode = m_ProcessingNodeStack.nodes[m_CurrentAsset.editSettings.lockedProcessor];
m_CurrentProcessor = m_processorStack.processors[m_CurrentAsset.editSettings.selectedProcessor];
m_CurrentProcessingNode = m_ProcessingNodeStack.nodes[m_CurrentAsset.editSettings.selectedProcessor];
m_processorStack.InvalidateAll();
m_ProcessingNodeStack.InvalidateAll();
RefreshCanvas();
}

if(m_CurrentGraphicsAPI != device)
{
m_CurrentGraphicsAPI = device;
if(m_processorStack != null)
m_processorStack.InvalidateAll();
if(m_ProcessingNodeStack != null)
m_ProcessingNodeStack.InvalidateAll();
Repaint();
}
ColorSpace colorSpace = QualitySettings.activeColorSpace;

if(m_processorStack != null)
m_processorStack.InvalidateAll();
if(m_ProcessingNodeStack != null)
m_ProcessingNodeStack.InvalidateAll();
}
}
public static void CleanupAsset(ImageSequence asset)
{
var subAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(asset));
List<Object> toDelete = new List<Object>();
var allSettings = asset.processorInfos.Select(i => i.Settings);
foreach(var subAsset in subAssets)
{
if(subAsset is ProcessorInfo && !asset.processorInfos.Contains(subAsset))
{
toDelete.Add(subAsset);
}
else if(subAsset is ProcessorBase && !allSettings.Contains(subAsset))
{
toDelete.Add(subAsset);
}
}
foreach(var o in toDelete)
{
AssetDatabase.RemoveObjectFromAsset(o);
}
if(toDelete.Count > 0)
{
EditorUtility.SetDirty(asset);
}
}
}

6
Editor/ImageSequencer/ImageSequencer.Styles.cs


using UnityEngine;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ImageSequencer : EditorWindow
{

public GUIStyle playbackControlWindow;
public GUIContent proTitle;
public GUIContent title;
public readonly GUIContent iconPlay = EditorGUIUtility.IconContent("Animation.Play", "Play the sequence");

public Styles()
{
title = new GUIContent("Image Sequencer");
proTitle = new GUIContent("Image Sequencer", AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png"));
title = new GUIContent("Image Sequencer", AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png"));
scrollView = new GUIStyle();
scrollView.padding = new RectOffset(8, 8, 0, 0);

71
Editor/ImageSequencer/ImageSequencer.Processors.cs


using System.Linq;
using System.Reflection;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ImageSequencer : EditorWindow
{

{
if(m_ProcessorDataProvider == null)
{
m_ProcessorDataProvider = new ProcessorDataProvider(m_processorStack, m_CurrentAsset);
m_ProcessorDataProvider = new ProcessorDataProvider(m_ProcessingNodeStack, m_CurrentAsset);
}
private void MenuSelectProcessor(ReorderableList list)

if (list.count > 0 && list.index != -1)
{
SetCurrentFrameProcessor(m_processorStack.processors[list.index], false);
SetCurrentFrameProcessor(m_ProcessingNodeStack.nodes[list.index], false);
}
else
SetCurrentFrameProcessor(null, false);

{
Undo.RecordObject(m_CurrentAsset, "Reorder Processors");
m_processorStack.ReorderProcessors(m_CurrentAsset);
m_processorStack.InvalidateAll();
m_ProcessingNodeStack.ReorderProcessors(m_CurrentAsset);
m_ProcessingNodeStack.InvalidateAll();
m_CurrentAsset.editSettings.lockedProcessor = m_processorStack.processors.IndexOf(m_LockedPreviewProcessor);
m_CurrentAsset.editSettings.lockedProcessor = m_ProcessingNodeStack.nodes.IndexOf(m_LockedPreviewProcessor);
EditorUtility.SetDirty(m_CurrentAsset);
}

{
int idx = list.index;
Undo.RecordObject(m_CurrentAsset, "Remove Processor : " + m_processorStack.processors[idx].GetName());
m_processorStack.RemoveProcessor(idx,m_CurrentAsset);
Undo.RecordObject(m_CurrentAsset, "Remove Processor : " + m_ProcessingNodeStack.nodes[idx].GetName());
m_ProcessingNodeStack.RemoveProcessor(idx,m_CurrentAsset);
// If was locked, unlock beforehand
if (idx == m_CurrentAsset.editSettings.lockedProcessor)

if(m_processorStack.processors.Count > 0)
if(m_ProcessingNodeStack.nodes.Count > 0)
int newIdx = Mathf.Clamp(idx - 1, 0, m_processorStack.processors.Count - 1);
int newIdx = Mathf.Clamp(idx - 1, 0, m_ProcessingNodeStack.nodes.Count - 1);
SetCurrentFrameProcessor(m_processorStack.processors[newIdx], false);
SetCurrentFrameProcessor(m_ProcessingNodeStack.nodes[newIdx], false);
list.index = newIdx;
}
else

}
previewCanvas.currentFrameIndex = 0;
m_processorStack.InvalidateAll();
m_ProcessingNodeStack.InvalidateAll();
if(m_CurrentProcessor != null)
previewCanvas.sequence = m_CurrentProcessor.OutputSequence;
if(m_CurrentProcessingNode != null)
previewCanvas.sequence = m_CurrentProcessingNode.OutputSequence;
previewCanvas.sequence = m_processorStack.inputSequence;
previewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
previewCanvas.currentFrameIndex = Mathf.Clamp(previewCanvas.currentFrameIndex, 0, previewCanvas.sequence.length - 1);

public void SetCurrentFrameProcessor(FrameProcessor processor, bool wantLock)
public void SetCurrentFrameProcessor(ProcessingNode node, bool wantLock)
m_LockedPreviewProcessor = processor;
if(processor != null)
m_LockedPreviewProcessor = node;
if(node != null)
m_CurrentProcessor = processor;
m_CurrentAsset.editSettings.lockedProcessor = m_processorStack.processors.IndexOf(processor);
m_CurrentProcessingNode = node;
m_CurrentAsset.editSettings.lockedProcessor = m_ProcessingNodeStack.nodes.IndexOf(node);
m_CurrentProcessor = m_processorStack.processors[Mathf.Min(m_ProcessorsReorderableList.index, m_processorStack.processors.Count-1)];
m_CurrentProcessingNode = m_ProcessingNodeStack.nodes[Mathf.Min(m_ProcessorsReorderableList.index, m_ProcessingNodeStack.nodes.Count-1)];
bool needChange = (m_CurrentProcessor != processor);
bool needChange = (m_CurrentProcessingNode != node);
m_CurrentProcessor = processor;
m_CurrentProcessingNode = node;
m_CurrentProcessor = m_LockedPreviewProcessor;
m_CurrentProcessingNode = m_LockedPreviewProcessor;
m_CurrentAsset.editSettings.selectedProcessor = m_processorStack.processors.IndexOf(processor);
m_CurrentAsset.editSettings.selectedProcessor = m_ProcessingNodeStack.nodes.IndexOf(node);
RefreshCanvas();
EditorUtility.SetDirty(m_CurrentAsset);
}

using (new EditorGUI.DisabledScope(true))
{
GUI.Toggle(toggle_rect, m_processorStack.processors[index].Enabled, "");
GUI.Toggle(toggle_rect, m_ProcessingNodeStack.nodes[index].Enabled, "");
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_processorStack.processors[index].ToString()), VFXToolboxStyles.RListLabel);
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_ProcessingNodeStack.nodes[index].ToString()), VFXToolboxStyles.RListLabel);
}

Rect view_rect = new Rect(rect.x + rect.width - 37, rect.y+2, 16, 16);
Rect lock_rect = new Rect(rect.x + rect.width - 16, rect.y+2, 16, 14);
bool enabled = GUI.Toggle(toggle_rect, m_processorStack.processors[index].Enabled,"");
if(enabled != m_processorStack.processors[index].Enabled)
bool enabled = GUI.Toggle(toggle_rect, m_ProcessingNodeStack.nodes[index].Enabled,"");
if(enabled != m_ProcessingNodeStack.nodes[index].Enabled)
m_processorStack.processors[index].Enabled = enabled;
m_processorStack.processors[index].Invalidate();
m_ProcessingNodeStack.nodes[index].Enabled = enabled;
m_ProcessingNodeStack.nodes[index].Invalidate();
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_processorStack.processors[index].ToString()), VFXToolboxStyles.RListLabel);
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_ProcessingNodeStack.nodes[index].ToString()), VFXToolboxStyles.RListLabel);
if((m_LockedPreviewProcessor == null && isActive) || m_processorStack.processors.IndexOf(m_LockedPreviewProcessor) == index)
if((m_LockedPreviewProcessor == null && isActive) || m_ProcessingNodeStack.nodes.IndexOf(m_LockedPreviewProcessor) == index)
bool locked = (m_LockedPreviewProcessor != null) && index == m_processorStack.processors.IndexOf(m_LockedPreviewProcessor);
bool locked = (m_LockedPreviewProcessor != null) && index == m_ProcessingNodeStack.nodes.IndexOf(m_LockedPreviewProcessor);
if(isActive || locked)
{

if(b)
SetCurrentFrameProcessor(m_processorStack.processors[index],true);
SetCurrentFrameProcessor(m_ProcessingNodeStack.nodes[index],true);
else
SetCurrentFrameProcessor(null, true);
}

28
Editor/ImageSequencer/ImageSequencer.InputFrames.cs


using UnityEditorInternal;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ImageSequencer : EditorWindow
{

foreach (string s in names)
{
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(s);
if(t != null) m_processorStack.inputSequence.frames.Add(new ProcessingFrame(t));
if(t != null) m_ProcessingNodeStack.inputSequence.frames.Add(new ProcessingFrame(t));
m_processorStack.InvalidateAll();
m_ProcessingNodeStack.InvalidateAll();
m_processorStack.SyncFramesToAsset(m_CurrentAsset);
m_ProcessingNodeStack.SyncFramesToAsset(m_CurrentAsset);
UpdateInputTexturesHash();
}
}

{
Undo.RecordObject(m_CurrentAsset, "Reorder Input Frames");
UpdateViewport();
m_processorStack.SyncFramesToAsset(m_CurrentAsset);
m_ProcessingNodeStack.SyncFramesToAsset(m_CurrentAsset);
UpdateInputTexturesHash();
}

}
}
Undo.RecordObject(m_CurrentAsset, "Remove Input Frames");
m_processorStack.SyncFramesToAsset(m_CurrentAsset);
m_ProcessingNodeStack.SyncFramesToAsset(m_CurrentAsset);
if(m_processorStack.inputSequence.length > 0)
m_processorStack.InvalidateAll();
if(m_ProcessingNodeStack.inputSequence.length > 0)
m_ProcessingNodeStack.InvalidateAll();
}
public void DrawInputFrameRListElement(Rect rect, int index, bool isActive, bool isFocused)

{
Undo.RecordObject(m_CurrentAsset, "Clear All Input Frames");
// Remove frames and update hash
m_processorStack.RemoveAllInputFrames(m_CurrentAsset);
m_processorStack.SyncFramesToAsset(m_CurrentAsset);
m_ProcessingNodeStack.RemoveAllInputFrames(m_CurrentAsset);
m_ProcessingNodeStack.SyncFramesToAsset(m_CurrentAsset);
m_CurrentProcessor = null;
m_CurrentProcessingNode = null;
m_PreviewCanvas.sequence = m_processorStack.inputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
// Request an update
Invalidate();
RefreshCanvas();

{
Undo.RecordObject(m_CurrentAsset, "Sort All Input Frames");
// Sort frames and update hash
m_processorStack.SortAllInputFrames(m_CurrentAsset);
m_ProcessingNodeStack.SortAllInputFrames(m_CurrentAsset);
m_InputFramesHashCode = GetInputTexturesHashCode();
LoadAsset(m_CurrentAsset);

{
Undo.RecordObject(m_CurrentAsset, "Reverse Input Frames Order");
// Inverse frame order and update hash
m_processorStack.ReverseAllInputFrames(m_CurrentAsset);
m_ProcessingNodeStack.ReverseAllInputFrames(m_CurrentAsset);
m_InputFramesHashCode = GetInputTexturesHashCode();
LoadAsset(m_CurrentAsset);

55
Editor/ImageSequencer/ImageSequencer.GUI.cs


using UnityEditorInternal;
using System.Collections.Generic;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ImageSequencer : EditorWindow
{

public void OnGUI()
{
titleContent = styles.title;
if(EditorGUIUtility.isProSkin)
titleContent = styles.proTitle;
else
titleContent = styles.title;
m_MinimumSize = new Vector2(880, 320);
InitializeGUI();

Invalidate();
m_NeedRedraw = false;
}
else if((m_AutoCook && m_CurrentProcessor != null))
else if((m_AutoCook && m_CurrentProcessingNode != null))
m_CurrentProcessor.RequestProcessOneFrame(previewCanvas.currentFrameIndex);
m_CurrentProcessingNode.RequestProcessOneFrame(previewCanvas.currentFrameIndex);
Invalidate();
}
}

private void DrawTabbedPanelSelector()
{
SidePanelMode prevMode = m_SidePanelViewMode;
bool hasInputFrames = m_processorStack.inputSequence.frames.Count > 0;
bool hasInputFrames = m_ProcessingNodeStack.inputSequence.frames.Count > 0;
SidePanelMode newMode = (SidePanelMode)VFXToolboxGUIUtility.TabbedButtonsGUILayout(
(int)prevMode,
new string[] { "Input Frames", "Processors", "Export"},

{
case SidePanelMode.InputFrames:
m_PreviewCanvas.sequence = m_processorStack.inputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
break;

m_PreviewCanvas.sequence = m_LockedPreviewProcessor.OutputSequence;
else
{
if(m_CurrentProcessor != null)
m_PreviewCanvas.sequence = m_CurrentProcessor.OutputSequence;
if(m_CurrentProcessingNode != null)
m_PreviewCanvas.sequence = m_CurrentProcessingNode.OutputSequence;
if (m_processorStack.processors.Count > 0)
m_PreviewCanvas.sequence = m_processorStack.processors[m_processorStack.processors.Count - 1].OutputSequence;
if (m_ProcessingNodeStack.nodes.Count > 0)
m_PreviewCanvas.sequence = m_ProcessingNodeStack.nodes[m_ProcessingNodeStack.nodes.Count - 1].OutputSequence;
m_PreviewCanvas.sequence = m_processorStack.inputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
}
}

m_PreviewCanvas.sequence = m_processorStack.outputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.outputSequence;
break;
}

GUILayout.Space(8);
m_InputFramesReorderableList.DoLayoutList();
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_processorStack.inputSequence.length > 0)
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_ProcessingNodeStack.inputSequence.length > 0)
{
RemoveInputFrame(m_InputFramesReorderableList);
Event.current.Use();

{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.Label(VFXToolboxGUIUtility.Get("Frame Processor Stack"),EditorStyles.boldLabel,GUILayout.Width(180));
GUILayout.Label(VFXToolboxGUIUtility.Get("Frame Processors"),EditorStyles.boldLabel,GUILayout.Width(180));
m_processorStack.RemoveAllProcessors(m_CurrentAsset);
m_ProcessingNodeStack.RemoveAllProcessors(m_CurrentAsset);
m_CurrentProcessor = null;
m_CurrentProcessingNode = null;
m_PreviewCanvas.sequence = m_processorStack.inputSequence;
m_PreviewCanvas.sequence = m_ProcessingNodeStack.inputSequence;
EditorUtility.SetDirty(m_CurrentAsset);
// Request Repaint
Invalidate();

GUILayout.Space(10);
// Draw inspector and Invalidates whatever needs to.
for(int i = 0; i < m_processorStack.processors.Count; i++)
for(int i = 0; i < m_ProcessingNodeStack.nodes.Count; i++)
bool changed = m_processorStack.processors[i].OnSidePanelGUI(m_CurrentAsset,i);
bool changed = m_ProcessingNodeStack.nodes[i].OnSidePanelGUI(m_CurrentAsset,i);
m_processorStack.processors[i].Invalidate();
m_ProcessingNodeStack.nodes[i].Invalidate();
UpdateViewport();
}
m_Dirty = m_Dirty || changed;

// Handle final keyboard events (delete)
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_processorStack.processors.Count > 0)
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_ProcessingNodeStack.nodes.Count > 0)
{
MenuRemoveProcessor(m_ProcessorsReorderableList);
Event.current.Use();

private void DrawExportPanelContent()
{
int length = m_processorStack.outputSequence.length;
int length = m_ProcessingNodeStack.outputSequence.length;
if(length > 0)
{

m_CurrentAsset.exportSettings.dataContents = (ImageSequence.DataContents)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Import as|Sets the importer mode"), m_CurrentAsset.exportSettings.dataContents);
if(m_CurrentAsset.exportSettings.dataContents == ImageSequence.DataContents.Sprite)
{
FrameProcessor p = m_processorStack.processors[m_processorStack.processors.Count - 1];
if (((float)p.OutputWidth % p.NumU) != 0 || ((float)p.OutputHeight % p.NumV) != 0)
EditorGUILayout.HelpBox("Warning : texture size is not a multiplier of rows ("+p.NumU+") and columns ("+p.NumV+") count, this will lead to incorrect rendering of the sprite animation", MessageType.Warning);
ProcessingNode n = m_ProcessingNodeStack.nodes[m_ProcessingNodeStack.nodes.Count - 1];
if (((float)n.OutputWidth % n.NumU) != 0 || ((float)n.OutputHeight % n.NumV) != 0)
EditorGUILayout.HelpBox("Warning : texture size is not a multiplier of rows ("+n.NumU+") and columns ("+n.NumV+") count, this will lead to incorrect rendering of the sprite animation", MessageType.Warning);
}
switch(m_CurrentAsset.exportSettings.dataContents)

if (fileName != "")
{
m_CurrentAsset.exportSettings.fileName = fileName;
m_CurrentAsset.exportSettings.frameCount = (ushort)m_processorStack.outputSequence.frames.Count;
m_CurrentAsset.exportSettings.frameCount = (ushort)m_ProcessingNodeStack.outputSequence.frames.Count;
}
}
// Export Again

23
Editor/ImageSequencer/ImageSequencer.Export.cs


using UnityEngine;
using System.IO;
using VFXToolbox.MiniTGA;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ImageSequencer : EditorWindow
{

string title = "Save Texture, use # for frame numbering.";
string defaultFileName, extension;
int count = m_processorStack.outputSequence.frames.Count;
int numU = m_processorStack.outputSequence.numU;
int numV = m_processorStack.outputSequence.numV;
int count = m_ProcessingNodeStack.outputSequence.frames.Count;
int numU = m_ProcessingNodeStack.outputSequence.numU;
int numV = m_ProcessingNodeStack.outputSequence.numV;
string defaultDir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_CurrentAsset));

Debug.LogWarning("VFX Toolbox Warning : Saving a texture outside the project's scope. Import Settings will not be applied");
}
int frameCount = m_processorStack.outputSequence.length;
int frameCount = m_ProcessingNodeStack.outputSequence.length;
if(frameCount > 1 && !Path.GetFileNameWithoutExtension(path).Contains("#"))
{

try
{
int i = 1;
foreach (ProcessingFrame frame in m_processorStack.outputSequence.frames)
foreach (ProcessingFrame frame in m_ProcessingNodeStack.outputSequence.frames)
{
if(VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Exporting Frame #" + i + "/" + frameCount, (float)i / frameCount, 0, true))
{

break;
case ImageSequence.ExportMode.Targa:
{
bytes = MiniTGA.MiniTGA.MiniTGAWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs);
bytes = MiniTGA.MiniTGAWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs);
}
break;
case ImageSequence.ExportMode.PNG:

case ImageSequence.DataContents.Sprite:
importer.textureType = TextureImporterType.Sprite;
importer.spriteImportMode = SpriteImportMode.Multiple;
importer.spritesheet = GetSpriteMetaData(frame, m_processorStack.outputSequence.numU, m_processorStack.outputSequence.numV );
importer.spritesheet = GetSpriteMetaData(frame, m_ProcessingNodeStack.outputSequence.numU, m_ProcessingNodeStack.outputSequence.numV );
break;
}
importer.mipmapEnabled = m_CurrentAsset.exportSettings.generateMipMaps;

float a = inputs[k].a;
inputs[k] = new Color(a, a, a, a);
}
MiniTGA.MiniTGA.MiniTGAWrite(alphaFilename,(ushort)frame.texture.width, (ushort)frame.texture.height, false, inputs);
MiniTGA.MiniTGAWrite(alphaFilename,(ushort)frame.texture.width, (ushort)frame.texture.height, false, inputs);
AssetDatabase.Refresh();

{
alphaImporter.textureType = TextureImporterType.Sprite;
alphaImporter.spriteImportMode = SpriteImportMode.Multiple;
alphaImporter.spritesheet = GetSpriteMetaData(frame, m_processorStack.outputSequence.numU, m_processorStack.outputSequence.numV);
alphaImporter.spritesheet = GetSpriteMetaData(frame, m_ProcessingNodeStack.outputSequence.numU, m_ProcessingNodeStack.outputSequence.numV);
alphaImporter.alphaSource = TextureImporterAlphaSource.None;
}
else

private void UpdateExportedAssets()
{
if (ExportToFile(true) != "")
m_CurrentAsset.exportSettings.frameCount = (ushort)m_processorStack.outputSequence.frames.Count;
m_CurrentAsset.exportSettings.frameCount = (ushort)m_ProcessingNodeStack.outputSequence.frames.Count;
else
m_CurrentAsset.exportSettings.frameCount = 0;
}

4
Editor/ImageSequencer/ImageSequenceAssetFactory.cs


using UnityEditor.ProjectWindowCallback;
using System.IO;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
[MenuItem("Assets/Create/Visual Effects/Image Sequence", priority = 301)]
[MenuItem("Assets/Create/Visual Effects/Image Sequence", priority = 321)]
private static void MenuCreatePostProcessingProfile()
{
var icon = EditorGUIUtility.FindTexture("ImageSequence Icon");

4
Editor/ImageSequencer/ImageSequenceAssetEditor.cs


using UnityEngine;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[CustomEditor(typeof(ImageSequence))]
internal class ImageSequenceAssetEditor : Editor

for(int i = 0; i < processorsCount; i++)
{
var item = processors.GetArrayElementAtIndex(i).objectReferenceValue as ProcessorInfo;
EditorGUILayout.LabelField("#"+i+" - " + item.ProcessorName + (item.Enabled?"":" (Disabled)"));
EditorGUILayout.LabelField("#"+i+" - " + item.Settings.label + (item.Enabled?"":" (Disabled)"));
}
EditorGUI.indentLevel--;
}

25
Editor/ImageSequencer/FilterPopup/ProcessorDataProvider.cs


using UnityEngine;
using UnityEditor;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
private Dictionary<Type, ProcessorAttribute> m_dataSource;
private FrameProcessorStack m_processorStack;
private Dictionary<Type, ProcessorAttribute> m_DataSource;
private ProcessingNodeStack m_ProcessingNodeStack;
private ImageSequence m_CurrentAsset;
public class ProcessorElement : FilterPopupWindow.Element

}
}
internal ProcessorDataProvider(FrameProcessorStack stack, ImageSequence asset)
internal ProcessorDataProvider(ProcessingNodeStack stack, ImageSequence asset)
m_dataSource = stack.settingsDefinitions;
m_processorStack = stack;
m_DataSource = stack.settingsDefinitions;
m_ProcessingNodeStack = stack;
m_CurrentAsset = asset;
}

var processors = m_dataSource.ToList();
var processors = m_DataSource.ToList();
processors.Sort((processorA, processorB) => {
int res = processorA.Value.category.CompareTo(processorB.Value.category);
return res != 0 ? res : processorA.Value.name.CompareTo(processorB.Value.name);

// Add Element
Undo.RecordObject(m_CurrentAsset, "Add Processor");
FrameProcessor processor = null;
ProcessingNode processor = null;
ProcessorAttribute attribute = m_processorStack.settingsDefinitions[settingType];
Type processorType = attribute.processorType;
ProcessorAttribute attribute = m_ProcessingNodeStack.settingsDefinitions[settingType];
processor = (FrameProcessor)Activator.CreateInstance(processorType, m_processorStack, info);
processor = (ProcessingNode)Activator.CreateInstance(typeof(ProcessingNode), m_ProcessingNodeStack, info);
m_processorStack.AddProcessor(processor, m_CurrentAsset);
m_processorStack.InvalidateAll();
m_ProcessingNodeStack.AddProcessor(processor, m_CurrentAsset);
m_ProcessingNodeStack.InvalidateAll();
}
}

6
Editor/ImageSequencer/Exporters/MiniTGA.cs


using System;
using UnityEngine;
using UnityEditor.VFXToolbox;
using UnityEditor.Experimental.VFX.Toolbox;
namespace MiniTGA
namespace VFXToolbox.MiniTGA
public static class MiniTGA
internal static class MiniTGA
{
// Writes TGA into a memory buffer.
// Input:

8
Editor/ImageSequencer/Exporters/MiniEXR.cs


using UnityEngine;
using UnityEditor.VFXToolbox;
using UnityEditor.Experimental.VFX.Toolbox;
// MiniEXR 2013 by Aras Pranckevicius / Unity Technologies.
//

// Writes OpenEXR RGB files out of half-precision RGBA or RGB data.
//
namespace MiniEXR {
namespace VFXToolbox.MiniEXR {
//Based on source-forge project: http://sourceforge.net/projects/csharp-half/
internal static class HalfHelper
{

return result;
}
}
public static class MiniEXR {
internal static class MiniEXR {
// Writes EXR into a memory buffer.
// Input:

22
Editor/ImageSequencer/Attributes/ProcessorAttribute.cs


using UnityEngine;
using System;
namespace UnityEditor.VFXToolbox.ImageSequencer
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
/// <summary>
/// Attribute for Class derived from ProcessorBase.
/// Determines the ImageSequencer menu name and category for the processor when adding new processors to the asset.
/// </summary>
/// <summary>
/// Menu Category where the Processor will be stored into
/// </summary>
/// <summary>
/// Processor name used to display in the menu
/// </summary>
public readonly Type processorType;
public ProcessorAttribute(string category, string name, Type processorType)
/// <summary>
/// Defines a Processor Entry in the ImageSequencer add Processor Menu.
/// </summary>
/// <param name="category">Menu Category where the Processor will be stored into</param>
/// <param name="name">Processor name used to display in the menu</param>
public ProcessorAttribute(string category, string name)
this.processorType = processorType;
}
}
}

9
CHANGELOG.md


# Changelog
All notable changes to this package are documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.0-preview] - 2020-01-07
* Initial Version

7
CHANGELOG.md.meta


fileFormatVersion: 2
guid: 9ad57f86e7dfbfa4a95870ba31a6686a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Editor/Common.meta


fileFormatVersion: 2
guid: 3dfcf7e3db1c87140891088eaecec382
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

4
Documentation~/TableOfContents.md


* [VFX Toolbox](Index.md)
* [Image Sequencer](ImageSequencer.md)
* [DCC Tools](DCCTools.md)

12
Documentation~/index.md


# VFX Toolbox
VFX Toolbox is a set of unity tools, and DCC scripts that enables Visual Effect Artists to improve their workflow and interaction with Unity.
<u>Unity Tools:</u>
* [Image Sequencer](ImageSequencer.md) : A Workflow tool that enables making optimized flipbook and texture sheets from sequences of images.
<u>DCC Tools :</u>
* [Unity VFX Tools for Houdini](DCCTools.md) : A set of Houdini nodes that enable exporting data to Unity (for use with Visual Effect Graph).

15
Documentation~/DCCTools.md


# DCC Tools
VFX Toolbox comes bundled with a set of DCC Tools that help export data for unity.
## Houdini DCC Tools for Visual Effect Graph
Houdini DCC Tools are located inside the `DCC-Tools~/Houdini` folder of the VFX Toolbox Package. This folder is not visible in editor but it can be browsed by clicking the Packages/VFX Toolbox Folder in the Project View, then selecting Show in Explorer from the Context Menu.
The Houdini Folder contains the following files:
* **Examples** (Folder) : Contains a set of houdini example files (.hipnc)
* **Unity_VFX_Tools.hda** : The digital Asset file containing the Unity VFX Toolbox Digital assets for Houdini.
<u>Compatibility:</u> Houdini 16.5 and newer.

22
Documentation~/images/ImageSequenceAssets.png

之前 之后
宽度: 652  |  高度: 202  |  大小: 8.1 KiB

571
Documentation~/images/ImageSequenceInspector.png

之前 之后
宽度: 466  |  高度: 1028  |  大小: 137 KiB

86
Documentation~/images/ImageSequencer-AddProcessorMenu.png

之前 之后
宽度: 240  |  高度: 320  |  大小: 5.7 KiB

21
Documentation~/images/ImageSequencer-AlphaFromRGB.png

之前 之后
宽度: 349  |  高度: 105  |  大小: 6.9 KiB

49
Documentation~/images/ImageSequencer-AssembleFlipbook.png

之前 之后
宽度: 353  |  高度: 178  |  大小: 12 KiB

14
Documentation~/images/ImageSequencer-BreakFlipbook.png

之前 之后
宽度: 355  |  高度: 87  |  大小: 3.4 KiB

10
Documentation~/images/ImageSequencer-ClearProcessors.png

之前 之后
宽度: 350  |  高度: 32  |  大小: 1.8 KiB

76
Documentation~/images/ImageSequencer-ColorCorrection.png

之前 之后
宽度: 352  |  高度: 350  |  大小: 13 KiB

120
Documentation~/images/ImageSequencer-Crop.png

之前 之后
宽度: 355  |  高度: 409  |  大小: 10 KiB

86
Documentation~/images/ImageSequencer-CustomMaterial.png

之前 之后
宽度: 352  |  高度: 219  |  大小: 14 KiB

19
Documentation~/images/ImageSequencer-Decimate.png

之前 之后
宽度: 355  |  高度: 67  |  大小: 2.4 KiB

58
Documentation~/images/ImageSequencer-Export.png

之前 之后
宽度: 353  |  高度: 384  |  大小: 18 KiB

56
Documentation~/images/ImageSequencer-Fade.png

之前 之后
宽度: 355  |  高度: 312  |  大小: 8.8 KiB

25
Documentation~/images/ImageSequencer-FixBorders.png

之前 之后
宽度: 353  |  高度: 206  |  大小: 7.1 KiB

49
Documentation~/images/ImageSequencer-FrameProcessorList.png

之前 之后
宽度: 355  |  高度: 210  |  大小: 9.4 KiB

9
Documentation~/images/ImageSequencer-InheritProcessors.png

之前 之后
宽度: 349  |  高度: 27  |  大小: 2.8 KiB

42
Documentation~/images/ImageSequencer-InputFrames.png

之前 之后
宽度: 351  |  高度: 299  |  大小: 8.6 KiB

34
Documentation~/images/ImageSequencer-Looping.png

之前 之后
宽度: 354  |  高度: 325  |  大小: 10 KiB

66
Documentation~/images/ImageSequencer-OptionsPopup.png

之前 之后
宽度: 200  |  高度: 321  |  大小: 15 KiB

6
Documentation~/images/ImageSequencer-PremultiplyAlpha.png

之前 之后
宽度: 353  |  高度: 91  |  大小: 3.7 KiB

19
Documentation~/images/ImageSequencer-ProcessorPreview.png

之前 之后
宽度: 354  |  高度: 53  |  大小: 3.1 KiB

18
Documentation~/images/ImageSequencer-ProcessorPreviewLock.png

之前 之后
宽度: 359  |  高度: 50  |  大小: 2.7 KiB

131
Documentation~/images/ImageSequencer-Processors.png

之前 之后
宽度: 352  |  高度: 406  |  大小: 19 KiB

76
Documentation~/images/ImageSequencer-RemapColor.png

之前 之后
宽度: 355  |  高度: 88  |  大小: 6.9 KiB

89
Documentation~/images/ImageSequencer-RemoveBackground.png

之前 之后
宽度: 348  |  高度: 160  |  大小: 10 KiB

53
Documentation~/images/ImageSequencer-Resize.png

之前 之后
宽度: 456  |  高度: 307  |  大小: 7.2 KiB

94
Documentation~/images/ImageSequencer-Retime.png

之前 之后
宽度: 352  |  高度: 327  |  大小: 12 KiB

6
Documentation~/images/ImageSequencer-Rotate.png

之前 之后
宽度: 351  |  高度: 160  |  大小: 4.9 KiB

10
Documentation~/images/ImageSequencer-Tabs.png

之前 之后
宽度: 351  |  高度: 33  |  大小: 2.0 KiB

18
Documentation~/images/ImageSequencer-Toolbar.png

之前 之后
宽度: 529  |  高度: 22  |  大小: 3.1 KiB

10
Documentation~/images/ImageSequencer-UpdateButton.png

之前 之后
宽度: 95  |  高度: 42  |  大小: 1.2 KiB

940
Documentation~/images/ImageSequencer.png
文件差异内容过多而无法显示
查看文件

11
Documentation~/images/ImageSequencerNoAsset.png

之前 之后
宽度: 882  |  高度: 400  |  大小: 9.6 KiB

649
Documentation~/images/ImageSequencerWindow.png
文件差异内容过多而无法显示
查看文件

317
Documentation~/images/ImageSequencerWorkflow.png

之前 之后
宽度: 624  |  高度: 207  |  大小: 77 KiB

854
Documentation~/ImageSequencer.md


# Image Sequencer
![Screenshot of Image Sequencer](images/ImageSequencer.png)
## Introduction
The VFX Image Sequencer is a tool which provide Visual Effect Artists with the means to process sequences of images, and generate texture assets such as flipbooks texture sheets.
![Image Sequencer Workflow](Images/ImageSequencerWorkflow.png)
To edit sequences of images, the tool relies on **Image Sequence assets**. These template assets contain references to project imported textures, a set of processing settings, and an export configuration. Every asset can be considered as a “Project File” in order to generate one output texture file or a sequence of these.
## Quick Start Guide
Image Sequence Assets can be created from the Project Window Create Menu under the Category VFX Toolbox. Each asset contains configuration to read a sequence of images and output one or a sequence of textures.
These assets can be considered as a “Project Template” for producing flipbook texture sheets, so you can retain the settings and iterate on them for future updates.
### Creating Image Sequences
Image Sequences are assets that can be created using the main Menu : **Assets/Create/Visual Effects/Image Sequence**. Every Image Sequence asset contains a "Project Template" to create a Texture Sheet.
### Opening Image Sequences
The Image sequencer window is the main editor of Image Sequence assets. You can open this window :
* By clicking the **Window/Visual Effects/Image Sequencer** menu item
* By clicking the **Edit Sequence** button
* By double-clicking an Image Sequence asset in the Project Window.
While the Image Sequencer window is open, selecting another image sequence in the project window will load it in the Image Sequencer window.
### Editing Image Sequences
Once created, the Image Sequence can be edited in the Image Sequencer using the following actions:
1. Adding Input Frames
2. Add and Edit Processors to alter the source image sequence.
3. Export the Output of the Processors as one or many Images
In order to switch between these actions, you can use the 3 tabs located at the top of the left inspector pane:
![](images/ImageSequencer-Tabs.png)
If no images are not imported, the Processors and Export tabs are disabled and cannot be clicked.
#### Adding Input Frames
![](images/ImageSequencer-InputFrames.png)
You can import images in the sequence by clicking the **Input Frames** tab Button to enter Input Frames editing mode then by dragging them directly from the Project Window to the Image Sequencer window, or by dragging a folder that contains images from the Project Window to the Image Sequencer Window
> For more information, see the Input Frames Workflow section of this document.
>
#### Adding and Editing Processors
![](images/ImageSequencer-Processors.png)
After adding Input Frames, by clicking the Processors Tab button, you can enter the Processors editing Mode. In this view you can add processors in the reorderable list, edit them and iterate to generate texture sheets.
In this mode you will be able to perform operations on textures such as Assembling and Disassembling Texture Sheets, Adjusting Timing of a Sequence, Make an image Sequence looping, Crop images to save empty space, Fix borders, adjust Colors, and many more actions.
> For more information, see the Processors Workflow section of this document.
>
#### Exporting Images
![](images/ImageSequencer-Export.png)
After Adding Processors, when you have a result that suits you, you can click the third Tab Button "Export" to enter Export Mode. In this mode you can configure the texture generation options and also the texture import options.
In this view you will be able to set up your output textures either as color, masks or normal maps and ensure they are exported correctly.
> For more information, see the Export Workflow section of this document.
>
## Image Sequence Inspector
![ImageSequence Inspector](images/ImageSequenceInspector.png)
Upon selecting an Image Sequence Asset, the inspector displays an overview of this asset, with its input frames, processors overview, and optionally the Exported file (if any). This is only a read-only overview of the processing information of this sequence.
- To edit this sequence, click the **Edit Sequence** button, this will open the Image Sequencer Editor Window.
- You can preview input sequence of images by clicking the preview button.
## Image Sequencer Window
![ImageSequencer Window](Images/ImageSequencerWindow.png)
The Image Sequence editor window comes with two zones:
* The **Toolbar** at the top provides controls to access the currently edited Image sequence, and display controls for the Canvas.
* The **Inspector** (left pane) displays contextual controls for adding input frames, configure processing, and export to other textures.
* The **Canvas** (right pane) offers preview of the processed sequence at different stages (input sequence, processors, exported texture). It contains play controls at the bottom, view options in the top toolbar, and handles panning and zooming using mouse.
### Inspector
The inspector lies in the left part of the window and is the part of the window where you will make changes to the Image Sequence. At the top of the inspector lies a 3-Button tab in order to switch between the editing modes : Input Frames, Processors and Export.
![](images/ImageSequencer-Tabs.png)
### Canvas
The canvas is in the right side of the window and is used to preview the changes made in the Image Sequencer Inspector. It displays contextually either the Input Frames, the output of a processor, or the exported texture.
Navigation in the canvas is made by using the mouse or trackpad.
Some keyboard shortcuts enable access to same features accessible with the mouse:
* Alt+Left Mouse Drag : Pan the viewport
* Middle Mouse Drag : Pan the viewport
* Alt+Right Mouse Drag : Zoom the viewport
* Mouse Wheel : Zoom the viewport
Some keyboard shortcuts enable focusing on the image:
* F focuses the image, fitting the zoom to fill the viewport
* Shift + F focuses the image, with a 100% zoom
(Note that these controls are available in the options popup menu)
### Sequence Playback Controls
Playback controls are visible when your sequence contains 2 or more images. They are available as a bottom panel.
![img](https://lh6.googleusercontent.com/VHDcK1K4X-3kyH9cGkwlJFPdK-fS3mxZVco5IsmEmwsSKp4HGWsJzpsgeduzOHTacxFTGzvASWeKeXlWZxGl5S_pGTxpC7E1rFnxAoTJQ4yuNEPX8VjjiuW4hH7MNakvwC1mKUR9)
The sequence playback panel contains a timeline track showing the current progress of the timeline.
Every frame is displayed in the track as a colored cell:
* Bright blue cell displays the currently selected (displayed) frame in the canvas.
* Blue darker cells tells that these frames have already been rendered.
* Gray cells mean frames have not already been rendered
> (You can enable automatic rendering by activating the AutoCook Option in the options menu)
Here is an overview of the Toolbar located under the sequence trackbar
* **Playback Buttons**
* First Frame
* Previous Frame
* Play/Pause
* Next Frame
* Last Frame
* **Image & Sequence information**
* Frame N on Total Count
* TCR time (based on frame rate)
* **Speed and Framerate Control**
* Number of frames per second
* Popup to set a given play rate
### Viewport Toolbar
The viewport toolbar contains controls for toggle display of various options in the viewport.
![Toolbar](images/ImageSequencer-Toolbar.png)
The toolbar contains the following elements:
* **Options Popup Menu**: Opens the Option popup menu to configure the Canvas display options.
* **RGBA toggles** : Use these buttons to filter the Red, Green, Blue or Alpha channel display.
* **MipMap Selector** : Using this slider, you can preview the different mip-maps of the currently displayed image.
* **Background brightness**: Using this slider, you can adjust the background checkerboard brightness.
#### Options popup menu
![img](images/ImageSequencer-OptionsPopup.png)
The Options popup menu is accessed via the toolbar button on the right.It contains toggles & buttons for visibility, and commands for the view.
* Viewport Options:
* Grid Outline (Toggle) : Toggles Outline around Sub Image Cells and around texture.
* Frame Processor Overlays (Toggle) : Toggle Processor Overlay in the canvas to display visually its changes.
* Texture Filtering (Toggle) : Toggles Texture Filtering in the Canvas
### Update Button
![](images/ImageSequencer-UpdateButton.png)
## Workflow
This section details how to use the Image Sequencer and create output Texture Sheets. The workflow for a new Image Sequence is as follows:
1. Import and Configure Source Images
2. Create Image Sequence Asset and Open It
3. Adding input frames into the Image Sequence
4. Add Processors and Build your Texture Sheets
5. Export the output sequence as one or many textures.
After the first iteration on an Image sequence Asset, you can open this sequence later, make adjustments, then update your output image in one click using the **Update Button** in the canvas
### Importing and Configuring source images
Your source images will be imported inside the project for full efficiency, but they do not have to be used in your project. These textures will behave as intermediate sources for generating assets, so It could be a good Idea to use asset labels to filter them in order to ensure they are isolated and unused.
#### Manual settings
Texture configuration for the source images answers the “lossless quality” requirement : at this stage of workflow, you do not want to degrade quality, so importing your textures at maximum quality is required here.
In order to stay at lossless quality, make sure you have the following:
- Keep your textures uncompressed :
- Use Compression : None
- Ensure your texture will not be resized if too large:
- Advanced/Non-Power of Two : None
- Max Size : 8192
- Ensure your alpha channel will not be modified by the importer:
- Disable the Alpha Is Transparency (if available)
- Ensure the sRGB flag matches your image contents:
- sRGB on for any 8-bit per component Color Image
- sRGB off for:
- Any HDR Image (exr)
- Flow Maps, Normal Maps, Bump Maps and/or masks
#### Asset Postprocessor
It is advised to use an [AssetPostprocessor](https://docs.unity3d.com/ScriptReference/AssetPostprocessor.html) to ensure all your sources are configured correctly. Here is an example of one that would add a VFX and source label and Configure all textures within the Asset/Resources/ folder, with the same settings except disable sRGB for exr and textures with the **_nrm** nomenclature in their filename, or **_lin** nomenclature for forcing non sRGB.
You can find this default example that you can modify to fit your needs at the following path of the project:
```C#
using System.IO;
namespace UnityEditor.VFXToolbox
{
public class ImageSequencerSourcePostprocessor : AssetPostprocessor
{
// Internal flags for usage
public enum Usage
{
Color,
LinearData
}
public const string m_RootFolder = "Assets/VFXResources";
public const string m_NormalNomenclaturePostFix = "_nrm";
public const string m_LinearNomenclatureSuffix = "_lin";
public const string m_OpticalFlowNomenclatureSuffix = "_of";
public readonly string[] m_Labels = new string[] { "Weapon", "Audio" };
void OnPreprocessTexture()
{
if (assetPath.StartsWith(m_RootFolder)) // for all assets in VFX resources folder
{
string filename = Path.GetFileName(assetPath);
string extension = Path.GetExtension(assetPath);
// Default usage is color
Usage usage = Usage.Color;
// if containing normal suffix, switch to linear
if (filename.ToLower().Contains(m_NormalNomenclaturePostFix.ToLower()))
usage = Usage.LinearData;
// if containing linear suffix, switch to linear
if (filename.ToLower().Contains(m_LinearNomenclatureSuffix.ToLower()))
usage = Usage.LinearData;
// if containing opticalflow suffix, switch to linear
if (filename.ToLower().Contains(m_OpticalFlowNomenclatureSuffix.ToLower()))
usage = Usage.LinearData;
// if HDR, switch to linear
if(extension.ToLower() == "EXR".ToLower())
usage = Usage.LinearData;
TextureImporter importer = (TextureImporter)assetImporter;
// Even if we have normalmaps, we don't want to encode them in swizzled NM yet.
importer.textureType = TextureImporterType.Default;
switch(usage)
{
default: // Color, but should not happen
case Usage.Color:
importer.sRGBTexture = true;
break;
case Usage.LinearData:
importer.sRGBTexture = false;
break;
}
importer.alphaSource = TextureImporterAlphaSource.FromInput;
importer.alphaIsTransparency = false;
importer.maxTextureSize = 8192;
importer.mipmapEnabled = true;
importer.mipmapFilter = TextureImporterMipFilter.KaiserFilter;
importer.npotScale = TextureImporterNPOTScale.None;
importer.textureShape = TextureImporterShape.Texture2D;
importer.textureCompression = TextureImporterCompression.Uncompressed;
}
}
}
}
```
Here are some guidelines for handling various situations when your source image changes:
- Source file changes: In order to update these sources (i.e. you make changes inside your 3D VFX package and render your frames again), just overwrite the files. Source sequence length increases: If your sequence length ever changes, you will have to manually adjust your sequence length. A safe way to do this is to remove all the frames in the input sequence by clearing the list, then drag and drop the folder again.
- Source sequence length decreases: this corner case is not yet automatically addressed so you will have to manually trim the exceeding frames by deleting them in the project view. (or use a post-render script in your 3D Package to remove the files and the meta files)
### Creating an Image Sequence
You can create image sequences using the Create Asset Menu:
- From the main Menu : **Assets/Create/Visual Effects/Image Sequence**
- From the main Project Window + Menu : **Visual Effects/Image Sequence**
![Image Sequence Assets](images/ImageSequenceAssets.png)
After Clicking the item in the menu, enter the name of your Image Sequence asset in the Project View, then press Return key to validate.
Alternatively, When the Image Sequencer window is opened with no asset selected (eg: when opened from the menu), the Image Sequencer Window displays a message that informs that no Asset is selected, and provides the option to Create an image sequence from a button. When clicked, you can save a file for your new asset.
![](Images/ImageSequencerNoAsset.png)
#### Editing an Image Sequence asset
After Creating the Image Sequence, you can either:
* Double click its icon in the Project Window
* Click its Edit Sequence button at the top of the Unity Inspector
* Just select this asset while having the Image Sequencer window open.
### Input Frames
The first operation you need to do when working on an Image Sequence asset is to add Input Images to this sequence. These Images need to be already imported into your project and it is advised that they have particular settings in order to keep them as maximum quality. (See Importing and Configuring Source images)
![](images/ImageSequencer-InputFrames.png)
You can import images in the sequence by clicking the Input Frames tab Button to enter Input Frames editing mode then :
- By dragging them directly from the Project Window to the Image Sequencer window
- By dragging a folder that contains images from the Project Window to the Image Sequencer Window
- By selecting textures in the Project Window and clicking the plus button at the bottom of the reorderable list.
After importing, Images can be added, reordered, deleted, and using the popup menu, you can also perform actions such as sorting alphabetically. The Actions Popup Button offers you the following menu options:
* Clear : Removes all the elements in the Input Frames List
* Sort All : Sorts all elements in the input frames list alphabetically
* Reverse Order : Reverses the order of all elements in the Input Frames list
### Processors
After adding Input Frames to the Image Sequence, you can go into Processor Editing mode. To do so, click the Processors Tab Button at the top of the Left pane.
#### Processor Mode Inspector
When entering Processor mode, the left pane inspector changes and presents the following information:
![](images/ImageSequencer-ClearProcessors.png)
**Clear (Button) :** Clears the current list of frame processors (if not inheriting from another asset.)
![](images/ImageSequencer-FrameProcessorList.png)
**Frame Processors (Reorderable List) :** The list of currently used processors.
* If a frame processor is selected, below the list of processor list will appear the processor inspector.
* You can reorder processors by dragging them upwards or downwards in the list.
* You can enable or disable processors by clicking the toggle at the left of the Processor Title
#### Adding Processors
In order to add processors to the list, click the **+** button located at the bottom of the Processors List. Clicking the button opens the Processor Add Menu.
![Add Processor Menu](images/ImageSequencer-AddProcessorMenu.png)
You can use the Input Search Field at the top to filter and refine processors, or navigate the categories by clicking the items, and finally add the processor you want by clicking it in the list.
#### Removing Processors
You can remove processor using the following :
* Select a processor in the list then click the "-" button located at the bottom of the Processor List.
* Click the Clear button to remove all processors at once.
#### Editing Processors
Upon Selecting a Processor in the list, it displays its properties below the Processors list. You can then edit these properties to modify its behavior.
#### Adjusting Processor Visibility
![](images/ImageSequencer-ProcessorPreview.png)
Upon Selecting a processor in the list, it becomes previewed automatically in the viewport. You can check its visibility by looking at the **eye icon** located in the right part of the processor item in the Processors Reorderable List.
![](images/ImageSequencer-ProcessorPreviewLock.png)
You can **lock** or **unlock** the preview of a certain processor by toggling its lock icon, located in the far right part of the processor item in the Processors Reorderable List.
When in **locked preview state**, you can still select and edit other processors to preview the changes on the currently locked processor view.
#### Inheriting Processors and Settings from other Image Sequences
#### ![ImageSequencer-InheritProcessors](images/ImageSequencer-InheritProcessors.png)
**Inherit Processors From (Image Sequence Asset) :** When using an Image Sequence asset in this field, the whole processor stack becomes overridden by the same processors and their configuration stored into the other asset. When inheriting, you cannot edit these values, except in the Asset that defined them.
### Exporting the Image Sequence
After working on an image sequence, and adding processors, you will end up in a state where you want to generate a texture out of the result of this image sequence.
## Built-In Processors
This section details the built-in processors bundled with Image Sequencer and their behavior.
### Main Category
#### Custom Material
Custom Material Processor enables using custom materials to process Frames. Custom Materials enable performing operations on a frame's pixels using a shader.
When selecting a Custom Material processor, the following inspector is displayed:
![](images/ImageSequencer-CustomMaterial.png)
The **material** field references a material in the project, for use with this processor. When a material is selected, it displays its inspector below the material field.
You can adjust material properties directly in this inspector.
> Note: Materials you edit in the inspector are edited directly in the asset so they can share their settings across various Image Sequences.
To write shaders compatible with Image Sequencer, see the [Custom Material Shader](#writing-shaders-for-custom-material-processor) documentation.
### Color
Color processors work on image color for common tasks.
#### Alpha From RGB
Alpha from RGB Processor generates an alpha channel value based on luminance values stored in the RGB channels of the input frames.
When selecting an Alpha from RGB processor, the following inspector is displayed:
![](images/ImageSequencer-AlphaFromRGB.png)
**Color Filter** : A color to apply as a tint before converting the RGB values to Grayscale.
#### Color Correction
The Color Correction processor applies Brightness, Contrast and Saturation control over the input Frames, as well as Alpha Remapping.
When selecting an Alpha from RGB processor, the following inspector is displayed:
![](images/ImageSequencer-ColorCorrection.png)
* **Brightness** : Controls image Brightnness
* **Contrast** : Control Image Contrast
* **Saturation** : Control Image Color Saturation
* **Alpha** Curve : Remaps the Alpha Output Range based on the input Alpha values
#### Pre-multiply Alpha
The Pre-Multiply Alpha Processor applies a simple Alpha mask on RGB values to ensure RGB values are pre-multiplied by the alpha channel. This process is often required to use your textures in pre-multiplied alpha blend mode.
When selecting an Pre-Multiply Alpha processor, the following inspector is displayed:
![](images/ImageSequencer-PremultiplyAlpha.png)
* **Remove Alpha** (Toggle) : Replaces the alpha channel by a solid opacity
* **Alpha Value** (Float) : The solid opacity value to apply if Remove Alpha is enabled.
#### Remap Color
The Remap Color Processor remaps the output color based on a single grayscale value. This process is quite similar to Photoshop's Gradient Map filter.
When selecting an Remap Color processor, the following inspector is displayed:
![](images/ImageSequencer-RemapColor.png)
* **Color Source** (Enum) : Controls how the input grayscale gradient is handled
* SRGB Luminance : RGB luminance as sRGB values (for sRGB input images)
* Linear Luminance : Linear Luminance (for HDR input images)
* Linear Red : Linear Red input channel value
* Linear Green : Linear Green input channel value
* Linear Blue : Linear Blue input channel value
* Alpha : Linear Alpha input channel value
* **Remap Gradient** (Gradient) : Gradient sampled using the input grayscale value to output values.
#### Remove Background
The Remove Background Processor works by trying to remove the RGB values of the background color and restoring the original color of the alpha blended element.
When selecting a Remove Background processor, the following inspector is displayed:
![](images/ImageSequencer-RemoveBackground.png)
* **Background Color** (Color) : the assumed Color of the background, used for reverse lerp.
* **Grab** (Button) : Tries to fetch the first pixel's color to use it as the background color.
### Common
Common-category processors work on various tasks such as optimizing the size of every frame and performing adjustments.
#### Crop
The Crop Processor applies cropping to the four edges of a frame in order to reduce unused transparent space and maximize overdraw efficiency. It provides an automatic computation of the closest bounds.
When selecting a Crop processor, the following inspector is displayed:
![](images/ImageSequencer-Crop.png)
* **Top** (Slider) : The amount of pixels to crop at the top edge of the image.
* **Bottom** (Slider) : The amount of pixels to crop at the bottom edge of the image.
* **Left** (Slider) : The amount of pixels to crop at the left edge of the image.
* **Right** (Slider) : The amount of pixels to crop at the right edge of the image.
* **Automatic Crop Values** : A tool that computes automatically the Top, Bottom, Left and Right values.
* **Alpha Threshold** (Slider) : The alpha threshold taken into account for the closest bound computation
* **Find** (Button) : Clicking this button will perform the check on all Crop processor's input frames.
#### Fix Borders
The Fix Border Processor applies gradient fading to all edges of the image, ensuring it fades towards transparent and/or a given color.
When selecting a Fix Border processor, the following inspector is displayed:
![](images/ImageSequencer-FixBorders.png)
* **Left** (Slider) : The percentage in width that will fade in from the left edge.
* **Right** (Slider) : The percentage in width that will fade in from the right edge.
* **Top** (Slider) : The percentage in width that will fade in from the top edge.
* **Bottom** (Slider) : The percentage in width that will fade in from the bottom edge.
* **Fade to Color** : The blended target color that will apply to RGB channels (use alpha = 0 to not affect RGB at all)
* **Fade to Alpha** : The target alpha that will be blended to at the edges of the image (default 0 to fade out)
* **Exponent** : An exponent applied to the gradient to make it harder or softer
#### Resize
The Resize processor simply resizes images from the processor's input sequence.
When selecting a Resize processor, the following inspector is displayed:
![](images/ImageSequencer-Resize.png)
* Width : the desired output width.
* Height : the desired output height.
> Use the buttons to access common power of two values.
#### Rotate
The Rotate processor provides 90 degree step rotation to apply on the processor's input images.
When selecting a Rotate processor, the following inspector is displayed:
![](images/ImageSequencer-Rotate.png)
* **Rotation Mode** (enum) : Provides rotation options for 90, 180 or 270 degrees clockwise.
### Sequence
Sequence processors applies processing that modifies the sequence timing's and frame count.
#### Decimate
The Decimate processor discards input images to keep only one image out of N.
When selecting a Decimate processor, the following inspector is displayed:
![](images/ImageSequencer-Decimate.png)
* **Decimate By** (int) : The decimation factor (keeping one image out of N).
#### Fade
The Fade processor fades the image alpha and color to a given color matte, based on a time curve.
When selecting a Fade processor, the following inspector is displayed:
![](images/ImageSequencer-Fade.png)
* **Fade to Color** (Color) : The color to fade To
* **Fade Curve** (Curve) : The fading curve based on the overall progress of the sequence (percentage as X axis), with Y values blending from the Fade color (0.0) to the Actual color (1.0)
#### Loop Sequence
The Loop Sequence Processor outputs a sequence that is ensured to loop, based on a longer input sequence, and a sync frame.
Images from the input sequence are blended from a sequence prior to the sync frame and after the sync frame. Then blended using a curve in order to ensure the last and first frame (sync frame and sync frame+1) are contiguous.
When selecting a Loop Sequence processor, the following inspector is displayed:
![](images/ImageSequencer-Looping.png)
* **Input Sync Frame** : the desired frame index to use as sync point.
* **Output Sequence Length** : the desired output sequence length, cannot be greater than half of the input sequence length.
* **Mix Curve** : The blend curve to apply between the sequence prior to the sync point.
#### Retime
The retime processor applies temporal remapping to the input sequence in order to apply acceleration and/or deceleration to the motion of images.
When selecting a Retime processor, the following inspector is displayed:
![](images/ImageSequencer-Retime.png)
* **Sequence Length** : the desired output sequence length
* **Use Retiming Curve** : whether to remap the time using a curve instead of a linear retiming.
* **Retime Curve** : the time remap curve to apply to the input sequence. Based on the output sequence percentage (X axis), and given the input sequence frames (Y axis)
### Texture Sheet
Texture Sheet processors applies processing to image sequences to turn them into flipbooks or to turn flipbooks into sequences of images.
#### Assemble Flipbook
The Assemble Flipbook assembles a sequence of images into a flipbook sprite sheet of given rows and columns.
When selecting a Assemble Flipbook processor, the following inspector is displayed:
![](images/ImageSequencer-AssembleFlipbook.png)
* **Assemble Mode** (enum) : How the input sequence is processed
* Full Sprite Sheet : Assembles the sprite sheet as rows and columns
* Vertical
* **Columns** (U) : how many columns to pack into
* **Rows** (V) : how many rows to pack into
* **Find Best Ratios** : Clicking this button will show a drop down with all Row and Column possibilities, sorted by closeness to a power-of-two-compatible ratio (ability to resize to sizes that are power of while maintaining a correct Texel aspect ratio)
#### Break Flipbook
The Break Flipbook operator splits an input image into a sequence of length based on the given number of rows and columns.
When selecting a Break Flipbook processor, the following inspector is displayed:
![](images/ImageSequencer-BreakFlipbook.png)
* Columns : the desired amount of columns to split into
* Rows : the desired amount of rows to split into.
### Shaders for Custom Material Processor
Shaders for Custom Material processor provide a simple way to write a filter for Image Sequencer without relying to writing a full Custom Processor. By using a custom material processor you will be able to run a shader for every input frame and output an output frame of same dimensions and same rows and columns.
#### Writing a shader for Custom Materials
In order to create shaders for Custom material processor you need to create a simple unlit shader from the Project Create Menu : **Shaders > Unlit Shader**. You can alter the default code to match the following code:
```c
Shader "ImageSequencer/SimpleCustomMaterial"
{
Properties
{
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _InputFrame;
float4 _FrameData;
float4 _FlipbookData;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Process the Color
fixed4 col = tex2D(_InputFrame, i.uv);
return col;
}
ENDCG
}
}
}
```
#### Specific Uniforms
In order to access the input frame texture, you need to declare `sampler2D _InputFrame;` as uniform variable in the body of the shader. This uniform **must not be declared** into the `properties` section of the shader.
If you want to access per-frame data, you can declare the following uniform variables in order to get values from Image Sequencer.
```
// ImageSequencer CustomMaterial uniforms
// ======================================
//
// sampler2D _InputFrame; // Input Frame (from previous sequence)
//
// float4 _FrameData; // Frame Data
// // x, y : width (x) and height (y) in pixels
// // z, w : sequence index (z) and length (w)
//
// float4 _FlipbookData; // Flipbook Data
// // x, y : number of columns (x) and rows (y)
// // z, w : (unused)
```
### Custom Processors
Custom Processors can be created in your project, to extend the features of Image Sequencer. Custom processors differ from Custom Material Processors as they can access more of the ImageSequencer features:
* Output a sequence length that's different from the input sequence length
* Output a sequence of images of different pixel dimensions from the input sequence pixel dimensions.
* Change the **numU** and **numV** texture sheet cells.
* Perform multiple shader calls per execution
* Display a custom inspector and custom canvas overlay
#### Creating a new Custom Processor
To create a new custom processor so you need the following:
* A C# class that extends `UnityEditor.Experimental.VFX.Toolbox.ImageSequencer.ProcessorBase`
* This class is for use with **UnityEditor** assembly only and must be stored into an `Editor` folder.
* The C# class must implement the `[Processor("Category","Name")]` class attribute to be visible in the add menu.
* A Shader file that will be referenced and used by the C# class.
Here is a sample C# code that you can use as a starting base.
```c#
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Experimental.VFX.Toolbox.ImageSequencer;
using UnityEngine;
[Processor("Category","Sample Processor")]
public class SampleProcessor : ProcessorBase
{
/// <summary>
/// The Shader Path (in the project) of the Shader File
/// </summary>
public override string shaderPath => "Assets/Path/To/Shader.shader";
/// <summary>
/// The Processor Name (as it will appear in the list)
/// </summary>
public override string processorName => "Sample Processor";
/// <summary>
/// Default() configures a default instance of the processor
/// of the processor (e.g: when it will be added from the menu)
/// </summary>
public override void Default()
{
}
/// <summary>
/// OnInspectorGUI() displays the properties in the left pane editor.
/// use the serializedObject to fetch serializedProperties
/// </summary>
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
return changed;
}
/// <summary>
/// Process(int frame) method is called when the frame needs to be processed.
/// ProcessFrame(int, texture) needs to be called in order to run the shader on the full frame.
/// You can still perform Graphics.Blit() if you need to perform multiple blits.
/// </summary>
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
ProcessFrame(frame, inputFrame);
return true;
}
}
```
Here is the corresponding Shader code:
```c
Shader "Hidden/VFXToolbox/ImageSequencer/SampleProcessor"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.uv);
}
ENDCG
}
}
}
```

157
Editor/ImageSequencer/ProcessingNodeStack.Serialization.cs


using UnityEngine;
using System;
using System.Reflection;
using System.Collections.Generic;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ProcessingNodeStack
{
public void AddSettingsObjectToAsset(ImageSequence asset, ScriptableObject settings)
{
AssetDatabase.AddObjectToAsset(settings,asset);
settings.hideFlags = HideFlags.HideInHierarchy;
}
public void AddProcessorInfoObjectToAsset(ImageSequence asset, ProcessorInfo info)
{
AssetDatabase.AddObjectToAsset(info,asset);
info.hideFlags = HideFlags.HideInHierarchy;
}
public void RemoveAllInputFrames(ImageSequence asset)
{
asset.inputFrameGUIDs.Clear();
m_InputSequence.frames.Clear();
EditorUtility.SetDirty(asset);
}
public void SortAllInputFrames(ImageSequence asset)
{
asset.inputFrameGUIDs.Sort((guidA,guidB) => {
return string.Compare(AssetDatabase.GUIDToAssetPath(guidA), AssetDatabase.GUIDToAssetPath(guidB));
});
EditorUtility.SetDirty(asset);
}
public void ReverseAllInputFrames(ImageSequence asset)
{
asset.inputFrameGUIDs.Reverse();
EditorUtility.SetDirty(asset);
}
public void LoadFramesFromAsset(ImageSequence asset)
{
inputSequence.frames.Clear();
if (asset.inputFrameGUIDs != null && asset.inputFrameGUIDs.Count > 0)
{
int count = asset.inputFrameGUIDs.Count;
int i = 1;
foreach (string guid in asset.inputFrameGUIDs)
{
VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Loading Textures (" + i + "/" + count + ")", (float)i/count, 0.1f);
string path = AssetDatabase.GUIDToAssetPath(guid);
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
if (t != null)
{
inputSequence.frames.Add(new ProcessingFrame(t));
}
else
{
inputSequence.frames.Add(ProcessingFrame.Missing);
}
i++;
}
VFXToolboxGUIUtility.ClearProgressBar();
}
}
public void SyncFramesToAsset(ImageSequence asset)
{
asset.inputFrameGUIDs.Clear();
foreach(ProcessingFrame f in inputSequence.frames)
{
asset.inputFrameGUIDs.Add(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(f.texture)));
}
EditorUtility.SetDirty(asset);
}
public void AddProcessor(ProcessingNode node, ImageSequence asset)
{
AddProcessorInfoObjectToAsset(asset, node.ProcessorInfo);
asset.processorInfos.Add(node.ProcessorInfo);
ProcessorBase settings = node.GetSettingsAbstract();
if (settings != null)
{
AddSettingsObjectToAsset(asset, settings);
node.ProcessorInfo.Settings = settings;
}
m_ProcessingNodes.Add(node);
EditorUtility.SetDirty(asset);
}
public void RemoveAllProcessors(ImageSequence asset)
{
asset.processorInfos.Clear();
m_ProcessingNodes.Clear();
EditorUtility.SetDirty(asset);
}
public void RemoveProcessor(int index, ImageSequence asset)
{
asset.processorInfos.RemoveAt(index);
m_ProcessingNodes.RemoveAt(index);
EditorUtility.SetDirty(asset);
}
public void ReorderProcessors(ImageSequence asset)
{
if(m_ProcessingNodes.Count > 0)
{
List<ProcessingNode> old = new List<ProcessingNode>();
foreach(ProcessingNode n in m_ProcessingNodes)
{
old.Add(n);
}
m_ProcessingNodes.Clear();
foreach(ProcessorInfo info in asset.processorInfos)
{
foreach(ProcessingNode p in old)
{
if(p.ProcessorInfo.Equals(info))
{
m_ProcessingNodes.Add(p);
break;
}
}
}
EditorUtility.SetDirty(asset);
}
}
public void LoadProcessorsFromAsset(ImageSequence asset)
{
m_ProcessingNodes.Clear();
var infos = asset.processorInfos;
UpdateProcessorsFromAssembly();
// Creating Runtime
foreach(ProcessorInfo procInfo in infos)
{
var processor = (ProcessingNode)Activator.CreateInstance(typeof(ProcessingNode), this, procInfo);
m_ProcessingNodes.Add(processor);
}
}
}
}

149
Editor/ImageSequencer/ProcessingNodeStack.cs


using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
internal partial class ProcessingNodeStack
{
public ProcessingFrameSequence inputSequence
{
get
{
return m_InputSequence;
}
}
public ProcessingFrameSequence outputSequence
{
get
{
if (m_ProcessingNodes.Count > 0)
return m_ProcessingNodes[m_ProcessingNodes.Count - 1].OutputSequence;
else
return m_InputSequence;
}
}
public ImageSequencer imageSequencer
{
get { return m_ImageSequencer; }
}
public List<ProcessingNode> nodes
{
get
{
return m_ProcessingNodes;
}
}
private List<ProcessingNode> m_ProcessingNodes;
private ProcessingFrameSequence m_InputSequence;
private ImageSequencer m_ImageSequencer;
public ProcessingNodeStack(ProcessingFrameSequence inputSequence, ImageSequencer imageSequencer)
{
m_InputSequence = inputSequence;
m_ProcessingNodes = new List<ProcessingNode>();
m_ImageSequencer = imageSequencer;
}
public void Dispose()
{
foreach(ProcessingNode p in m_ProcessingNodes)
{
p.Dispose();
}
m_ProcessingNodes.Clear();
}
public ProcessingFrameSequence GetOutputSequence()
{
if(m_ProcessingNodes.Count > 0)
{
return m_ProcessingNodes[m_ProcessingNodes.Count - 1].OutputSequence;
}
else
{
return inputSequence;
}
}
public ProcessingFrameSequence GetInputSequence(ProcessingNode processor)
{
int index = m_ProcessingNodes.IndexOf(processor);
if (index > 0)
{
return m_ProcessingNodes[index - 1].OutputSequence;
}
else
return m_InputSequence;
}
public ProcessingNode GetNextProcessor(ProcessingNode node)
{
int index = m_ProcessingNodes.IndexOf(node);
if(index < m_ProcessingNodes.Count-1)
{
return m_ProcessingNodes[index + 1];
}
return null;
}
public void Invalidate(ProcessingNode node)
{
int index = m_ProcessingNodes.IndexOf(node);
if(index != -1)
m_ProcessingNodes[index].Invalidate();
}
public void InvalidateAll()
{
if (m_ProcessingNodes.Count > 0)
m_ProcessingNodes[0].Invalidate();
}
public Dictionary<Type, ProcessorAttribute> settingsDefinitions { get; private set; }
public void UpdateProcessorsFromAssembly()
{
settingsDefinitions = new Dictionary<Type, ProcessorAttribute>();
var processorType = typeof(ProcessorBase);
var attrType = typeof(ProcessorAttribute);
Type[] allProcessorTypes = new Type[0];
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies() )
{
try
{
allProcessorTypes = allProcessorTypes.Concat(assembly.GetTypes().Where(t =>
t.IsClass
&& !t.IsAbstract
&& t.IsSubclassOf(processorType)
&& t.IsDefined(attrType, false)
)).ToArray();
}
catch (Exception e)
{
UnityEngine.Debug.LogException(e);
}
}
foreach (var type in allProcessorTypes)
{
var attr = (ProcessorAttribute)type.GetCustomAttributes(attrType, false)[0];
settingsDefinitions.Add(type, attr);
}
}
}
}

203
Editor/ImageSequencer/Serialization/ProcessorBase.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
/// <summary>
/// Base Class for Custom Processors. Derive from this class to add a new Processor.
/// In order to populate processors in the menu, you need to implement the [ProcessorAttribute] to the class.
/// </summary>
public abstract class ProcessorBase : ScriptableObject
{
/// <summary>
/// Asset Path of the Shader used for this processor (eg: "Assets/Shaders/MyShader.shader")
/// </summary>
public abstract string shaderPath { get; }
/// <summary>
/// Name of the Processor
/// </summary>
public abstract string processorName { get; }
/// <summary>
/// Display Label text of the processor (will be displayed in the processor list and asset inspector)
/// </summary>
public virtual string label => processorName;
/// <summary>
/// Number of U (Columns) defined by this processor. Implement to override default (passthrough input sequence's numU)
/// </summary>
public virtual int numU => inputSequenceNumU;
/// <summary>
/// Number of V (Rows) defined by this processor. Implement to override default (passthrough input sequence's numV)
/// </summary>
public virtual int numV => inputSequenceNumV;
/// <summary>
/// Number of frames defined by this processor. Implement to override default (passthrough input's sequence length)
/// </summary>
public virtual int sequenceLength => processingNode.InputSequence.length;
/// <summary>
/// Determines the actual processing of the frame. Will be called when this frame is requested by the Image Sequencer.
/// </summary>
/// <param name="frame">the requested frame index</param>
/// <returns></returns>
public abstract bool Process(int frame);
/// <summary>
/// Updates the output size of the processing node (resets internal render targets), implement to override the default (passthrough the input sequence frame width and height)
/// </summary>
public virtual void UpdateOutputSize()
{
processingNode.SetOutputSize(processingNode.InputSequence.width, processingNode.InputSequence.height);
}
/// <summary>
/// Displays the Processor inspector in the left pane of the Image Sequencer
/// </summary>
/// <param name="changed">Whether the inspector has already caught changes.</param>
/// <param name="serializedObject">The processor's serializedObject</param>
/// <returns>whether there has changes to apply</returns>
public abstract bool OnInspectorGUI(bool changed, SerializedObject serializedObject);
/// <summary>
/// Displays the Processor's Canvas Helpers as overlay.
/// </summary>
/// <param name="canvas">The Image Sequencer Canvas currently drawn</param>
/// <returns>whether the canvas needs to redraw</returns>
public virtual bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
return false;
}
/// <summary>
/// Sets the default values of the processor. Will be called to configure the default state when a new processor is added to the Image Sequence.
/// </summary>
public abstract void Default();
#region PROCESSINGNODE ACCESS
/// <summary>
/// The Input Sequence Frame Count
/// </summary>
public int inputSequenceLength => processingNode.InputSequence.length;
/// <summary>
/// The Input Sequence Frame Width (in pixels)
/// </summary>
public int inputSequenceWidth => processingNode.InputSequence.width;
/// <summary>
/// The Input Sequence Frame Height (in pixels)
/// </summary>
public int inputSequenceHeight => processingNode.InputSequence.height;
/// <summary>
/// The Input Sequence Flipbook U Count (Columns)
/// </summary>
public int inputSequenceNumU => processingNode.InputSequence.numU;
/// <summary>
/// The Input Sequence Flipbook V Count (Rows)
/// </summary>
public int inputSequenceNumV => processingNode.InputSequence.numV;
/// <summary>
/// Whether the Input frame Sequence is the Asset's Input Frame List (use to determine whether it needs gamma correction or not)
/// </summary>
public bool isInputFrameSequence => processingNode.InputSequence.processingNode == null;
/// <summary>
/// Whether the current processor is being previewed in the Image Sequencer Viewport, or not (for example, when the view is locked to another processor's result)
/// </summary>
public bool isCurrentlyPreviewed => processingNode.isCurrentlyPreviewed;
/// <summary>
/// The current Image Sequencer Viewport's preview sequence length. Please not that this is not necessarily this Processor's preview, use isCurrentlyPreviewed to check.
/// </summary>
public int previewSequenceLength => processingNode.previewSequenceLength;
/// <summary>
/// The current Image Sequencer Viewport's preview image index. Please not that this is not necessarily this Processor's preview, use isCurrentlyPreviewed to check.
/// </summary>
public int previewCurrentFrame => processingNode.previewCurrentFrame;
/// <summary>
/// The material internally used for this processor. It is created from the shader defined using this.shaderPath.
/// </summary>
public Material material => processingNode.material;
/// <summary>
/// Requests the Texture (and its Processing) of the Input Sequence Image at given index.
/// </summary>
/// <param name="index">The index of the Frame to be returned.</param>
/// <returns>The texture object corresponding to the frame.</returns>
public Texture RequestInputTexture(int index)
{
return processingNode.InputSequence.RequestFrame(index).texture;
}
/// <summary>
/// Requests the Texture of the Output Sequence Image at given index.
/// </summary>
/// <param name="index">The index of the Frame to be returned.</param>
/// <returns>The texture object corresponding to the frame.</returns>
public Texture RequestOutputTexture(int index)
{
return processingNode.OutputSequence.frames[index].texture;
}
/// <summary>
/// Sets the Output Size to the Processing Node
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public void SetOutputSize(int width, int height)
{
processingNode.SetOutputSize(width, height);
}
/// <summary>
/// Process the Frame at Given Index, using given material and texture as MainTexture.
/// </summary>
/// <param name="outputIndex">Frame Index to process</param>
/// <param name="mainTexture">Texture object to pass as MainTexture</param>
/// <param name="material">Material to use for the procesing</param>
public void ProcessFrame(int outputIndex, Texture mainTexture, Material material)
{
processingNode.ExecuteShaderAndDump(outputIndex, mainTexture, material);
}
/// <summary>
/// Process the Frame at Given Index, using default processor material and texture as MainTexture.
/// </summary>
/// <param name="outputIndex">Frame Index to process</param>
/// <param name="mainTexture">Texture object to pass as MainTexture</param>
public void ProcessFrame(int outputIndex, Texture mainTexture = null)
{
processingNode.ExecuteShaderAndDump(outputIndex, mainTexture);
}
/// <summary>
/// Invalidates the Processor (will require to rebake frames)
/// </summary>
public void Invalidate()
{
processingNode.Invalidate();
}
#endregion
#region INTERNAL
private ProcessingNode processingNode;
internal void AttachTo(ProcessingNode processor)
{
this.processingNode = processor;
}
#endregion
}
}

48
Editor/ImageSequencer/Serialization/Processors/AlphaFromRGBProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Color","Alpha From RGB")]
internal class AlphaFromRGBProcessor : ProcessorBase
{
public Color BWFilterTint;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/AlphaFromRGB.shader";
public override string processorName => "Alpha From RGB";
public override void Default()
{
BWFilterTint = Color.white;
}
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetVector("_RGBTint", BWFilterTint);
ProcessFrame(frame, inputFrame);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var tint = serializedObject.FindProperty("BWFilterTint");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(tint, VFXToolboxGUIUtility.Get("Color Filter"));
EditorGUILayout.HelpBox("Color Filter serves as a tint before applying the black and white desaturation, just like in black and white photography. This way you can filter color weighting.", MessageType.Info);
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
return changed;
}
}
}

319
Editor/ImageSequencer/Serialization/Processors/AssembleProcessor.cs


using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Texture Sheet", "Assemble Flipbook")]
internal class AssembleProcessor : ProcessorBase
{
public enum AssembleMode
{
FullSpriteSheet = 0,
VerticalSequence = 1
}
public int FlipbookNumU;
public int FlipbookNumV;
public AssembleMode Mode;
bool hasChanged = false;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/AssembleBlit.shader";
public override string processorName => "Assemble Flipbook";
public override string label
{
get
{
string numU = (Mode == AssembleMode.VerticalSequence) ? "*" : FlipbookNumU.ToString();
string numV = FlipbookNumV.ToString();
return $"{processorName} ({numU}x{numV})";
}
}
public override int numU
{
get
{
switch (Mode)
{
default:
case AssembleMode.FullSpriteSheet:
return FlipbookNumU * inputSequenceNumU;
case AssembleMode.VerticalSequence:
return inputSequenceNumU;
}
}
}
public override int numV => FlipbookNumV * inputSequenceNumV;
public override int sequenceLength
{
get
{
switch (Mode)
{
default:
case AssembleMode.FullSpriteSheet:
return 1;
case AssembleMode.VerticalSequence:
return inputSequenceLength / FlipbookNumV;
}
}
}
public override void Default()
{
FlipbookNumU = 5;
FlipbookNumV = 5;
Mode = AssembleMode.FullSpriteSheet;
}
public override void UpdateOutputSize()
{
switch (Mode)
{
case AssembleMode.FullSpriteSheet:
SetOutputSize(inputSequenceWidth * FlipbookNumU, inputSequenceHeight * FlipbookNumV);
break;
case AssembleMode.VerticalSequence:
SetOutputSize(inputSequenceWidth, inputSequenceHeight * FlipbookNumV);
break;
}
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
if (Event.current.type != EventType.Repaint)
return false;
Vector2 topRight;
Vector2 bottomLeft;
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2));
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2));
// Texts
GUI.color = canvas.styles.green;
for (int i = 0; i < FlipbookNumU; i++)
{
float cw = (topRight.x - bottomLeft.x) / FlipbookNumU;
GUI.Label(new Rect(bottomLeft.x + i * cw, topRight.y - 16, cw, 16), (i + 1).ToString(), canvas.styles.miniLabelCenter);
}
for (int i = 0; i < FlipbookNumV; i++)
{
float ch = (bottomLeft.y - topRight.y) / FlipbookNumV;
VFXToolboxGUIUtility.GUIRotatedLabel(new Rect(bottomLeft.x - 8, topRight.y + i * ch, 16, ch), (i + 1).ToString(), -90.0f, canvas.styles.miniLabelCenter);
}
GUI.color = Color.white;
return false;
}
public override bool Process(int frame)
{
int length = inputSequenceLength;
RenderTexture backup = RenderTexture.active;
switch (Mode)
{
case AssembleMode.FullSpriteSheet:
// Blit Every Image inside output
for (int i = 0; i < (FlipbookNumU * FlipbookNumV); i++)
{
int u = i % FlipbookNumU;
int v = (FlipbookNumV - 1) - (int)Mathf.Floor((float)i / FlipbookNumU);
Vector2 size = new Vector2(1.0f / FlipbookNumU, 1.0f / FlipbookNumV);
int idx = Mathf.Clamp(i, 0, length - 1);
Texture currentTexture = RequestInputTexture(idx);
Vector4 ClipCoordinates = new Vector4(u * size.x, v * size.y, size.x, size.y);
material.SetTexture("_MainTex", currentTexture);
material.SetVector("_CC", ClipCoordinates);
Graphics.Blit(currentTexture, (RenderTexture)RequestOutputTexture(0), material);
}
RenderTexture.active = backup;
break;
case AssembleMode.VerticalSequence:
// Blit Every N'th Image inside output
int cycleLength = inputSequenceLength / FlipbookNumV;
for (int i = 0; i < FlipbookNumV; i++)
{
int u = 0;
int v = FlipbookNumV - 1 - i;
Vector2 size = new Vector2(1.0f, 1.0f / FlipbookNumV);
int idx = Mathf.Clamp((i * cycleLength) + frame, 0, length - 1);
Texture currentTexture = RequestInputTexture(idx);
Vector4 ClipCoordinates = new Vector4(u * size.x, v * size.y, size.x, size.y);
material.SetTexture("_MainTex", currentTexture);
material.SetVector("_CC", ClipCoordinates);
Graphics.Blit(currentTexture, (RenderTexture)RequestOutputTexture(frame), material);
}
RenderTexture.active = backup;
break;
}
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var flipbookNumU = serializedObject.FindProperty("FlipbookNumU");
var flipbookNumV = serializedObject.FindProperty("FlipbookNumV");
var mode = serializedObject.FindProperty("Mode");
var assembleMode = (AssembleMode)mode.intValue;
EditorGUI.BeginChangeCheck();
assembleMode = (AssembleMode)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Assemble Mode"), assembleMode);
if (assembleMode != (AssembleMode)mode.intValue)
{
mode.intValue = (int)assembleMode;
hasChanged = true;
}
switch (assembleMode)
{
case AssembleMode.FullSpriteSheet:
int newU = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Columns (U) : "), flipbookNumU.intValue);
int newV = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue);
if (inputSequenceLength > 0)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Label(VFXToolboxGUIUtility.Get("Find Best Ratios"), GUILayout.Width(EditorGUIUtility.labelWidth));
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Get")))
{
float frameRatio = (float)inputSequenceWidth / (float)inputSequenceHeight;
int length = inputSequenceLength;
List<int> ratios = new List<int>();
SortedDictionary<int, float> coeffs = new SortedDictionary<int, float>();
float rad = Mathf.Sqrt(length);
for (int i = (int)rad; i >= 1; i--)
{
if (((float)length / (float)i) % 1.0f == 0.0f)
{
float pageRatio = (float)i / (length / i);
float fullRatio = frameRatio * pageRatio;
float divergence = Mathf.Abs(Mathf.Log(fullRatio, 2.0f) % 1.0f);
if (!ratios.Contains(i))
{
ratios.Add(i);
coeffs.Add(i, divergence);
}
fullRatio = frameRatio / pageRatio;
divergence = Mathf.Abs(Mathf.Log(fullRatio, 2.0f) % 1.0f);
if (!ratios.Contains(length / i))
{
ratios.Add(length / i);
coeffs.Add((length / i), divergence);
}
}
}
GenericMenu menu = new GenericMenu();
var sortedValues = coeffs.OrderBy(kvp => kvp.Value);
foreach (KeyValuePair<int, float> kvp in sortedValues)
{
int value = kvp.Key;
menu.AddItem(new GUIContent(value + " x " + (length / value) + ((kvp.Value == 0.0f) ? " (PERFECT)" : "")), false,
(o) => {
var seq_length = inputSequenceLength;
var seq_numU = (int)o;
var seq_numV = seq_length / seq_numU;
serializedObject.Update();
var seq_flipbookNumU = serializedObject.FindProperty("FlipbookNumU");
var seq_flipbookNumV = serializedObject.FindProperty("FlipbookNumV");
seq_flipbookNumU.intValue = seq_numU;
seq_flipbookNumV.intValue = seq_numV;
serializedObject.ApplyModifiedProperties();
UpdateOutputSize();
Invalidate();
}
, value);
}
if (menu.GetItemCount() > 0)
menu.ShowAsContext();
}
}
EditorGUILayout.HelpBox("Find Best Ratios will try to find matching possibilities for the current sequence, ordered by ratio pertinence, meaning that first results will have less stretch when resized to power-of-two textures.", MessageType.Info);
}
if (newU != flipbookNumU.intValue)
{
newU = Mathf.Clamp(newU, 1, inputSequenceLength / newV);
flipbookNumU.intValue = newU;
}
if (newV != flipbookNumV.intValue)
{
newV = Mathf.Clamp(newV, 1, inputSequenceLength / newU);
flipbookNumV.intValue = newV;
}
break;
case AssembleMode.VerticalSequence:
int numRows = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue);
if (numRows != flipbookNumV.intValue)
{
numRows = Mathf.Clamp(numRows, 1, inputSequenceLength);
flipbookNumV.intValue = numRows;
}
break;
}
if (EditorGUI.EndChangeCheck())
{
UpdateOutputSize();
Invalidate();
hasChanged = true;
}
return hasChanged;
}
}
}

101
Editor/ImageSequencer/Serialization/Processors/BreakFlipbookProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Texture Sheet", "Break Flipbook")]
internal class BreakFlipbookProcessor : ProcessorBase
{
public int FlipbookNumU;
public int FlipbookNumV;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/GetSubUV.shader";
public override string processorName => "Break Flipbook";
public override string label => $"{processorName} ({FlipbookNumU}x{FlipbookNumV}): {FlipbookNumU * FlipbookNumV} frame(s).";
private bool m_BypassSecurityCheck = false;
public override void Default()
{
FlipbookNumU = 5;
FlipbookNumV = 5;
}
public override void UpdateOutputSize()
{
int width = (int)Mathf.Ceil((float)inputSequenceWidth / FlipbookNumU);
int height = (int)Mathf.Ceil((float)inputSequenceHeight / FlipbookNumV);
SetOutputSize(width, height);
}
public override int sequenceLength => Mathf.Min(FlipbookNumU, inputSequenceWidth) * Mathf.Min(FlipbookNumV, inputSequenceHeight);
public override bool Process(int frame)
{
Texture texture = RequestInputTexture(0);
material.SetTexture("_MainTex", texture);
Vector4 rect = new Vector4();
int u = Mathf.Min(FlipbookNumU, texture.width);
int v = Mathf.Min(FlipbookNumV, texture.height);
int x = frame % FlipbookNumU;
int y = (int)Mathf.Floor((float)frame / u);
rect.x = (float)x;
rect.y = (float)(v - 1) - y;
rect.z = 1.0f / u;
rect.w = 1.0f / v;
material.SetVector("_Rect", rect);
ProcessFrame(frame, texture);
return true;
}
public override bool OnInspectorGUI(bool hasChanged, SerializedObject serializedObject)
{
var flipbookNumU = serializedObject.FindProperty("FlipbookNumU");
var flipbookNumV = serializedObject.FindProperty("FlipbookNumV");
EditorGUI.BeginChangeCheck();
int newU = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Columns (U) : "), flipbookNumU.intValue), 1, inputSequenceWidth);
int newV = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue), 1, inputSequenceHeight);
if (newU != flipbookNumU.intValue || flipbookNumV.intValue != newV)
GUI.changed = true;
if (m_BypassSecurityCheck)
EditorGUILayout.HelpBox("Warning: you are currently bypassing frame count limits. Proceed with caution when entering values, as it can take a long time to process and stall your editor.", MessageType.Warning);
if (EditorGUI.EndChangeCheck())
{
Debug.Log("Updated");
if (newU * newV <= 4096)
{
flipbookNumU.intValue = newU;
flipbookNumV.intValue = newV;
}
else
{
if (!m_BypassSecurityCheck && EditorUtility.DisplayDialog("VFX Toolbox", "CAUTION : You are going to generate a sequence of " + newU * newV + " frames. This could take a long time to process, stall your editor, and consume a large amount of memory. Are you SURE you want to Continue?", "I know what I am doing, proceed", "Cancel"))
m_BypassSecurityCheck = true;
if (m_BypassSecurityCheck)
{
flipbookNumU.intValue = newU;
flipbookNumV.intValue = newV;
}
}
Invalidate();
hasChanged = true;
}
return hasChanged;
}
}
}

113
Editor/ImageSequencer/Serialization/Processors/ColorCorrectionProcessor.cs


using UnityEngine;
using UnityEngine.VFXToolbox;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Color","Color Correction")]
internal class ColorCorrectionProcessor : ProcessorBase
{
[FloatSlider(0.0f,2.0f)]
public float Brightness;
[FloatSlider(0.0f,2.0f)]
public float Contrast;
[FloatSlider(0.0f,2.0f)]
public float Saturation;
public AnimationCurve AlphaCurve;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/ColorCorrection.shader";
public override string processorName => "Color Correction";
public override void Default()
{
Brightness = 1.0f;
Contrast = 1.0f;
Saturation = 1.0f;
DefaultCurve();
}
public void DefaultCurve()
{
AlphaCurve = AnimationCurve.Linear(0, 0, 1, 1);
}
public override bool Process(int frame)
{
if (m_CurveTexture == null)
{
InitTexture();
}
CurveToTextureUtility.CurveToTexture(AlphaCurve, ref m_CurveTexture);
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetFloat("_Brightness", Brightness);
material.SetFloat("_Contrast", Contrast);
material.SetFloat("_Saturation", Saturation);
material.SetTexture("_AlphaCurve", m_CurveTexture);
ProcessFrame(frame, inputFrame);
return true;
}
private void InitTexture()
{
m_CurveTexture = new Texture2D(256, 1, TextureFormat.RGBAHalf, false, true);
m_CurveTexture.wrapMode = TextureWrapMode.Clamp;
m_CurveTexture.filterMode = FilterMode.Bilinear;
CurveToTextureUtility.CurveToTexture(AlphaCurve, ref m_CurveTexture);
}
Texture2D m_CurveTexture;
CurveDrawer m_CurveDrawer;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
if (m_CurveDrawer == null)
{
m_CurveDrawer = new CurveDrawer(null, 0.0f, 1.0f, 0.0f, 1.0f, 140, false);
m_CurveDrawer.AddCurve(serializedObject.FindProperty("AlphaCurve"), new Color(1.0f, 0.55f, 0.1f), "Alpha Curve");
}
if (AlphaCurve == null)
DefaultCurve();
var brightness = serializedObject.FindProperty("Brightness");
var contrast = serializedObject.FindProperty("Contrast");
var saturation = serializedObject.FindProperty("Saturation");
var alphaCurve = serializedObject.FindProperty("AlphaCurve");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(brightness, VFXToolboxGUIUtility.Get("Brightness"));
EditorGUILayout.PropertyField(contrast, VFXToolboxGUIUtility.Get("Contrast"));
EditorGUILayout.PropertyField(saturation, VFXToolboxGUIUtility.Get("Saturation"));
bool curveChanged = false;
using (new GUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(VFXToolboxGUIUtility.Get("Alpha Curve"), GUILayout.Width(EditorGUIUtility.labelWidth));
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Reset")))
{
alphaCurve.animationCurveValue = AnimationCurve.Linear(0, 0, 1, 1);
m_CurveDrawer.ClearSelection();
curveChanged = true;
}
}
if (!curveChanged)
curveChanged = m_CurveDrawer.OnGUILayout();
if (EditorGUI.EndChangeCheck() || curveChanged)
{
Invalidate();
changed = true;
}
return changed;
}
}
}

230
Editor/ImageSequencer/Serialization/Processors/CropProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Common","Crop")]
internal class CropProcessor : ProcessorBase
{
public uint Crop_Top;
public uint Crop_Bottom;
public uint Crop_Left;
public uint Crop_Right;
public float AutoCropThreshold = 0.003f;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Crop.shader";
public override string processorName => "Crop";
public override void UpdateOutputSize()
{
int width = (inputSequenceWidth - (int)Crop_Left) - (int)Crop_Right;
int height = (inputSequenceHeight - (int)Crop_Top) - (int)Crop_Bottom;
SetOutputSize(width, height);
}
public override void Default()
{
Crop_Top = 0;
Crop_Bottom = 0;
Crop_Left = 0;
Crop_Right = 0;
AutoCropThreshold = 0.003f;
}
public override bool Process(int frame)
{
UpdateOutputSize();
Texture texture = RequestInputTexture(frame);
material.SetTexture("_MainTex", texture);
material.SetVector("_CropFactors", new Vector4(
(float)Crop_Left / texture.width,
(float)Crop_Right / texture.width,
(float)Crop_Top / texture.height,
(float)Crop_Bottom / texture.height
));
ProcessFrame(frame, texture);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
int sourceWidth = inputSequenceWidth;
int sourceHeight = inputSequenceHeight;
var crop_top = serializedObject.FindProperty("Crop_Top");
var crop_bottom = serializedObject.FindProperty("Crop_Bottom");
var crop_left = serializedObject.FindProperty("Crop_Left");
var crop_right = serializedObject.FindProperty("Crop_Right");
var threshold = serializedObject.FindProperty("AutoCropThreshold");
EditorGUI.BeginChangeCheck();
EditorGUILayout.IntSlider(crop_top, 0, (sourceHeight / 2) - 1, VFXToolboxGUIUtility.Get("Top"));
EditorGUILayout.IntSlider(crop_bottom, 0, (sourceHeight / 2) - 1, VFXToolboxGUIUtility.Get("Bottom"));
EditorGUILayout.IntSlider(crop_left, 0, (sourceWidth / 2) - 1, VFXToolboxGUIUtility.Get("Left"));
EditorGUILayout.IntSlider(crop_right, 0, (sourceWidth / 2) - 1, VFXToolboxGUIUtility.Get("Right"));
GUILayout.Space(20);
GUILayout.Label(VFXToolboxGUIUtility.Get("Automatic Crop Values"), EditorStyles.boldLabel);
EditorGUI.indentLevel += 2;
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.Slider(threshold, 0.0f, 1.0f, VFXToolboxGUIUtility.Get("Alpha Threshold"));
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Find")))
{
FindProperValues(threshold.floatValue, ref crop_top, ref crop_bottom, ref crop_left, ref crop_right);
GUI.changed = true;
}
}
EditorGUI.indentLevel -= 2;
GUILayout.Space(20);
Rect preview_rect;
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
preview_rect = GUILayoutUtility.GetRect(200, 200);
GUILayout.FlexibleSpace();
}
EditorGUI.DrawRect(preview_rect, new Color(0, 0, 0, 0.1f));
GUI.BeginClip(preview_rect);
GUI.Label(new Rect(0, 0, 200, 16), "Preview");
int top = 40;
int left = 40;
int right = 160;
int bottom = 160;
Handles.color = Color.green;
Handles.DrawLine(new Vector3(left, top), new Vector3(left, bottom));
Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, bottom));
Handles.DrawLine(new Vector3(right, bottom), new Vector3(right, top));
Handles.DrawLine(new Vector3(right, top), new Vector3(left, top));
top = (int)(40 + 120 * (float)crop_top.intValue / sourceHeight);
bottom = (int)(160 - 120 * (float)crop_bottom.intValue / sourceHeight);
left = (int)(40 + 120 * (float)crop_left.intValue / sourceWidth);
right = (int)(160 - 120 * (float)crop_right.intValue / sourceWidth);
Handles.color = Color.red;
Handles.DrawLine(new Vector3(left, top), new Vector3(left, bottom));
Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, bottom));
Handles.DrawLine(new Vector3(right, bottom), new Vector3(right, top));
Handles.DrawLine(new Vector3(right, top), new Vector3(left, top));
GUI.EndClip();
if (EditorGUI.EndChangeCheck())
{
UpdateOutputSize();
Invalidate();
changed = true;
}
return changed;
}
private void FindProperValues(float threshold, ref SerializedProperty top, ref SerializedProperty bottom, ref SerializedProperty left, ref SerializedProperty right)
{
int width = inputSequenceWidth;
int height = inputSequenceHeight;
int minX = width;
int maxX = 0;
int minY = height;
int maxY = 0;
Color[] colors;
RenderTexture tempRT = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
for (int i = 0; i < inputSequenceLength; i++)
{
Texture t = RequestInputTexture(i);
VFXToolboxGUIUtility.DisplayProgressBar("Crop processor", "Evaluating closest bound (Frame #" + i + " on " + inputSequenceLength + "...)", (float)i / inputSequenceLength);
if (!isInputFrameSequence)
{
colors = VFXToolboxUtility.ReadBack(t as RenderTexture);
}
else
{
Graphics.Blit(t, tempRT);
colors = VFXToolboxUtility.ReadBack(tempRT);
}
// Check frame
for (int j = 0; j < colors.Length; j++)
{
int x = j % width;
int y = j / width;
if (colors[j].a >= threshold)
{
minX = Mathf.Min(minX, x);
maxX = Mathf.Max(maxX, x);
minY = Mathf.Min(minY, y);
maxY = Mathf.Max(maxY, y);
}
}
}
VFXToolboxGUIUtility.ClearProgressBar();
bottom.intValue = minY;
top.intValue = height - maxY - 1;
left.intValue = minX;
right.intValue = width - maxX - 1;
RenderTexture.ReleaseTemporary(tempRT);
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
if (Event.current.type != EventType.Repaint)
return false;
Vector2 center = canvas.CanvasToScreen(Vector2.zero);
Vector2 topRight;
Vector2 bottomLeft;
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2 - Crop_Right, canvas.currentFrame.texture.height / 2 + Crop_Top));
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2 + Crop_Left, -canvas.currentFrame.texture.height / 2 - Crop_Bottom));
Handles.DrawSolidRectangleWithOutline(new Rect(topRight, bottomLeft - topRight), Color.clear, canvas.styles.green);
Vector2 topRightCrop;
Vector2 bottomLeftCrop;
topRightCrop = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2));
bottomLeftCrop = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2));
Handles.DrawSolidRectangleWithOutline(new Rect(topRightCrop, bottomLeftCrop - topRightCrop), Color.clear, canvas.styles.red);
// Arrows
Handles.color = canvas.styles.white;
Handles.DrawLine(new Vector3(center.x, topRight.y), new Vector3(center.x, topRightCrop.y));
Handles.DrawLine(new Vector3(center.x, bottomLeft.y), new Vector3(center.x, bottomLeftCrop.y));
Handles.DrawLine(new Vector3(topRight.x, center.y), new Vector3(topRightCrop.x, center.y));
Handles.DrawLine(new Vector3(bottomLeft.x, center.y), new Vector3(bottomLeftCrop.x, center.y));
Handles.color = Color.white;
// Texts
GUI.Label(new Rect(center.x, topRightCrop.y - 16, 64, 16), Crop_Top.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(center.x, bottomLeftCrop.y, 64, 16), Crop_Bottom.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(topRightCrop.x, center.y, 64, 16), Crop_Right.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(bottomLeftCrop.x - 64, center.y, 64, 16), Crop_Left.ToString(), canvas.styles.miniLabelRight);
return false;
}
}
}

84
Editor/ImageSequencer/Serialization/Processors/CustomMaterialProcessor.cs


using UnityEngine;
using UnityEngine.Serialization;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("","Custom Material")]
class CustomMaterialProcessor : ProcessorBase
{
[FormerlySerializedAs("material")]
public Material customMaterial;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Null.shader";
public override string processorName => "Custom Material";
public override string label => $"{processorName} ({((customMaterial == null) ? "Not Set" : customMaterial.name)})";
public override void Default()
{
customMaterial = null;
}
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
if (customMaterial != null)
{
customMaterial.SetTexture("_InputFrame", inputFrame);
customMaterial.SetVector("_FrameData", new Vector4(inputSequenceWidth, inputSequenceHeight, frame, sequenceLength));
customMaterial.SetVector("_FlipbookData", new Vector4(inputSequenceNumU, inputSequenceNumV, 0, 0));
ProcessFrame(frame, inputFrame, customMaterial);
}
else
{
material.SetTexture("_MainTex", inputFrame);
ProcessFrame(frame, inputFrame);
}
return true;
}
private Editor m_MaterialEditor;
private Shader m_CachedShader;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
EditorGUI.BeginChangeCheck();
Material mat = (Material)EditorGUILayout.ObjectField(VFXToolboxGUIUtility.Get("Material"), customMaterial, typeof(Material), false);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(this, "Custom Material Change");
customMaterial = mat;
EditorUtility.SetDirty(this);
Invalidate();
changed = true;
}
if (customMaterial != null)
{
Editor.CreateCachedEditor(customMaterial, typeof(MaterialEditor), ref m_MaterialEditor);
EditorGUI.BeginChangeCheck();
m_MaterialEditor.DrawHeader();
EditorGUIUtility.labelWidth = 120;
m_MaterialEditor.OnInspectorGUI();
if (m_CachedShader != customMaterial.shader)
{
// Hack : we cache shader in order to track changes as DrawHeader does not consider shader change as a EditorGUI.changed
m_CachedShader = customMaterial.shader;
GUI.changed = true;
}
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
}
return changed;
}
}
}

55
Editor/ImageSequencer/Serialization/Processors/DecimateProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Sequence","Decimate")]
internal class DecimateProcessor : ProcessorBase
{
public ushort DecimateBy;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Null.shader";
public override string label => $"{processorName} (1 of {DecimateBy})";
public override string processorName => "Decimate";
public override int sequenceLength => Mathf.Max(1, (int)Mathf.Floor((float)inputSequenceLength / DecimateBy));
public override void Default()
{
DecimateBy = 3;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var decimateBy = serializedObject.FindProperty("DecimateBy");
EditorGUI.BeginChangeCheck();
int newDecimate = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Decimate by"), (int)DecimateBy), 2, inputSequenceLength);
if (newDecimate != decimateBy.intValue)
{
decimateBy.intValue = newDecimate;
}
if (EditorGUI.EndChangeCheck())
{
changed = true;
}
return changed;
}
public override bool Process(int frame)
{
int targetFrame = frame * DecimateBy;
Texture texture = RequestInputTexture(targetFrame);
material.SetTexture("_MainTex", texture);
ProcessFrame(frame, texture);
return true;
}
}
}

101
Editor/ImageSequencer/Serialization/Processors/FadeProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Sequence","Fade")]
internal class FadeProcessor : ProcessorBase
{
public AnimationCurve FadeCurve;
public Color FadeToColor;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Fade.shader";
public override string processorName => "Fade";
public override void Default()
{
FadeCurve = new AnimationCurve();
FadeCurve.AddKey(new Keyframe(0.85f, 1f));
FadeCurve.AddKey(new Keyframe(1f, 0f));
FadeToColor = new Color(0.25f,0.25f,0.25f,0.0f);
}
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetColor("_FadeToColor", FadeToColor);
material.SetFloat("_Ratio", FadeCurve.Evaluate(((float)frame) / sequenceLength));
ProcessFrame(frame, inputFrame);
return true;
}
CurveDrawer m_CurveDrawer;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
if (m_CurveDrawer == null)
{
m_CurveDrawer = new CurveDrawer("Fade Curve", 0.0f, 1.0f, 0.0f, 1.0f, 140, false);
m_CurveDrawer.AddCurve(serializedObject.FindProperty("FadeCurve"), new Color(0.75f, 0.5f, 1.0f), "Fade Curve");
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI;
}
var fadeToColor = serializedObject.FindProperty("FadeToColor");
EditorGUI.BeginChangeCheck();
Color c = EditorGUILayout.ColorField(VFXToolboxGUIUtility.Get("Fade To Color"), fadeToColor.colorValue);
if (c != fadeToColor.colorValue)
{
fadeToColor.colorValue = c;
}
if (m_CurveDrawer.OnGUILayout())
{
changed = true;
}
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
return changed;
}
void OnCurveFieldGUI(Rect renderArea, Rect curveArea)
{
float seqRatio = -1.0f;
if (isCurrentlyPreviewed)
{
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f;
}
// If previewing current sequence : draw trackbar
if (seqRatio >= 0.0f)
{
Handles.color = Color.white;
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax));
}
}
bool CurveEquals(AnimationCurve target)
{
for (int i = 0; i < target.keys.Length; i++)
{
if (target[i].time != FadeCurve[i].time ||
target[i].value != FadeCurve[i].value ||
target[i].inTangent != FadeCurve[i].inTangent ||
target[i].outTangent != FadeCurve[i].outTangent)
{
return false;
}
}
return true;
}
}
}

137
Editor/ImageSequencer/Serialization/Processors/FixBordersProcessor.cs


using UnityEngine;
using UnityEngine.VFXToolbox;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Common","Fix Borders")]
class FixBordersProcessor : ProcessorBase
{
public Vector4 FixFactors;
public Color FadeToColor;
public float FadeToAlpha;
[FloatSlider(0.5f,4.0f)]
public float Exponent;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/FixBorders.shader";
public override string processorName => "Fix Borders";
public override void Default()
{
FixFactors = Vector4.zero;
FadeToColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
FadeToAlpha = 0.0f;
Exponent = 1.5f;
}
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetVector("_FixFactors", FixFactors);
material.SetColor("_FadeToColor", FadeToColor);
material.SetFloat("_FadeToAlpha", FadeToAlpha);
material.SetFloat("_Exponent", Exponent);
ProcessFrame(frame, inputFrame);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var fixFactors = serializedObject.FindProperty("FixFactors");
var fadeToColor = serializedObject.FindProperty("FadeToColor");
var fadeToAlpha = serializedObject.FindProperty("FadeToAlpha");
var exponent = serializedObject.FindProperty("Exponent");
Vector4 value = fixFactors.vector4Value;
EditorGUI.BeginChangeCheck();
float left = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Left"), value.x, 0.0f, 1.0f);
float right = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Right"), value.y, 0.0f, 1.0f);
float top = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Top"), value.z, 0.0f, 1.0f);
float bottom = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Bottom"), value.w, 0.0f, 1.0f);
if (
left != value.x
|| right != value.y
|| top != value.z
|| bottom != value.w
)
{
fixFactors.vector4Value = new Vector4(left, right, top, bottom);
}
Color c = EditorGUILayout.ColorField(new GUIContent("Fade to Color"), fadeToColor.colorValue, true, true, true);
if (c != fadeToColor.colorValue)
{
fadeToColor.colorValue = c;
}
float a = EditorGUILayout.Slider("Fade to Alpha", fadeToAlpha.floatValue, 0.0f, 1.0f);
if (a != fadeToAlpha.floatValue)
{
fadeToAlpha.floatValue = a;
}
EditorGUILayout.PropertyField(exponent, VFXToolboxGUIUtility.Get("Exponent"));
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
return changed;
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
if (Event.current.type != EventType.Repaint)
return false;
Vector2 center = canvas.CanvasToScreen(Vector2.zero);
int width = canvas.currentFrame.texture.width;
int height = canvas.currentFrame.texture.height;
// (left, right, top, bottom)
float left = FixFactors.x * width;
float right = FixFactors.y * width;
float top = FixFactors.z * height;
float bottom = FixFactors.w * height;
Vector2 topRight = canvas.CanvasToScreen(new Vector2(-width / 2, height / 2));
Vector2 bottomLeft = canvas.CanvasToScreen(new Vector2(width / 2, -height / 2));
Vector2 topRightCrop = canvas.CanvasToScreen(new Vector2(-width / 2 + right, height / 2 - top));
Vector2 bottomLeftCrop = canvas.CanvasToScreen(new Vector2(width / 2 - left, -height / 2 + bottom));
// Arrows
Handles.color = canvas.styles.green;
Handles.DrawLine(new Vector3(center.x, topRight.y), new Vector3(center.x, topRightCrop.y));
Handles.DrawLine(new Vector3(center.x, bottomLeft.y), new Vector3(center.x, bottomLeftCrop.y));
Handles.DrawLine(new Vector3(topRight.x, center.y), new Vector3(topRightCrop.x, center.y));
Handles.DrawLine(new Vector3(bottomLeft.x, center.y), new Vector3(bottomLeftCrop.x, center.y));
// Limits
Handles.color = canvas.styles.fadewhite;
Handles.DrawLine(new Vector3(topRight.x, topRightCrop.y), new Vector3(bottomLeft.x, topRightCrop.y));
Handles.DrawLine(new Vector3(topRight.x, bottomLeftCrop.y), new Vector3(bottomLeft.x, bottomLeftCrop.y));
Handles.DrawLine(new Vector3(topRightCrop.x, topRight.y), new Vector3(topRightCrop.x, bottomLeft.y));
Handles.DrawLine(new Vector3(bottomLeftCrop.x, topRight.y), new Vector3(bottomLeftCrop.x, bottomLeft.y));
// Texts
int labelwidth = 36;
GUI.color = canvas.styles.green;
GUI.Label(new Rect(center.x - labelwidth / 2, topRight.y - 20, labelwidth, 16), FixFactors.z.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(center.x - labelwidth / 2, bottomLeft.y + 4, labelwidth, 16), FixFactors.w.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(topRight.x + 4, center.y - 8, labelwidth, 16), FixFactors.y.ToString(), canvas.styles.miniLabel);
GUI.Label(new Rect(bottomLeft.x - labelwidth - 4, center.y - 8, labelwidth, 16), FixFactors.x.ToString(), canvas.styles.miniLabelRight);
Handles.color = Color.white;
GUI.color = Color.white;
return false;
}
}
}

301
Editor/ImageSequencer/Serialization/Processors/LoopingProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Sequence","Loop Sequence")]
class LoopingProcessor : ProcessorBase
{
public AnimationCurve curve;
public int syncFrame;
public int outputSequenceLength;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Blend.shader";
public override string processorName => "Looping";
public override string label => $"{processorName} ({outputSequenceLength} frame(s), Sync : {syncFrame + 1})";
public override int sequenceLength
{
get
{
if (inputSequenceLength > 0)
return outputSequenceLength;
else
return 0;
}
}
public override void Default()
{
curve = new AnimationCurve();
curve.AddKey(0.25f, 0.0f);
curve.AddKey(0.75f, 1.0f);
syncFrame = 25;
outputSequenceLength = 25;
}
public override bool Process(int frame)
{
int inputlength = inputSequenceLength;
int outputlength = sequenceLength;
float t = (float)frame / outputlength;
float blendFactor = Mathf.Clamp(curve.Evaluate(t), 0.0f, 1.0f);
int Prev = Mathf.Clamp((int)Mathf.Ceil(syncFrame + frame), 0, inputlength - 1);
int Next = Mathf.Clamp((int)Mathf.Floor(syncFrame - (outputlength - frame)), 0, inputlength - 1);
Texture prevtex = RequestInputTexture(Prev);
Texture nexttex = RequestInputTexture(Next);
material.SetTexture("_MainTex", prevtex);
material.SetTexture("_AltTex", nexttex);
material.SetFloat("_BlendFactor", blendFactor);
ProcessFrame(frame, prevtex);
return true;
}
CurveDrawer m_CurveDrawer;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var outputSequenceLength = serializedObject.FindProperty("outputSequenceLength");
var syncFrame = serializedObject.FindProperty("syncFrame");
EditorGUI.BeginChangeCheck();
int sync = syncFrame.intValue;
int newSync = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Input Sync Frame|The frame from input sequence that will be used at start and end of the output sequence."), sync, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue);
if (newSync != sync)
{
newSync = Mathf.Clamp(newSync, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue);
syncFrame.intValue = newSync;
}
int length = outputSequenceLength.intValue;
int newlength = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Output Sequence Length|How many frames will be in the output sequence?"), length, 2, (inputSequenceLength / 2) + 1);
if (newlength != length)
{
newlength = Mathf.Min(newlength, Mathf.Max(1, (inputSequenceLength / 2)));
outputSequenceLength.intValue = newlength;
syncFrame.intValue = Mathf.Clamp(syncFrame.intValue, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue);
}
float seqRatio = -1.0f;
if (isCurrentlyPreviewed)
{
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f;
}
// Draw Preview
GUILayout.Label(VFXToolboxGUIUtility.Get("Mix Curve"));
Rect preview_rect;
using (new GUILayout.HorizontalScope())
{
preview_rect = GUILayoutUtility.GetRect(200, 80);
}
EditorGUI.DrawRect(preview_rect, new Color(0.0f, 0.0f, 0.0f, 0.25f));
Rect gradient_rect = new RectOffset(40, 16, 0, 16).Remove(preview_rect);
float width = gradient_rect.width;
float height = gradient_rect.height;
Color topTrackColor = new Color(1.0f, 0.8f, 0.25f);
Color bottomTrackColor = new Color(0.25f, 0.8f, 1.0f);
using (new GUI.ClipScope(preview_rect))
{
GUI.color = topTrackColor;
Handles.color = topTrackColor;
GUI.Label(new Rect(0, 0, 32, 16), "In:", VFXToolboxStyles.miniLabel);
Handles.DrawLine(new Vector3(72, 8), new Vector3(width + 40 - 32, 8));
GUI.color = bottomTrackColor;
Handles.color = bottomTrackColor;
GUI.Label(new Rect(0, height - 16, 32, 16), "In:", VFXToolboxStyles.miniLabel);
Handles.DrawLine(new Vector3(72, height - 8), new Vector3(width + 40 - 32, height - 8));
GUI.color = Color.white;
Handles.color = Color.white;
GUI.Label(new Rect(0, height, 32, 16), "Out:", VFXToolboxStyles.miniLabel);
GUI.Label(new Rect(40, height, 32, 16), "1", VFXToolboxStyles.miniLabel);
GUI.Label(new Rect(width + 40 - 32, height, 32, 16), length.ToString(), VFXToolboxStyles.miniLabelRight);
Handles.DrawLine(new Vector3(72, height + 8), new Vector3(width + 40 - 32, height + 8));
}
AnimationCurve curve = serializedObject.FindProperty("curve").animationCurveValue;
using (new GUI.ClipScope(gradient_rect))
{
int seqLen = this.outputSequenceLength;
int syncF = syncFrame.intValue;
float w = Mathf.Ceil((float)width / seqLen);
for (int i = 0; i < seqLen; i++)
{
float t = (float)i / seqLen;
Color blended = Color.Lerp(bottomTrackColor, topTrackColor, curve.Evaluate(t));
EditorGUI.DrawRect(new Rect(i * w, 18, w, height - 36), blended);
}
GUI.color = topTrackColor;
GUI.Label(new Rect(0, 0, 32, 16), (syncF - seqLen + 1).ToString(), VFXToolboxStyles.miniLabel);
GUI.Label(new Rect(width - 32, 0, 32, 16), (syncF).ToString(), VFXToolboxStyles.miniLabelRight);
GUI.color = bottomTrackColor;
GUI.Label(new Rect(0, height - 16, 32, 16), (syncF + 1).ToString(), VFXToolboxStyles.miniLabel);
GUI.Label(new Rect(width - 32, height - 16, 32, 16), (syncF + seqLen).ToString(), VFXToolboxStyles.miniLabelRight);
GUI.color = Color.white;
}
// If previewing current sequence : draw trackbar
if (seqRatio >= 0.0f)
{
Handles.color = Color.white;
Handles.DrawLine(new Vector3(gradient_rect.xMin + seqRatio * gradient_rect.width, preview_rect.yMin), new Vector3(gradient_rect.xMin + seqRatio * gradient_rect.width, preview_rect.yMax));
}
// Curve Drawer
if (m_CurveDrawer == null)
{
m_CurveDrawer = new CurveDrawer(null, 0.0f, 1.0f, 0.0f, 1.0f, 140, false);
m_CurveDrawer.AddCurve(serializedObject.FindProperty("curve"), new Color(0.5f, 0.75f, 1.0f), "Looping Curve");
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI;
}
if (m_CurveDrawer.OnGUILayout())
{
changed = true;
}
if (EditorGUI.EndChangeCheck())
{
changed = true;
}
if (curve.keys.Length < 2 || curve.keys[0].value > 0.0f || curve.keys[curve.keys.Length - 1].value < 1.0f)
EditorGUILayout.HelpBox("Warning : Mix Curve must have first key's value equal 0 and last key's value equal 1 to achieve looping", MessageType.Warning);
return changed;
}
bool CurveEquals(AnimationCurve target)
{
for (int i = 0; i < target.keys.Length; i++)
{
if (target[i].time != curve[i].time ||
target[i].value != curve[i].value ||
target[i].inTangent != curve[i].inTangent ||
target[i].outTangent != curve[i].outTangent)
{
return false;
}
}
return true;
}
void OnCurveFieldGUI(Rect renderArea, Rect curveArea)
{
float seqRatio = -1.0f;
if (isCurrentlyPreviewed)
{
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f;
}
// If previewing current sequence : draw trackbar
if (seqRatio >= 0.0f)
{
Handles.color = Color.white;
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax));
}
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
int inLength = inputSequenceLength;
int outLength = sequenceLength;
int syncFrame = this.syncFrame;
int outCurIDX = canvas.currentFrameIndex;
float outCurT = (float)outCurIDX / outLength;
int inSeqAIDX = (syncFrame - outLength) + outCurIDX;
int inSeqBIDX = syncFrame + outCurIDX;
AnimationCurve mixCurve = curve;
float mix = mixCurve.Evaluate(outCurT);
Color topTrackColor = canvas.styles.yellow;
Color bottomTrackColor = canvas.styles.cyan;
Vector2 top = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width, canvas.currentFrame.texture.height) / 2);
Rect rect = new Rect((int)top.x + 24, (int)top.y + 8, 260, 280);
EditorGUI.DrawRect(new RectOffset(8, 8, 8, 8).Add(rect), canvas.styles.backgroundPanelColor);
GUILayout.BeginArea(rect);
GUI.color = topTrackColor;
GUILayout.Label("Mix Chunk A (Input Range : " + (syncFrame - outLength + 1).ToString() + "-" + syncFrame.ToString() + ")", canvas.styles.label);
using (new GUILayout.HorizontalScope())
{
Rect imgARect = GUILayoutUtility.GetRect(100, 100);
imgARect.width = 100;
imgARect.height = 100;
GUI.color = Color.white;
#if !UNITY_2018_2_OR_NEWER
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear);
#endif
GUI.DrawTexture(imgARect, RequestInputTexture(inSeqAIDX));
#if !UNITY_2018_2_OR_NEWER
GL.sRGBWrite = false;
#endif
GUI.color = canvas.styles.white;
Handles.DrawSolidRectangleWithOutline(imgARect, Color.clear, topTrackColor);
using (new GUILayout.VerticalScope())
{
GUI.color = topTrackColor;
GUILayout.Label("Frame #" + (inSeqAIDX + 1).ToString(), canvas.styles.miniLabel);
GUILayout.Label("Mixed at : " + (int)(mix * 100) + "%", canvas.styles.miniLabel);
}
}
GUILayout.Space(16);
GUI.color = bottomTrackColor;
GUILayout.Label("Mix Chunk B (Input Range : " + (syncFrame + 1).ToString() + "-" + (syncFrame + outLength).ToString() + ")", canvas.styles.label);
using (new GUILayout.HorizontalScope())
{
Rect imgBRect = GUILayoutUtility.GetRect(100, 100);
imgBRect.width = 100;
imgBRect.height = 100;
GUI.color = Color.white;
#if !UNITY_2018_2_OR_NEWER
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear);
#endif
GUI.DrawTexture(imgBRect, RequestInputTexture(inSeqBIDX));
#if !UNITY_2018_2_OR_NEWER
GL.sRGBWrite = false;
#endif
GUI.color = canvas.styles.white;
Handles.DrawSolidRectangleWithOutline(imgBRect, Color.clear, bottomTrackColor);
using (new GUILayout.VerticalScope())
{
GUI.color = bottomTrackColor;
GUILayout.Label("Frame #" + (inSeqBIDX + 1).ToString(), canvas.styles.miniLabel);
GUILayout.Label("Mixed at : " + (int)((1.0f - mix) * 100) + "%", canvas.styles.miniLabel);
}
}
GUI.color = Color.white;
GUILayout.EndArea();
return false;
}
}
}

50
Editor/ImageSequencer/Serialization/Processors/PremultiplyAlphaProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Color","Premultiply Alpha")]
class PremultiplyAlphaProcessor : ProcessorBase
{
public bool RemoveAlpha;
public float AlphaValue;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/PremultiplyAlpha.shader";
public override string processorName => "Premultiply Alpha";
public override void Default()
{
RemoveAlpha = false;
AlphaValue = 1.0f;
}
public override bool Process(int frame)
{
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetInt("_RemoveAlpha", RemoveAlpha ? 1 : 0);
material.SetFloat("_AlphaValue", AlphaValue);
ProcessFrame(frame, inputFrame);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var removeAlpha = serializedObject.FindProperty("RemoveAlpha");
var alphaValue = serializedObject.FindProperty("AlphaValue");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(removeAlpha, VFXToolboxGUIUtility.Get("Remove Alpha"));
EditorGUI.BeginDisabledGroup(!removeAlpha.boolValue);
EditorGUILayout.PropertyField(alphaValue, VFXToolboxGUIUtility.Get("Alpha Value"));
EditorGUI.EndDisabledGroup();
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
return changed;
}
}
}

85
Editor/ImageSequencer/Serialization/Processors/RemapColorProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Color","Remap Color")]
class RemapColorProcessor: ProcessorBase
{
public enum RemapColorSource
{
sRGBLuminance = 0,
LinearRGBLuminance = 1,
Alpha = 2,
LinearR = 3,
LinearG = 4,
LinearB = 5
}
public Gradient Gradient;
public RemapColorSource ColorSource;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/RemapColor.shader";
public override string processorName => "Remap Color";
public override void Default()
{
ColorSource = RemapColorSource.sRGBLuminance;
DefaultGradient();
}
public void DefaultGradient()
{
Gradient = new Gradient();
GradientColorKey[] colors = new GradientColorKey[2] { new GradientColorKey(Color.black, 0),new GradientColorKey(Color.white, 1) };
GradientAlphaKey[] alpha = new GradientAlphaKey[2] { new GradientAlphaKey(0,0), new GradientAlphaKey(1,1) };
Gradient.SetKeys(colors, alpha);
}
public override bool Process(int frame)
{
if (m_GradientTexture == null)
{
InitTexture();
}
CurveToTextureUtility.GradientToTexture(Gradient, ref m_GradientTexture);
Texture inputFrame = RequestInputTexture(frame);
material.SetTexture("_MainTex", inputFrame);
material.SetFloat("_Mode", (int)ColorSource);
material.SetTexture("_Gradient", m_GradientTexture);
ProcessFrame(frame, inputFrame);
return true;
}
Texture2D m_GradientTexture;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var colorSource = serializedObject.FindProperty("ColorSource");
var gradient = serializedObject.FindProperty("Gradient");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(colorSource, VFXToolboxGUIUtility.Get("Color Source"));
EditorGUILayout.PropertyField(gradient, VFXToolboxGUIUtility.Get("Remap Gradient"));
if (EditorGUI.EndChangeCheck())
{
Invalidate();
changed = true;
}
return changed;
}
private void InitTexture()
{
m_GradientTexture = new Texture2D(256, 1, TextureFormat.RGBA32, false, false);
m_GradientTexture.wrapMode = TextureWrapMode.Clamp;
m_GradientTexture.filterMode = FilterMode.Bilinear;
CurveToTextureUtility.GradientToTexture(Gradient, ref m_GradientTexture);
}
}
}

80
Editor/ImageSequencer/Serialization/Processors/RemoveBackgroundProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Color","Remove Background")]
class RemoveBackgroundProcessor : ProcessorBase
{
public Color BackgroundColor;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Unblend.shader";
public override string processorName => "Remove Background";
public override void Default()
{
BackgroundColor = new Color(0.25f,0.25f,0.25f,0.0f);
}
public override bool Process(int frame)
{
Texture tex = RequestInputTexture(frame);
SetOutputSize(tex.width, tex.height);
material.SetTexture("_MainTex", tex);
material.SetColor("_BackgroundColor", BackgroundColor);
ProcessFrame(frame, tex);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var bgColor = serializedObject.FindProperty("BackgroundColor");
EditorGUI.BeginChangeCheck();
using (new GUILayout.HorizontalScope())
{
EditorGUILayout.PropertyField(bgColor, VFXToolboxGUIUtility.Get("Background Color"));
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Grab"), GUILayout.Width(40)))
{
if (inputSequenceLength > 0)
{
var texture = RequestInputTexture(0);
Color background;
if (texture is RenderTexture)
{
background = VFXToolboxUtility.ReadBack((RenderTexture)texture)[0];
}
else
{
Texture2D inputFrame = (Texture2D)texture;
RenderTexture rt = RenderTexture.GetTemporary(inputFrame.width, inputFrame.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
Graphics.Blit(inputFrame, rt);
background = VFXToolboxUtility.ReadBack(rt)[0];
RenderTexture.ReleaseTemporary(rt);
}
if (QualitySettings.activeColorSpace == ColorSpace.Linear)
background = background.gamma;
bgColor.colorValue = background;
}
}
}
if (EditorGUI.EndChangeCheck())
{
UpdateOutputSize();
Invalidate();
changed = true;
}
GUILayout.Space(20);
EditorGUILayout.HelpBox("Please select a color corresponding to the solid background of the flipbook to try to reconstruct the pixel's color. \n\nThis filter will only work if your flipbook was rendered on a solid color background. Try the Grab button to fetch the upper left pixel of the first frame, or use the color picker.", MessageType.Info);
return changed;
}
}
}

144
Editor/ImageSequencer/Serialization/Processors/ResizeProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Common","Resize")]
internal class ResizeProcessor : ProcessorBase
{
public ushort Width;
public ushort Height;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Resize.shader";
public override string processorName => "Resize";
public override string label => $"{processorName} ({Width}x{Height})";
public override void Default()
{
Width = 256;
Height = 256;
}
public override void UpdateOutputSize()
{
SetOutputSize(Width, Height);
}
public override bool Process(int frame)
{
Texture texture = RequestInputTexture(frame);
Vector4 kernelAndSize = new Vector4((float)texture.width / (float)Width, (float)texture.height / (float)Height, (float)Width, (float)Height);
material.SetTexture("_MainTex", texture);
material.SetVector("_KernelAndSize", kernelAndSize);
ProcessFrame(frame, texture);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var width = serializedObject.FindProperty("Width");
var height = serializedObject.FindProperty("Height");
EditorGUI.BeginChangeCheck();
using (new GUILayout.HorizontalScope())
{
int w = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Width"), width.intValue), 1, 8192);
if (GUILayout.Button("", EditorStyles.popup, GUILayout.Width(16)))
{
GenericMenu menu = new GenericMenu();
for (int s = 8192; s >= 16; s /= 2)
{
menu.AddItem(VFXToolboxGUIUtility.Get(s.ToString()), false,
(o) => {
serializedObject.Update();
var out_width = serializedObject.FindProperty("Width");
out_width.intValue = (int)o;
serializedObject.ApplyModifiedProperties();
Invalidate();
UpdateOutputSize();
}, s);
}
menu.ShowAsContext();
}
if (w != width.intValue)
{
width.intValue = w;
}
}
using (new GUILayout.HorizontalScope())
{
int h = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Height"), height.intValue), 1, 8192);
if (GUILayout.Button("", EditorStyles.popup, GUILayout.Width(16)))
{
GenericMenu menu = new GenericMenu();
for (int s = 8192; s >= 16; s /= 2)
{
menu.AddItem(VFXToolboxGUIUtility.Get(s.ToString()), false, (o) => {
serializedObject.Update();
var out_height = serializedObject.FindProperty("Height");
out_height.intValue = (int)o;
serializedObject.ApplyModifiedProperties();
Invalidate();
UpdateOutputSize();
}, s);
}
menu.ShowAsContext();
}
if (h != height.intValue)
{
height.intValue = h;
}
}
if (Mathf.Log(height.intValue, 2) % 1.0f != 0 || Mathf.Log(width.intValue, 2) % 1.0f != 0)
{
EditorGUILayout.HelpBox("Warning: your resize resolution is not a power of two.", MessageType.Warning);
}
if (EditorGUI.EndChangeCheck())
{
UpdateOutputSize();
Invalidate();
changed = true;
}
return changed;
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
if (Event.current.type != EventType.Repaint)
return false;
Vector2 center = canvas.CanvasToScreen(Vector2.zero);
Vector2 topRight;
Vector2 bottomLeft;
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2));
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2));
// Arrows
Handles.color = canvas.styles.green;
Handles.DrawLine(new Vector3(topRight.x, topRight.y - 16), new Vector3(bottomLeft.x, topRight.y - 16));
Handles.DrawLine(new Vector3(bottomLeft.x - 16, topRight.y), new Vector3(bottomLeft.x - 16, bottomLeft.y));
Handles.color = Color.white;
// Texts
GUI.color = Color.green;
GUI.Label(new Rect(center.x - 32, topRight.y - 32, 64, 16), Width.ToString(), canvas.styles.miniLabelCenter);
VFXToolboxGUIUtility.GUIRotatedLabel(new Rect(bottomLeft.x - 48, center.y - 8, 64, 16), Height.ToString(), -90.0f, canvas.styles.miniLabelCenter);
GUI.color = Color.white;
return false;
}
}
}

140
Editor/ImageSequencer/Serialization/Processors/RetimeProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Sequence","Retime")]
class RetimeProcessor : ProcessorBase
{
public AnimationCurve curve;
public int outputSequenceLength;
public bool useCurve;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Blend.shader";
public override string processorName => "Retime";
public override string label => $"{processorName} ({outputSequenceLength} frames)";
public override int sequenceLength
{
get
{
if (inputSequenceLength > 0)
return outputSequenceLength;
else
return 0;
}
}
public override void Default()
{
curve = new AnimationCurve();
curve.AddKey(new Keyframe(0, 0));
curve.AddKey(new Keyframe(1, 24));
outputSequenceLength = 25;
useCurve = true;
}
public override bool Process(int frame)
{
int inputlength = inputSequenceLength;
int outputlength = sequenceLength;
float t = (float)frame / outputlength;
float Frame;
if (useCurve)
Frame = curve.Evaluate(t);
else
Frame = t * inputlength;
float blendFactor = Frame % 1.0f;
int Prev = Mathf.Clamp((int)Mathf.Floor(Frame), 0, inputlength - 1);
int Next = Mathf.Clamp((int)Mathf.Ceil(Frame), 0, inputlength - 1);
Texture prevtex = RequestInputTexture(Prev);
Texture nexttex = RequestInputTexture(Next);
material.SetTexture("_MainTex", prevtex);
material.SetTexture("_AltTex", nexttex);
material.SetFloat("_BlendFactor", blendFactor);
ProcessFrame(frame, prevtex);
return true;
}
CurveDrawer m_CurveDrawer;
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
if (m_CurveDrawer == null)
{
m_CurveDrawer = new CurveDrawer("Retime Curve", 0.0f, 1.0f, 0.0f, inputSequenceLength, 140, false);
m_CurveDrawer.AddCurve(serializedObject.FindProperty("curve"), new Color(0.5f, 0.75f, 1.0f), "Retime Curve");
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI;
}
var useCurve = serializedObject.FindProperty("useCurve");
var outputSequenceLength = serializedObject.FindProperty("outputSequenceLength");
EditorGUI.BeginChangeCheck();
int length = outputSequenceLength.intValue;
int newlength = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Sequence Length"), length, 1, inputSequenceLength);
if (newlength != length)
{
outputSequenceLength.intValue = newlength;
}
EditorGUILayout.PropertyField(useCurve, VFXToolboxGUIUtility.Get("Use Retiming Curve"));
if (useCurve.boolValue)
{
m_CurveDrawer.SetBounds(new Rect(0, 0, 1, inputSequenceLength - 1));
if (m_CurveDrawer.OnGUILayout())
{
changed = true;
}
}
if (EditorGUI.EndChangeCheck())
{
changed = true;
}
return changed;
}
void OnCurveFieldGUI(Rect renderArea, Rect curveArea)
{
float seqRatio = -1.0f;
if (isCurrentlyPreviewed)
{
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f;
}
// If previewing current sequence : draw trackbar
if (seqRatio >= 0.0f)
{
Handles.color = Color.white;
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax));
}
}
bool CurveEquals(AnimationCurve target)
{
for (int i = 0; i < target.keys.Length; i++)
{
if (target[i].time != curve[i].time ||
target[i].value != curve[i].value ||
target[i].inTangent != curve[i].inTangent ||
target[i].outTangent != curve[i].outTangent)
{
return false;
}
}
return true;
}
}
}

77
Editor/ImageSequencer/Serialization/Processors/RotateProcessor.cs


using UnityEngine;
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer
{
[Processor("Common","Rotate")]
internal class RotateProcessor : ProcessorBase
{
public enum RotateMode
{
None = 0,
Rotate90 = 1,
Rotate180 = 2,
Rotate270 = 3
}
public RotateMode FrameRotateMode;
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Rotate.shader";
public override string processorName => "Rotate";
public override string label => $"{processorName} ({FrameRotateMode})";
public override int numU => (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180) ? base.numU : base.numV;
public override int numV => (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180) ? base.numV : base.numU;
public override void UpdateOutputSize()
{
if (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180)
SetOutputSize(inputSequenceWidth, inputSequenceHeight);
else
SetOutputSize(inputSequenceHeight, inputSequenceWidth);
}
public override void Default()
{
FrameRotateMode = 0;
}
public override bool Process(int frame)
{
UpdateOutputSize();
Texture texture = RequestInputTexture(frame);
material.SetTexture("_MainTex", texture);
material.SetInt("_Mode", (int)FrameRotateMode);
ProcessFrame(frame, texture);
return true;
}
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject)
{
var rotatemode = serializedObject.FindProperty("FrameRotateMode");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(rotatemode, VFXToolboxGUIUtility.Get("Rotation Mode"));
if (EditorGUI.EndChangeCheck())
{
UpdateOutputSize();
Invalidate();
changed = true;
}
return changed;
}
public override bool OnCanvasGUI(ImageSequencerCanvas canvas)
{
Vector2 pos = canvas.CanvasToScreen(Vector2.zero + (new Vector2(canvas.currentFrame.texture.width, canvas.currentFrame.texture.height) / 2));
Rect r = new Rect(pos.x, pos.y - 16, 150, 16);
GUI.Label(r, VFXToolboxGUIUtility.Get($"Rotation : {ObjectNames.NicifyVariableName(FrameRotateMode.ToString())}"));
return false;
}
}
}

4
Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png

之前 之后
宽度: 16  |  高度: 16  |  大小: 1.9 KiB

103
Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png.meta


fileFormatVersion: 2
guid: 39609299c2088084c8e7f2837973b90b
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 10
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

7
Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png

之前 之后
宽度: 16  |  高度: 16  |  大小: 1.2 KiB

103
Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png.meta


fileFormatVersion: 2
guid: a4b87311d85ae7d4a8800739c0a7b012
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 10
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

9
Editor/Examples.meta


fileFormatVersion: 2
guid: cb01cd6b53c5afb4e932d16ea337934c
folderAsset: yes
timeCreated: 1475064740
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

部分文件因为文件数量过多而无法显示

正在加载...
取消
保存