peeweek
6 年前
当前提交
515c9969
共有 227 个文件被更改,包括 7560 次插入 和 1 次删除
-
8README.md
-
5LICENSE.md
-
9com.unity.vfx-toolbox/Editor.meta
-
9com.unity.vfx-toolbox/Editor/Canvas.meta
-
9com.unity.vfx-toolbox/Editor/Canvas/Shaders.meta
-
69com.unity.vfx-toolbox/Editor/Canvas/Shaders/VFXToolboxCanvas.shader
-
9com.unity.vfx-toolbox/Editor/Canvas/Shaders/VFXToolboxCanvas.shader.meta
-
638com.unity.vfx-toolbox/Editor/Canvas/VFXToolboxCanvas.cs
-
12com.unity.vfx-toolbox/Editor/Canvas/VFXToolboxCanvas.cs.meta
-
9com.unity.vfx-toolbox/Editor/Examples.meta
-
73com.unity.vfx-toolbox/Editor/Examples/ImageSequencerSourcePostprocessor.cs
-
12com.unity.vfx-toolbox/Editor/Examples/ImageSequencerSourcePostprocessor.cs.meta
-
9com.unity.vfx-toolbox/Editor/External.meta
-
860com.unity.vfx-toolbox/Editor/External/CurveEditor.cs
-
12com.unity.vfx-toolbox/Editor/External/CurveEditor.cs.meta
-
9com.unity.vfx-toolbox/Editor/Resources.meta
-
34com.unity.vfx-toolbox/Editor/Resources/MissingTexture.png
-
76com.unity.vfx-toolbox/Editor/Resources/MissingTexture.png.meta
-
9com.unity.vfx-toolbox/Editor/Utility.meta
-
275com.unity.vfx-toolbox/Editor/Utility/CurveDrawer.cs
-
12com.unity.vfx-toolbox/Editor/Utility/CurveDrawer.cs.meta
-
59com.unity.vfx-toolbox/Editor/Utility/CurveToTextureUtility.cs
-
12com.unity.vfx-toolbox/Editor/Utility/CurveToTextureUtility.cs.meta
-
699com.unity.vfx-toolbox/Editor/Utility/FilterPopupWindow.cs
-
12com.unity.vfx-toolbox/Editor/Utility/FilterPopupWindow.cs.meta
-
21com.unity.vfx-toolbox/Editor/Utility/FloatSliderPropertyDrawer.cs
-
12com.unity.vfx-toolbox/Editor/Utility/FloatSliderPropertyDrawer.cs.meta
-
9com.unity.vfx-toolbox/Editor/Utility/Shaders.meta
-
56com.unity.vfx-toolbox/Editor/Utility/Shaders/BlitRect.shader
-
9com.unity.vfx-toolbox/Editor/Utility/Shaders/BlitRect.shader.meta
-
108com.unity.vfx-toolbox/Editor/Utility/Splitter.cs
-
12com.unity.vfx-toolbox/Editor/Utility/Splitter.cs.meta
-
210com.unity.vfx-toolbox/Editor/Utility/VFXToolboxGUIUtility.cs
-
12com.unity.vfx-toolbox/Editor/Utility/VFXToolboxGUIUtility.cs.meta
-
80com.unity.vfx-toolbox/Editor/Utility/VFXToolboxStyles.cs
-
12com.unity.vfx-toolbox/Editor/Utility/VFXToolboxStyles.cs.meta
-
116com.unity.vfx-toolbox/Editor/Utility/VFXToolboxUtility.cs
-
12com.unity.vfx-toolbox/Editor/Utility/VFXToolboxUtility.cs.meta
-
9com.unity.vfx-toolbox/ImageSequencer.meta
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders.meta
-
97com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Blur.shader
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Blur.shader.meta
-
140com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/HeightToNormal.shader
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/HeightToNormal.shader.meta
-
100com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/LightNormals.shader
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/LightNormals.shader.meta
-
68com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Negative.shader
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Negative.shader.meta
-
89com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Rotate.shader
-
9com.unity.vfx-toolbox/ImageSequencer/CustomMaterialShaders/Rotate.shader.meta
-
9com.unity.vfx-toolbox/ImageSequencer/Editor.meta
-
9com.unity.vfx-toolbox/ImageSequencer/Editor/Attributes.meta
-
20com.unity.vfx-toolbox/ImageSequencer/Editor/Attributes/ProcessorAttribute.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/Attributes/ProcessorAttribute.cs.meta
-
8com.unity.vfx-toolbox/ImageSequencer/Editor/EditorResources.meta
-
4com.unity.vfx-toolbox/ImageSequencer/Editor/EditorResources/ImageSequence Icon.png
-
92com.unity.vfx-toolbox/ImageSequencer/Editor/EditorResources/ImageSequence Icon.png.meta
-
9com.unity.vfx-toolbox/ImageSequencer/Editor/Exporters.meta
-
477com.unity.vfx-toolbox/ImageSequencer/Editor/Exporters/MiniEXR.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/Exporters/MiniEXR.cs.meta
-
65com.unity.vfx-toolbox/ImageSequencer/Editor/Exporters/MiniTGA.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/Exporters/MiniTGA.cs.meta
-
9com.unity.vfx-toolbox/ImageSequencer/Editor/FilterPopup.meta
-
121com.unity.vfx-toolbox/ImageSequencer/Editor/FilterPopup/ProcessorDataProvider.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/FilterPopup/ProcessorDataProvider.cs.meta
-
278com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessor.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessor.cs.meta
-
158com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessorStack.Serialization.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessorStack.Serialization.cs.meta
-
143com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessorStack.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/FrameProcessorStack.cs.meta
-
54com.unity.vfx-toolbox/ImageSequencer/Editor/GPUFrameProcessor.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/GPUFrameProcessor.cs.meta
-
218com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequenceAssetEditor.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequenceAssetEditor.cs.meta
-
52com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequenceAssetFactory.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequenceAssetFactory.cs.meta
-
357com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.Export.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.Export.cs.meta
-
779com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.GUI.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.GUI.cs.meta
-
192com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.InputFrames.cs
-
12com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.InputFrames.cs.meta
-
185com.unity.vfx-toolbox/ImageSequencer/Editor/ImageSequencer.Processors.cs
|
|||
# VFXToolbox |
|||
VFX Tools |
|||
Additional tools for Visual Effect Artists. |
|||
|
|||
## Image Sequencer |
|||
|
|||
This utility enables the authoring of Flipbook Texture Sheets in an easy way. Create Image Sequence assets, import your texture sequences and start retiming, loop, and assemble into a flipbook texture sheet. |
|||
|
|||
By using templates, you can go back, make adjustments then re-export with only one click. |
|
|||
Copyright © 2018 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). |
|||
|
|||
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. |
|
|||
fileFormatVersion: 2 |
|||
guid: b78adc1efd7b8084ebcc10daf51ab527 |
|||
folderAsset: yes |
|||
timeCreated: 1471966525 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 32d359bec3922dd42bb2ec1e37f0d997 |
|||
folderAsset: yes |
|||
timeCreated: 1475496311 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 7ff2e73f8967b4146ba5168de55dfe65 |
|||
folderAsset: yes |
|||
timeCreated: 1475496541 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/VFXToolbox/ImageSequencer/ImageSequencerCanvas" |
|||
{ |
|||
Properties |
|||
{ |
|||
_MainTex ("Texture", 2D) = "white" {} |
|||
_RGBAMask("RGBAMask", Color) = (1.0,1.0,1.0,1.0) |
|||
_MipMap("MipMap", float) = 0.0 |
|||
} |
|||
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; |
|||
}; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
sampler2D _MainTex; |
|||
float4 _RGBAMask; |
|||
float _MipMap; |
|||
|
|||
fixed4 frag (v2f i) : SV_Target |
|||
{ |
|||
fixed4 col = tex2Dlod(_MainTex, float4(i.uv,0.0f,_MipMap)); |
|||
|
|||
if(_RGBAMask.r + _RGBAMask.g + _RGBAMask.b == 0.0f) |
|||
{ |
|||
// As we preview alpha, we need to linearize |
|||
float a = GammaToLinearSpaceExact(col.a); |
|||
col = float4(a,a,a,1.0f); |
|||
} |
|||
else |
|||
{ |
|||
if(_RGBAMask.a == 0.0f) |
|||
{ |
|||
col.a = 1.0f; |
|||
} |
|||
col.rgb *= _RGBAMask.rgb; |
|||
} |
|||
return col; |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 327f9df1e15b2fa48ba14251e55689e2 |
|||
timeCreated: 1471965342 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public abstract class VFXToolboxCanvas |
|||
{ |
|||
public Rect displayRect |
|||
{ |
|||
get { return m_Rect; } |
|||
set { m_Rect = value; } |
|||
} |
|||
|
|||
public bool maskR |
|||
{ |
|||
get { return m_Material.GetColor("_RGBAMask").r == 1.0f; } |
|||
set |
|||
{ |
|||
Color c = m_Material.GetColor("_RGBAMask"); |
|||
if (value) c.r = 1.0f; else c.r = 0.0f; |
|||
m_Material.SetColor("_RGBAMask", c); |
|||
Invalidate(true); |
|||
} |
|||
} |
|||
|
|||
public bool maskG |
|||
{ |
|||
get { return m_Material.GetColor("_RGBAMask").g == 1.0f; } |
|||
set |
|||
{ |
|||
Color c = m_Material.GetColor("_RGBAMask"); |
|||
if (value) c.g = 1.0f; else c.g = 0.0f; |
|||
m_Material.SetColor("_RGBAMask", c); |
|||
Invalidate(true); |
|||
} |
|||
} |
|||
|
|||
public bool maskB |
|||
{ |
|||
get { return m_Material.GetColor("_RGBAMask").b == 1.0f; } |
|||
set |
|||
{ |
|||
Color c = m_Material.GetColor("_RGBAMask"); |
|||
if (value) c.b = 1.0f; else c.b = 0.0f; |
|||
m_Material.SetColor("_RGBAMask", c); |
|||
Invalidate(true); |
|||
} |
|||
|
|||
} |
|||
|
|||
public bool maskA |
|||
{ |
|||
get { return m_Material.GetColor("_RGBAMask").a == 1.0f; } |
|||
set |
|||
{ |
|||
Color c = m_Material.GetColor("_RGBAMask"); |
|||
if (value) c.a = 1.0f; else c.a = 0.0f; |
|||
m_Material.SetColor("_RGBAMask", c); |
|||
Invalidate(true); |
|||
} |
|||
} |
|||
|
|||
public bool filter |
|||
{ |
|||
get { return m_bFilter; } |
|||
set { |
|||
if(m_bFilter!=value) |
|||
{ |
|||
m_bFilter = value; |
|||
InvalidateRenderTarget(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
public int mipMap |
|||
{ |
|||
get { |
|||
return m_MipMap; |
|||
} |
|||
set { |
|||
if(m_MipMap != value) |
|||
{ |
|||
m_MipMap = value; |
|||
m_Material.SetFloat("_MipMap", (float)value); |
|||
InvalidateRenderTarget(); |
|||
} |
|||
else |
|||
{ |
|||
m_MipMap = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public int mipMapCount |
|||
{ |
|||
get { return GetMipMapCount(); } |
|||
} |
|||
|
|||
public bool showGrid |
|||
{ |
|||
get |
|||
{ |
|||
return m_bShowGrid; |
|||
} |
|||
set |
|||
{ |
|||
m_bShowGrid = value; |
|||
} |
|||
} |
|||
|
|||
public float BackgroundBrightness |
|||
{ |
|||
get |
|||
{ |
|||
return m_bgBrightness; |
|||
} |
|||
set |
|||
{ |
|||
SetBGBrightness(value); |
|||
} |
|||
} |
|||
|
|||
public Styles styles |
|||
{ |
|||
get |
|||
{ |
|||
if (m_Styles == null) |
|||
m_Styles = new Styles(this); |
|||
return m_Styles; |
|||
} |
|||
} |
|||
|
|||
public float zoom |
|||
{ |
|||
get |
|||
{ |
|||
return m_Zoom; |
|||
} |
|||
set |
|||
{ |
|||
m_Zoom = value; |
|||
Invalidate(false); |
|||
} |
|||
} |
|||
|
|||
private Vector2 m_CameraPosition = Vector2.zero; |
|||
private float m_Zoom = 1.0f; |
|||
private bool m_DragPreview = false; |
|||
private bool m_ZoomPreview = false; |
|||
private Vector2 m_ZoomPreviewCenter; |
|||
private Vector2 m_PreviousMousePosition; |
|||
|
|||
private Styles m_Styles; |
|||
|
|||
private Vector2 m_ZoomMinMax = new Vector2(0.2f, 10.0f); |
|||
private bool m_bFilter = true; |
|||
private int m_MipMap = 0; |
|||
private bool m_bShowGrid = true; |
|||
|
|||
protected Rect m_Rect; |
|||
|
|||
private Shader m_Shader; |
|||
private Material m_Material; |
|||
private RenderTexture m_RenderTexture; |
|||
|
|||
private bool m_IsDirtyRenderTarget; |
|||
private bool m_bNeedRedraw; |
|||
private float m_bgBrightness = -1.0f; |
|||
|
|||
public Texture texture |
|||
{ |
|||
get { return GetTexture(); } |
|||
set { SetTexture(value); } |
|||
} |
|||
private Texture m_Texture; |
|||
|
|||
public CanvasGUIDelegate onCanvasGUI; |
|||
public delegate void CanvasGUIDelegate(); |
|||
|
|||
public VFXToolboxCanvas(Rect displayRect, string shaderName = "Packages/com.unity.vfx-toolbox/Editor/Canvas/Shaders/VFXToolboxCanvas.shader") |
|||
{ |
|||
m_Rect = displayRect; |
|||
|
|||
m_IsDirtyRenderTarget = true; |
|||
m_bNeedRedraw = true; |
|||
m_Shader = AssetDatabase.LoadAssetAtPath<Shader>(shaderName); |
|||
m_Material = new Material(m_Shader) { hideFlags = HideFlags.DontSave }; |
|||
m_RenderTexture = RenderTexture.GetTemporary(1,1,0); |
|||
} |
|||
|
|||
public virtual void Invalidate(bool needRedraw) |
|||
{ |
|||
m_bNeedRedraw = m_bNeedRedraw | needRedraw; |
|||
} |
|||
|
|||
protected abstract void SetTexture(Texture tex); |
|||
protected abstract Texture GetTexture(); |
|||
|
|||
protected virtual int GetMipMapCount() |
|||
{ |
|||
if (texture != null) |
|||
{ |
|||
if (texture is Texture2D) |
|||
{ |
|||
return (texture as Texture2D).mipmapCount; |
|||
} |
|||
else |
|||
return 0; |
|||
} |
|||
else |
|||
return 0; |
|||
} |
|||
|
|||
public void InvalidateRenderTarget() |
|||
{ |
|||
m_IsDirtyRenderTarget = true; |
|||
} |
|||
|
|||
private 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))); |
|||
|
|||
if(m_RenderTexture.width != width || m_RenderTexture.height != height) |
|||
{ |
|||
RenderTexture.ReleaseTemporary(m_RenderTexture); |
|||
m_RenderTexture = RenderTexture.GetTemporary(width,height,0,RenderTextureFormat.ARGBHalf); |
|||
} |
|||
|
|||
if (filter) |
|||
m_RenderTexture.filterMode = FilterMode.Bilinear; |
|||
else |
|||
m_RenderTexture.filterMode = FilterMode.Point; |
|||
|
|||
m_IsDirtyRenderTarget = false; |
|||
Invalidate(true); |
|||
} |
|||
|
|||
public void Recenter(bool Refit) |
|||
{ |
|||
m_CameraPosition = Vector2.zero; |
|||
if(Refit) |
|||
{ |
|||
float hZoom = (m_Rect.height - 70) / texture.height; |
|||
float wZoom = (m_Rect.width - 70) / texture.width; |
|||
|
|||
m_Zoom = Mathf.Min(hZoom, wZoom); |
|||
} |
|||
else |
|||
{ |
|||
m_Zoom = 1.0f; |
|||
} |
|||
} |
|||
|
|||
private void Zoom(float ZoomDelta, Vector2 zoomCenter) |
|||
{ |
|||
Vector2 centerPos = - new Vector2(zoomCenter.x - m_Rect.width / 2, zoomCenter.y - m_Rect.height / 2) - m_CameraPosition; |
|||
|
|||
float prevZoom = m_Zoom; |
|||
|
|||
m_Zoom -= ZoomDelta; |
|||
if(m_Zoom < m_ZoomMinMax.x) |
|||
m_Zoom = m_ZoomMinMax.x; |
|||
else if(m_Zoom > m_ZoomMinMax.y) |
|||
m_Zoom = m_ZoomMinMax.y; |
|||
else |
|||
{ |
|||
m_CameraPosition += centerPos - ((m_Zoom / prevZoom) * centerPos); |
|||
} |
|||
} |
|||
|
|||
protected virtual void HandleKeyboardEvents() |
|||
{ |
|||
if(Event.current.type == EventType.KeyDown) |
|||
{ |
|||
switch(Event.current.keyCode) |
|||
{ |
|||
// Viewport Toggles
|
|||
case KeyCode.F: |
|||
Recenter(!Event.current.shift); |
|||
break; |
|||
case KeyCode.G: |
|||
showGrid = !showGrid; |
|||
break; |
|||
case KeyCode.J: |
|||
filter = !filter; |
|||
Invalidate(true); |
|||
break; |
|||
// Brightness Control
|
|||
case KeyCode.V: |
|||
BrightnessDown(0.1f); |
|||
break; |
|||
case KeyCode.B: |
|||
ResetBrightness(); |
|||
break; |
|||
case KeyCode.N: |
|||
BrightnessUp(0.1f); |
|||
break; |
|||
default: |
|||
return; // Return without using event.
|
|||
} |
|||
Invalidate(false); |
|||
Event.current.Use(); |
|||
} |
|||
} |
|||
|
|||
private void DrawCurrentTexture() |
|||
{ |
|||
Rect rect = new Rect(0, 0, m_Rect.width, m_Rect.height); |
|||
|
|||
// Pan : use Middle Mouse button or Alt+Click
|
|||
if(Event.current.type == EventType.MouseDown && (Event.current.button == 2 || (Event.current.button == 0 && Event.current.alt))) |
|||
{ |
|||
m_DragPreview = true; |
|||
} |
|||
|
|||
if((Event.current.rawType == EventType.MouseUp || Event.current.rawType == EventType.DragExited) && (Event.current.button == 2 || Event.current.button == 0)) |
|||
{ |
|||
m_DragPreview = false; |
|||
Invalidate(false); |
|||
} |
|||
|
|||
if((!m_DragPreview && Event.current.alt) || m_DragPreview) |
|||
{ |
|||
EditorGUIUtility.AddCursorRect(rect, MouseCursor.Pan); |
|||
Invalidate(false); |
|||
} |
|||
|
|||
if(m_DragPreview && Event.current.type == EventType.MouseDrag) |
|||
{ |
|||
m_CameraPosition -= Event.current.delta; |
|||
Invalidate(false); |
|||
} |
|||
|
|||
// Zoom : using MouseWheel
|
|||
if (Event.current.type == EventType.ScrollWheel && rect.Contains(Event.current.mousePosition) ) |
|||
{ |
|||
// Delta negative when zooming In, Positive when zooming out
|
|||
Zoom(Event.current.delta.y * 0.05f, Event.current.mousePosition); |
|||
Invalidate(false); |
|||
} |
|||
|
|||
|
|||
// Zoom : using Alt + RightClick
|
|||
if (Event.current.type == EventType.MouseDown && Event.current.button == 1 && Event.current.alt) |
|||
{ |
|||
m_ZoomPreview = true; |
|||
m_ZoomPreviewCenter = Event.current.mousePosition; |
|||
m_PreviousMousePosition = m_ZoomPreviewCenter; |
|||
} |
|||
|
|||
if (Event.current.rawType == EventType.MouseUp && Event.current.button == 1) |
|||
{ |
|||
m_ZoomPreview = false; |
|||
} |
|||
|
|||
if(m_ZoomPreview) |
|||
{ |
|||
EditorGUIUtility.AddCursorRect(rect, MouseCursor.Zoom); |
|||
Vector2 mouseDelta = Event.current.mousePosition - m_PreviousMousePosition; |
|||
Zoom((mouseDelta.x + mouseDelta.y) * -0.002f, m_ZoomPreviewCenter); |
|||
Invalidate(false); |
|||
m_PreviousMousePosition = Event.current.mousePosition; |
|||
} |
|||
|
|||
// Draw Texture
|
|||
if(Event.current.type == EventType.Repaint) |
|||
{ |
|||
GUI.DrawTexture |
|||
( |
|||
new Rect( |
|||
(rect.width/2) - m_CameraPosition.x - (texture.width*m_Zoom*0.5f), |
|||
(rect.height/2) - m_CameraPosition.y - (texture.height*m_Zoom*0.5f), |
|||
texture.width*m_Zoom, |
|||
texture.height*m_Zoom |
|||
), |
|||
m_RenderTexture, |
|||
ScaleMode.ScaleToFit |
|||
); |
|||
} |
|||
|
|||
} |
|||
|
|||
public Vector2 CanvasToScreen(Vector2 Position) |
|||
{ |
|||
return new Vector2( |
|||
(m_Rect.width / 2) - m_CameraPosition.x - (Position.x * m_Zoom), |
|||
(m_Rect.height / 2) - m_CameraPosition.y - (Position.y * m_Zoom) |
|||
); |
|||
} |
|||
|
|||
protected virtual void DrawGrid() |
|||
{ |
|||
Vector2 src, dst; |
|||
if(BackgroundBrightness < 0.5f) |
|||
Handles.color = new Color(1.0f,1.0f,1.0f,0.33333f); |
|||
else |
|||
Handles.color = new Color(0.0f,0.0f,0.0f,0.66666f); |
|||
|
|||
src = CanvasToScreen(new Vector2(-texture.width/2, -texture.height/2)); |
|||
dst = CanvasToScreen(new Vector2(texture.width/2, texture.height/2)); |
|||
|
|||
Handles.DrawLine(new Vector2(src.x,src.y), new Vector2(dst.x,src.y)); |
|||
Handles.DrawLine(new Vector2(dst.x,src.y), new Vector2(dst.x,dst.y)); |
|||
Handles.DrawLine(new Vector2(dst.x,dst.y), new Vector2(src.x,dst.y)); |
|||
Handles.DrawLine(new Vector2(src.x,dst.y), new Vector2(src.x,src.y)); |
|||
|
|||
Handles.color = Color.white; |
|||
} |
|||
|
|||
private void BlitIntoRenderTarget() |
|||
{ |
|||
// Backup GUI RenderTarget
|
|||
var oldrendertarget = RenderTexture.active; |
|||
|
|||
Graphics.Blit(texture, m_RenderTexture, m_Material); |
|||
|
|||
// Restore GUI RenderTarget
|
|||
RenderTexture.active = oldrendertarget; |
|||
} |
|||
|
|||
public virtual void OnGUI() |
|||
{ |
|||
|
|||
if(m_bgBrightness < 0.0f) |
|||
{ |
|||
ResetBrightness(); |
|||
} |
|||
|
|||
// Focus taken when clicked in viewport
|
|||
if (Event.current.type == EventType.MouseDown && m_Rect.Contains(Event.current.mousePosition)) |
|||
{ |
|||
GUI.FocusControl(""); |
|||
} |
|||
|
|||
if(texture != null && Event.current.type == EventType.Repaint) |
|||
{ |
|||
if (m_IsDirtyRenderTarget) |
|||
UpdateRenderTarget(); |
|||
|
|||
if(m_bNeedRedraw) |
|||
{ |
|||
BlitIntoRenderTarget(); |
|||
m_bNeedRedraw = false; |
|||
} |
|||
} |
|||
|
|||
GUI.BeginGroup(m_Rect); |
|||
Rect LocalRect = new Rect(Vector2.zero, m_Rect.size); |
|||
|
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear); |
|||
#endif
|
|||
GUI.DrawTextureWithTexCoords(LocalRect, BackgroundTexture, new Rect(0, 0, m_Rect.width / 64, m_Rect.height / 64)); |
|||
|
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
//GL.sRGBWrite = false;
|
|||
#endif
|
|||
if (texture != null) |
|||
{ |
|||
HandleKeyboardEvents(); |
|||
|
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear); |
|||
#endif
|
|||
DrawCurrentTexture(); |
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
//GL.sRGBWrite = false;
|
|||
#endif
|
|||
|
|||
if (showGrid) DrawGrid(); |
|||
if (onCanvasGUI != null) |
|||
onCanvasGUI(); |
|||
} |
|||
else |
|||
GUI.Label(LocalRect, VFXToolboxGUIUtility.Get("No Texture"), EditorStyles.centeredGreyMiniLabel); |
|||
|
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
//GL.sRGBWrite = false;
|
|||
#endif
|
|||
|
|||
GUI.EndGroup(); |
|||
} |
|||
|
|||
#region BRIGHTNESS CONTROLS
|
|||
|
|||
public Texture2D BackgroundTexture { get { return m_BackgroundTexture; } } |
|||
private Texture2D m_BackgroundTexture; |
|||
|
|||
public void SetBGBrightness(float value) |
|||
{ |
|||
m_bgBrightness = value; |
|||
m_BackgroundTexture = Styles.GetBGTexture(value); |
|||
} |
|||
|
|||
public void ResetBrightness() |
|||
{ |
|||
if (EditorGUIUtility.isProSkin) |
|||
BackgroundBrightness = 0.2f; |
|||
else |
|||
BackgroundBrightness = 0.4f; |
|||
} |
|||
|
|||
public void BrightnessUp(float value) |
|||
{ |
|||
BackgroundBrightness = Mathf.Min(BackgroundBrightness + value,1.0f); |
|||
} |
|||
|
|||
public void BrightnessDown(float value) |
|||
{ |
|||
BackgroundBrightness = Mathf.Max(0.0f, BackgroundBrightness - value); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region STYLES
|
|||
|
|||
|
|||
|
|||
public class Styles |
|||
{ |
|||
public GUIStyle miniLabel |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabel : m_ViewportMiniLabelDark; } |
|||
} |
|||
|
|||
public GUIStyle miniLabelRight |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabelRight : m_ViewportMiniLabelRightDark; } |
|||
} |
|||
|
|||
public GUIStyle miniLabelCenter |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportMiniLabelCenter : m_ViewportMiniLabelCenterDark; } |
|||
} |
|||
|
|||
public GUIStyle label |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportLabel : m_ViewportLabelDark; } |
|||
} |
|||
|
|||
public GUIStyle largeLabel |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_ViewportLargeLabel : m_ViewportLargeLabelDark; } |
|||
} |
|||
|
|||
public Color backgroundPanelColor |
|||
{ |
|||
get { return m_Canvas.BackgroundBrightness < 0.5f ? m_BackgroundPanelColor : m_BackgroundPanelColorDark; } |
|||
} |
|||
|
|||
public Color red { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 0, 0, 1) : new Color(0.7f, 0, 0, 1); } } |
|||
public Color green { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 1, 0, 1) : new Color(0, 0.5f, 0, 1); } } |
|||
public Color blue { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 0, 1, 1) : new Color(0, 0, 0.5f, 1); } } |
|||
public Color white { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 1, 1, 1) : new Color(0, 0, 0, 1); } } |
|||
public Color black { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0, 0, 0, 1) : new Color(1, 1, 1, 1); } } |
|||
public Color yellow { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1.0f, 0.8f, 0.25f) : new Color(0.5f, 0.4f, 0.1f); } } |
|||
public Color cyan { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(0.25f, 0.8f, 1.0f) : new Color(0.1f, 0.4f, 0.5f); } } |
|||
public Color fadewhite { get { return m_Canvas.BackgroundBrightness < 0.5f ? new Color(1, 1, 1, 0.25f) : new Color(0, 0, 0, 0.25f); } } |
|||
|
|||
private GUIStyle m_ViewportMiniLabel; |
|||
private GUIStyle m_ViewportMiniLabelDark; |
|||
private GUIStyle m_ViewportMiniLabelRight; |
|||
private GUIStyle m_ViewportMiniLabelRightDark; |
|||
private GUIStyle m_ViewportMiniLabelCenter; |
|||
private GUIStyle m_ViewportMiniLabelCenterDark; |
|||
private GUIStyle m_ViewportLabel; |
|||
private GUIStyle m_ViewportLabelDark; |
|||
private GUIStyle m_ViewportLargeLabel; |
|||
private GUIStyle m_ViewportLargeLabelDark; |
|||
|
|||
private VFXToolboxCanvas m_Canvas; |
|||
|
|||
private Color m_BackgroundPanelColor; |
|||
private Color m_BackgroundPanelColorDark; |
|||
|
|||
public Styles(VFXToolboxCanvas canvas) |
|||
{ |
|||
m_Canvas = canvas; |
|||
|
|||
m_ViewportMiniLabel = new GUIStyle(EditorStyles.miniLabel); |
|||
m_ViewportMiniLabel.normal.textColor = Color.white; |
|||
m_ViewportMiniLabelDark = new GUIStyle(EditorStyles.miniLabel); |
|||
m_ViewportMiniLabelDark.normal.textColor = Color.black; |
|||
|
|||
m_ViewportMiniLabelRight = new GUIStyle(m_ViewportMiniLabel); |
|||
m_ViewportMiniLabelRight.alignment = TextAnchor.MiddleRight; |
|||
m_ViewportMiniLabelRightDark = new GUIStyle(m_ViewportMiniLabelDark); |
|||
m_ViewportMiniLabelRightDark.alignment = TextAnchor.MiddleRight; |
|||
|
|||
m_ViewportMiniLabelCenter = new GUIStyle(m_ViewportMiniLabel); |
|||
m_ViewportMiniLabelCenter.alignment = TextAnchor.MiddleCenter; |
|||
m_ViewportMiniLabelCenterDark = new GUIStyle(m_ViewportMiniLabelDark); |
|||
m_ViewportMiniLabelCenterDark.alignment = TextAnchor.MiddleCenter; |
|||
|
|||
m_ViewportLabel = new GUIStyle(EditorStyles.largeLabel); |
|||
m_ViewportLabel.normal.textColor = Color.white; |
|||
|
|||
m_ViewportLabelDark = new GUIStyle(EditorStyles.largeLabel); |
|||
m_ViewportLabelDark.normal.textColor = Color.black; |
|||
|
|||
m_ViewportLargeLabel = new GUIStyle(EditorStyles.largeLabel); |
|||
m_ViewportLargeLabel.fontSize = 24; |
|||
m_ViewportLargeLabel.normal.textColor = Color.white; |
|||
|
|||
m_ViewportLargeLabelDark = new GUIStyle(EditorStyles.largeLabel); |
|||
m_ViewportLargeLabelDark.fontSize = 24; |
|||
m_ViewportLargeLabelDark.normal.textColor = Color.black; |
|||
|
|||
m_BackgroundPanelColor = new Color(0.02f, 0.02f, 0.02f, 0.85f); |
|||
m_BackgroundPanelColorDark = new Color(0.25f, 0.25f, 0.25f, 0.85f); |
|||
|
|||
} |
|||
|
|||
public static Texture2D GetBGTexture(float brightness) |
|||
{ |
|||
Texture2D out_tex = new Texture2D(2, 2) { hideFlags = HideFlags.DontSave }; |
|||
Color[] bgcolors = new Color[4]; |
|||
brightness *= 0.95f; |
|||
bgcolors[0] = new Color(brightness+0.05f, brightness+0.05f, brightness+0.05f); |
|||
bgcolors[1] = new Color(brightness, brightness, brightness); |
|||
bgcolors[2] = new Color(brightness, brightness, brightness); |
|||
bgcolors[3] = new Color(brightness+0.05f, brightness+0.05f, brightness+0.05f); |
|||
out_tex.SetPixels(bgcolors); |
|||
out_tex.wrapMode = TextureWrapMode.Repeat; |
|||
out_tex.filterMode = FilterMode.Point; |
|||
out_tex.Apply(); |
|||
return out_tex; |
|||
} |
|||
|
|||
|
|||
} |
|||
#endregion
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ea792668228c44546a965aa9034a163d |
|||
timeCreated: 1475496324 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: cb01cd6b53c5afb4e932d16ea337934c |
|||
folderAsset: yes |
|||
timeCreated: 1475064740 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.IO; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
/// <summary>
|
|||
/// Example of an Asset PostProcessor that configures all textures within a given folder (default Asset/Resources)
|
|||
/// to be imported with settings according to VFX Toolbox Image Sequencer recommandations.
|
|||
/// </summary>
|
|||
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 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 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; |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1e7e4f3b8c8b7044993e1c771fe77dfe |
|||
timeCreated: 1475062871 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 79e4635bcb669464dad7c404d7fe4963 |
|||
folderAsset: yes |
|||
timeCreated: 1478099486 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public sealed class CurveEditor |
|||
{ |
|||
#region Enums
|
|||
|
|||
enum EditMode |
|||
{ |
|||
None, |
|||
Moving, |
|||
TangentEdit |
|||
} |
|||
|
|||
enum Tangent |
|||
{ |
|||
In, |
|||
Out |
|||
} |
|||
#endregion
|
|||
|
|||
#region Structs
|
|||
public struct Settings |
|||
{ |
|||
public Rect bounds; |
|||
public RectOffset padding; |
|||
public Color selectionColor; |
|||
public float curvePickingDistance; |
|||
public float keyTimeClampingDistance; |
|||
|
|||
public static Settings defaultSettings |
|||
{ |
|||
get |
|||
{ |
|||
return new Settings |
|||
{ |
|||
bounds = new Rect(0f, 0f, 1f, 1f), |
|||
padding = new RectOffset(10, 10, 10, 10), |
|||
selectionColor = Color.yellow, |
|||
curvePickingDistance = 6f, |
|||
keyTimeClampingDistance = 1e-4f |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public struct CurveState |
|||
{ |
|||
public bool visible; |
|||
public bool editable; |
|||
public uint minPointCount; |
|||
public float zeroKeyConstantValue; |
|||
public Color color; |
|||
public float width; |
|||
public float handleWidth; |
|||
public bool showNonEditableHandles; |
|||
public bool onlyShowHandlesOnSelection; |
|||
public bool loopInBounds; |
|||
|
|||
public static CurveState defaultState |
|||
{ |
|||
get |
|||
{ |
|||
return new CurveState |
|||
{ |
|||
visible = true, |
|||
editable = true, |
|||
minPointCount = 2, |
|||
zeroKeyConstantValue = 0f, |
|||
color = Color.white, |
|||
width = 2f, |
|||
handleWidth = 2f, |
|||
showNonEditableHandles = true, |
|||
onlyShowHandlesOnSelection = false, |
|||
loopInBounds = false |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public struct Selection |
|||
{ |
|||
public SerializedProperty curve; |
|||
public int keyframeIndex; |
|||
public Keyframe? keyframe; |
|||
|
|||
public Selection(SerializedProperty curve, int keyframeIndex, Keyframe? keyframe) |
|||
{ |
|||
this.curve = curve; |
|||
this.keyframeIndex = keyframeIndex; |
|||
this.keyframe = keyframe; |
|||
} |
|||
} |
|||
|
|||
internal struct MenuAction |
|||
{ |
|||
internal SerializedProperty curve; |
|||
internal int index; |
|||
internal Vector3 position; |
|||
|
|||
internal MenuAction(SerializedProperty curve) |
|||
{ |
|||
this.curve = curve; |
|||
this.index = -1; |
|||
this.position = Vector3.zero; |
|||
} |
|||
|
|||
internal MenuAction(SerializedProperty curve, int index) |
|||
{ |
|||
this.curve = curve; |
|||
this.index = index; |
|||
this.position = Vector3.zero; |
|||
} |
|||
|
|||
internal MenuAction(SerializedProperty curve, Vector3 position) |
|||
{ |
|||
this.curve = curve; |
|||
this.index = -1; |
|||
this.position = position; |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
#region Fields & properties
|
|||
public Settings settings { get; private set; } |
|||
|
|||
Dictionary<SerializedProperty, CurveState> m_Curves; |
|||
Rect m_CurveArea; |
|||
|
|||
SerializedProperty m_SelectedCurve; |
|||
int m_SelectedKeyframeIndex = -1; |
|||
|
|||
EditMode m_EditMode = EditMode.None; |
|||
Tangent m_TangentEditMode; |
|||
|
|||
bool m_Dirty; |
|||
#endregion
|
|||
|
|||
#region Constructors & destructors
|
|||
public CurveEditor() |
|||
: this(Settings.defaultSettings) |
|||
{} |
|||
|
|||
public CurveEditor(Settings settings) |
|||
{ |
|||
this.settings = settings; |
|||
m_Curves = new Dictionary<SerializedProperty, CurveState>(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Public API
|
|||
public void Add(params SerializedProperty[] curves) |
|||
{ |
|||
foreach (var curve in curves) |
|||
Add(curve, CurveState.defaultState); |
|||
} |
|||
|
|||
public void Add(SerializedProperty curve) |
|||
{ |
|||
Add(curve, CurveState.defaultState); |
|||
} |
|||
|
|||
public void Add(SerializedProperty curve, CurveState state) |
|||
{ |
|||
// Make sure the property is in fact an AnimationCurve
|
|||
var animCurve = curve.animationCurveValue; |
|||
if (animCurve == null) |
|||
throw new ArgumentException("curve"); |
|||
|
|||
if (m_Curves.ContainsKey(curve)) |
|||
Debug.LogWarning("Curve has already been added to the editor"); |
|||
|
|||
m_Curves.Add(curve, state); |
|||
} |
|||
|
|||
public void Remove(SerializedProperty curve) |
|||
{ |
|||
m_Curves.Remove(curve); |
|||
} |
|||
|
|||
public void RemoveAll() |
|||
{ |
|||
m_Curves.Clear(); |
|||
} |
|||
|
|||
public CurveState GetCurveState(SerializedProperty curve) |
|||
{ |
|||
CurveState state; |
|||
if (!m_Curves.TryGetValue(curve, out state)) |
|||
throw new KeyNotFoundException("curve"); |
|||
|
|||
return state; |
|||
} |
|||
|
|||
public void SetCurveState(SerializedProperty curve, CurveState state) |
|||
{ |
|||
if (!m_Curves.ContainsKey(curve)) |
|||
throw new KeyNotFoundException("curve"); |
|||
|
|||
m_Curves[curve] = state; |
|||
} |
|||
|
|||
public Selection GetSelection() |
|||
{ |
|||
Keyframe? key = null; |
|||
if (m_SelectedKeyframeIndex > -1) |
|||
{ |
|||
var curve = m_SelectedCurve.animationCurveValue; |
|||
|
|||
if (m_SelectedKeyframeIndex >= curve.length) |
|||
m_SelectedKeyframeIndex = -1; |
|||
else |
|||
key = curve[m_SelectedKeyframeIndex]; |
|||
} |
|||
|
|||
return new Selection(m_SelectedCurve, m_SelectedKeyframeIndex, key); |
|||
} |
|||
|
|||
public void ClearSelection() |
|||
{ |
|||
m_SelectedCurve = null; |
|||
m_SelectedKeyframeIndex = -1; |
|||
} |
|||
|
|||
public void SetKeyframe(SerializedProperty curve, int keyframeIndex, Keyframe keyframe) |
|||
{ |
|||
var animCurve = curve.animationCurveValue; |
|||
SetKeyframe(animCurve, keyframeIndex, keyframe); |
|||
SaveCurve(curve, animCurve); |
|||
} |
|||
|
|||
public void SetBounds(Rect bounds) |
|||
{ |
|||
var newSettings = settings; |
|||
newSettings.bounds = bounds; |
|||
settings = newSettings; |
|||
} |
|||
|
|||
public bool OnGUI(Rect rect) |
|||
{ |
|||
if(Event.current.type == EventType.Repaint) |
|||
m_Dirty = false; |
|||
|
|||
GUI.BeginClip(rect); |
|||
{ |
|||
var area = new Rect(Vector2.zero, rect.size); |
|||
m_CurveArea = settings.padding.Remove(area); |
|||
|
|||
foreach (var curve in m_Curves) |
|||
OnCurveGUI(area, curve.Key, curve.Value); |
|||
|
|||
OnGeneralUI(area); |
|||
} |
|||
GUI.EndClip(); |
|||
|
|||
return m_Dirty; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region UI & events
|
|||
|
|||
void OnCurveGUI(Rect rect, SerializedProperty curve, CurveState state) |
|||
{ |
|||
// Discard invisible curves
|
|||
if (!state.visible) |
|||
return; |
|||
|
|||
var animCurve = curve.animationCurveValue; |
|||
var keys = animCurve.keys; |
|||
var length = keys.Length; |
|||
|
|||
// Curve drawing
|
|||
// Slightly dim non-editable curves
|
|||
var color = state.color; |
|||
if (!state.editable) |
|||
color.a *= 0.5f; |
|||
|
|||
Handles.color = color; |
|||
var bounds = settings.bounds; |
|||
|
|||
if (length == 0) |
|||
{ |
|||
var p1 = CurveToCanvas(new Vector3(bounds.xMin, state.zeroKeyConstantValue)); |
|||
var p2 = CurveToCanvas(new Vector3(bounds.xMax, state.zeroKeyConstantValue)); |
|||
Handles.DrawAAPolyLine(state.width, p1, p2); |
|||
} |
|||
else if (length == 1) |
|||
{ |
|||
var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value)); |
|||
var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[0].value)); |
|||
Handles.DrawAAPolyLine(state.width, p1, p2); |
|||
} |
|||
else |
|||
{ |
|||
var prevKey = keys[0]; |
|||
for (int k = 1; k < length; k++) |
|||
{ |
|||
var key = keys[k]; |
|||
var pts = BezierSegment(prevKey, key); |
|||
|
|||
if (float.IsInfinity(prevKey.outTangent) || float.IsInfinity(key.inTangent)) |
|||
{ |
|||
var s = HardSegment(prevKey, key); |
|||
Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]); |
|||
} |
|||
else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width); |
|||
|
|||
prevKey = key; |
|||
} |
|||
|
|||
// Curve extents & loops
|
|||
if (keys[0].time > bounds.xMin) |
|||
{ |
|||
if (state.loopInBounds) |
|||
{ |
|||
var p1 = keys[length - 1]; |
|||
p1.time -= settings.bounds.width; |
|||
var p2 = keys[0]; |
|||
var pts = BezierSegment(p1, p2); |
|||
|
|||
if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent)) |
|||
{ |
|||
var s = HardSegment(p1, p2); |
|||
Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]); |
|||
} |
|||
else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width); |
|||
} |
|||
else |
|||
{ |
|||
var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value)); |
|||
var p2 = CurveToCanvas(keys[0]); |
|||
Handles.DrawAAPolyLine(state.width, p1, p2); |
|||
} |
|||
} |
|||
|
|||
if (keys[length - 1].time < bounds.xMax) |
|||
{ |
|||
if (state.loopInBounds) |
|||
{ |
|||
var p1 = keys[length - 1]; |
|||
var p2 = keys[0]; |
|||
p2.time += settings.bounds.width; |
|||
var pts = BezierSegment(p1, p2); |
|||
|
|||
if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent)) |
|||
{ |
|||
var s = HardSegment(p1, p2); |
|||
Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]); |
|||
} |
|||
else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width); |
|||
} |
|||
else |
|||
{ |
|||
var p1 = CurveToCanvas(keys[length - 1]); |
|||
var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[length - 1].value)); |
|||
Handles.DrawAAPolyLine(state.width, p1, p2); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Make sure selection is correct (undo can break it)
|
|||
bool isCurrentlySelectedCurve = curve == m_SelectedCurve; |
|||
|
|||
if (isCurrentlySelectedCurve && m_SelectedKeyframeIndex >= length) |
|||
m_SelectedKeyframeIndex = -1; |
|||
|
|||
// Handles & keys
|
|||
for (int k = 0; k < length; k++) |
|||
{ |
|||
bool isCurrentlySelectedKeyframe = k == m_SelectedKeyframeIndex; |
|||
var e = Event.current; |
|||
|
|||
var pos = CurveToCanvas(keys[k]); |
|||
var hitRect = new Rect(pos.x - 8f, pos.y - 8f, 16f, 16f); |
|||
var offset = isCurrentlySelectedCurve |
|||
? new RectOffset(5, 5, 5, 5) |
|||
: new RectOffset(6, 6, 6, 6); |
|||
|
|||
var outTangent = pos + CurveTangentToCanvas(keys[k].outTangent).normalized * 40f; |
|||
var inTangent = pos - CurveTangentToCanvas(keys[k].inTangent).normalized * 40f; |
|||
var inTangentHitRect = new Rect(inTangent.x - 7f, inTangent.y - 7f, 14f, 14f); |
|||
var outTangentHitrect = new Rect(outTangent.x - 7f, outTangent.y - 7f, 14f, 14f); |
|||
|
|||
// Draw
|
|||
if (state.showNonEditableHandles) |
|||
{ |
|||
if (e.type == EventType.Repaint) |
|||
{ |
|||
var selectedColor = (isCurrentlySelectedCurve && isCurrentlySelectedKeyframe) |
|||
? settings.selectionColor |
|||
: state.color; |
|||
|
|||
// Keyframe
|
|||
EditorGUI.DrawRect(offset.Remove(hitRect), selectedColor); |
|||
|
|||
// Tangents
|
|||
if (isCurrentlySelectedCurve && (!state.onlyShowHandlesOnSelection || (state.onlyShowHandlesOnSelection && isCurrentlySelectedKeyframe))) |
|||
{ |
|||
Handles.color = selectedColor; |
|||
|
|||
if (k > 0 || state.loopInBounds) |
|||
{ |
|||
Handles.DrawAAPolyLine(state.handleWidth, pos, inTangent); |
|||
EditorGUI.DrawRect(offset.Remove(inTangentHitRect), selectedColor); |
|||
} |
|||
|
|||
if (k < length - 1 || state.loopInBounds) |
|||
{ |
|||
Handles.DrawAAPolyLine(state.handleWidth, pos, outTangent); |
|||
EditorGUI.DrawRect(offset.Remove(outTangentHitrect), selectedColor); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Events
|
|||
if (state.editable) |
|||
{ |
|||
// Keyframe move
|
|||
if (m_EditMode == EditMode.Moving && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe) |
|||
{ |
|||
EditMoveKeyframe(animCurve, keys, k); |
|||
} |
|||
|
|||
// Tangent editing
|
|||
if (m_EditMode == EditMode.TangentEdit && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe) |
|||
{ |
|||
bool alreadyBroken = !(Mathf.Approximately(keys[k].inTangent, keys[k].outTangent) || (float.IsInfinity(keys[k].inTangent) && float.IsInfinity(keys[k].outTangent))); |
|||
EditMoveTangent(animCurve, keys, k, m_TangentEditMode, e.shift || !(alreadyBroken || e.control)); |
|||
} |
|||
|
|||
// Keyframe selection & context menu
|
|||
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition)) |
|||
{ |
|||
if (hitRect.Contains(e.mousePosition)) |
|||
{ |
|||
if (e.button == 0) |
|||
{ |
|||
SelectKeyframe(curve, k); |
|||
m_EditMode = EditMode.Moving; |
|||
e.Use(); |
|||
} |
|||
else if (e.button == 1) |
|||
{ |
|||
// Keyframe context menu
|
|||
var menu = new GenericMenu(); |
|||
menu.AddItem(new GUIContent("Delete Key"), false, (x) => |
|||
{ |
|||
var action = (MenuAction)x; |
|||
var curveValue = action.curve.animationCurveValue; |
|||
action.curve.serializedObject.Update(); |
|||
RemoveKeyframe(curveValue, action.index); |
|||
m_SelectedKeyframeIndex = -1; |
|||
SaveCurve(action.curve, curveValue); |
|||
action.curve.serializedObject.ApplyModifiedProperties(); |
|||
}, new MenuAction(curve, k)); |
|||
menu.ShowAsContext(); |
|||
e.Use(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Tangent selection & edit mode
|
|||
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition)) |
|||
{ |
|||
if (inTangentHitRect.Contains(e.mousePosition) && (k > 0 || state.loopInBounds)) |
|||
{ |
|||
SelectKeyframe(curve, k); |
|||
m_EditMode = EditMode.TangentEdit; |
|||
m_TangentEditMode = Tangent.In; |
|||
e.Use(); |
|||
} |
|||
else if (outTangentHitrect.Contains(e.mousePosition) && (k < length - 1 || state.loopInBounds)) |
|||
{ |
|||
SelectKeyframe(curve, k); |
|||
m_EditMode = EditMode.TangentEdit; |
|||
m_TangentEditMode = Tangent.Out; |
|||
e.Use(); |
|||
} |
|||
} |
|||
|
|||
// Mouse up - clean up states
|
|||
if (e.rawType == EventType.MouseUp && m_EditMode != EditMode.None) |
|||
{ |
|||
m_EditMode = EditMode.None; |
|||
} |
|||
|
|||
// Set cursors
|
|||
{ |
|||
EditorGUIUtility.AddCursorRect(hitRect, MouseCursor.MoveArrow); |
|||
|
|||
if (k > 0 || state.loopInBounds) |
|||
EditorGUIUtility.AddCursorRect(inTangentHitRect, MouseCursor.RotateArrow); |
|||
|
|||
if (k < length - 1 || state.loopInBounds) |
|||
EditorGUIUtility.AddCursorRect(outTangentHitrect, MouseCursor.RotateArrow); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Handles.color = Color.white; |
|||
SaveCurve(curve, animCurve); |
|||
} |
|||
|
|||
void OnGeneralUI(Rect rect) |
|||
{ |
|||
var e = Event.current; |
|||
|
|||
// Selection
|
|||
if (e.type == EventType.MouseDown) |
|||
{ |
|||
GUI.FocusControl(null); |
|||
m_SelectedCurve = null; |
|||
m_SelectedKeyframeIndex = -1; |
|||
bool used = false; |
|||
|
|||
var hit = CanvasToCurve(e.mousePosition); |
|||
float curvePickValue = CurveToCanvas(hit).y; |
|||
|
|||
// Try and select a curve
|
|||
foreach (var curve in m_Curves) |
|||
{ |
|||
if (!curve.Value.editable || !curve.Value.visible) |
|||
continue; |
|||
|
|||
var prop = curve.Key; |
|||
var state = curve.Value; |
|||
var animCurve = prop.animationCurveValue; |
|||
float hitY = animCurve.length == 0 |
|||
? state.zeroKeyConstantValue |
|||
: animCurve.Evaluate(hit.x); |
|||
|
|||
var curvePos = CurveToCanvas(new Vector3(hit.x, hitY)); |
|||
|
|||
if (Mathf.Abs(curvePos.y - curvePickValue) < settings.curvePickingDistance) |
|||
{ |
|||
m_SelectedCurve = prop; |
|||
|
|||
if (e.clickCount == 2 && e.button == 0) |
|||
{ |
|||
// Create a keyframe on double-click on this curve
|
|||
EditCreateKeyframe(animCurve, hit, true, state.zeroKeyConstantValue); |
|||
SaveCurve(prop, animCurve); |
|||
} |
|||
else if (e.button == 1) |
|||
{ |
|||
// Curve context menu
|
|||
var menu = new GenericMenu(); |
|||
menu.AddItem(new GUIContent("Add Key"), false, (x) => |
|||
{ |
|||
var action = (MenuAction)x; |
|||
var curveValue = action.curve.animationCurveValue; |
|||
action.curve.serializedObject.Update(); |
|||
EditCreateKeyframe(curveValue, hit, true, 0f); |
|||
SaveCurve(action.curve, curveValue); |
|||
action.curve.serializedObject.ApplyModifiedProperties(); |
|||
}, new MenuAction(prop, hit)); |
|||
menu.ShowAsContext(); |
|||
e.Use(); |
|||
used = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (e.clickCount == 2 && e.button == 0 && m_SelectedCurve == null) |
|||
{ |
|||
// Create a keyframe on every curve on double-click
|
|||
foreach (var curve in m_Curves) |
|||
{ |
|||
if (!curve.Value.editable || !curve.Value.visible) |
|||
continue; |
|||
|
|||
var prop = curve.Key; |
|||
var state = curve.Value; |
|||
var animCurve = prop.animationCurveValue; |
|||
EditCreateKeyframe(animCurve, hit, e.alt, state.zeroKeyConstantValue); |
|||
SaveCurve(prop, animCurve); |
|||
} |
|||
} |
|||
else if (!used && e.button == 1) |
|||
{ |
|||
// Global context menu
|
|||
var menu = new GenericMenu(); |
|||
menu.AddItem(new GUIContent("Add Key At Position"), false, () => ContextMenuAddKey(hit, false)); |
|||
menu.AddItem(new GUIContent("Add Key On Curves"), false, () => ContextMenuAddKey(hit, true)); |
|||
menu.ShowAsContext(); |
|||
} |
|||
|
|||
e.Use(); |
|||
} |
|||
|
|||
// Delete selected key(s)
|
|||
if (e.type == EventType.KeyDown && (e.keyCode == KeyCode.Delete || e.keyCode == KeyCode.Backspace)) |
|||
{ |
|||
if (m_SelectedKeyframeIndex != -1 && m_SelectedCurve != null) |
|||
{ |
|||
var animCurve = m_SelectedCurve.animationCurveValue; |
|||
var length = animCurve.length; |
|||
|
|||
if (m_Curves[m_SelectedCurve].minPointCount < length && length >= 0) |
|||
{ |
|||
EditDeleteKeyframe(animCurve, m_SelectedKeyframeIndex); |
|||
m_SelectedKeyframeIndex = -1; |
|||
SaveCurve(m_SelectedCurve, animCurve); |
|||
} |
|||
|
|||
e.Use(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void SaveCurve(SerializedProperty prop, AnimationCurve curve) |
|||
{ |
|||
prop.animationCurveValue = curve; |
|||
} |
|||
|
|||
void Invalidate() |
|||
{ |
|||
m_Dirty = true; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Keyframe manipulations
|
|||
|
|||
void SelectKeyframe(SerializedProperty curve, int keyframeIndex) |
|||
{ |
|||
m_SelectedKeyframeIndex = keyframeIndex; |
|||
m_SelectedCurve = curve; |
|||
Invalidate(); |
|||
} |
|||
|
|||
void ContextMenuAddKey(Vector3 hit, bool createOnCurve) |
|||
{ |
|||
SerializedObject serializedObject = null; |
|||
|
|||
foreach (var curve in m_Curves) |
|||
{ |
|||
if (!curve.Value.editable || !curve.Value.visible) |
|||
continue; |
|||
|
|||
var prop = curve.Key; |
|||
var state = curve.Value; |
|||
|
|||
if (serializedObject == null) |
|||
{ |
|||
serializedObject = prop.serializedObject; |
|||
serializedObject.Update(); |
|||
} |
|||
|
|||
var animCurve = prop.animationCurveValue; |
|||
EditCreateKeyframe(animCurve, hit, createOnCurve, state.zeroKeyConstantValue); |
|||
SaveCurve(prop, animCurve); |
|||
} |
|||
|
|||
if (serializedObject != null) |
|||
serializedObject.ApplyModifiedProperties(); |
|||
|
|||
Invalidate(); |
|||
} |
|||
|
|||
void EditCreateKeyframe(AnimationCurve curve, Vector3 position, bool createOnCurve, float zeroKeyConstantValue) |
|||
{ |
|||
float tangent = EvaluateTangent(curve, position.x); |
|||
|
|||
if (createOnCurve) |
|||
{ |
|||
position.y = curve.length == 0 |
|||
? zeroKeyConstantValue |
|||
: curve.Evaluate(position.x); |
|||
} |
|||
|
|||
AddKeyframe(curve, new Keyframe(position.x, position.y, tangent, tangent)); |
|||
} |
|||
|
|||
void EditDeleteKeyframe(AnimationCurve curve, int keyframeIndex) |
|||
{ |
|||
RemoveKeyframe(curve, keyframeIndex); |
|||
} |
|||
|
|||
void AddKeyframe(AnimationCurve curve, Keyframe newValue) |
|||
{ |
|||
curve.AddKey(newValue); |
|||
Invalidate(); |
|||
} |
|||
|
|||
void RemoveKeyframe(AnimationCurve curve, int keyframeIndex) |
|||
{ |
|||
curve.RemoveKey(keyframeIndex); |
|||
Invalidate(); |
|||
} |
|||
|
|||
void SetKeyframe(AnimationCurve curve, int keyframeIndex, Keyframe newValue) |
|||
{ |
|||
var keys = curve.keys; |
|||
|
|||
if (keyframeIndex > 0) |
|||
newValue.time = Mathf.Max(keys[keyframeIndex - 1].time + settings.keyTimeClampingDistance, newValue.time); |
|||
|
|||
if (keyframeIndex < keys.Length - 1) |
|||
newValue.time = Mathf.Min(keys[keyframeIndex + 1].time - settings.keyTimeClampingDistance, newValue.time); |
|||
|
|||
curve.MoveKey(keyframeIndex, newValue); |
|||
Invalidate(); |
|||
} |
|||
|
|||
void EditMoveKeyframe(AnimationCurve curve, Keyframe[] keys, int keyframeIndex) |
|||
{ |
|||
var key = CanvasToCurve(Event.current.mousePosition); |
|||
float inTgt = keys[keyframeIndex].inTangent; |
|||
float outTgt = keys[keyframeIndex].outTangent; |
|||
SetKeyframe(curve, keyframeIndex, new Keyframe(key.x, key.y, inTgt, outTgt)); |
|||
} |
|||
|
|||
void EditMoveTangent(AnimationCurve curve, Keyframe[] keys, int keyframeIndex, Tangent targetTangent, bool linkTangents) |
|||
{ |
|||
var pos = CanvasToCurve(Event.current.mousePosition); |
|||
|
|||
float time = keys[keyframeIndex].time; |
|||
float value = keys[keyframeIndex].value; |
|||
|
|||
pos -= new Vector3(time, value); |
|||
|
|||
if (targetTangent == Tangent.In && pos.x > 0f) |
|||
pos.x = 0f; |
|||
|
|||
if (targetTangent == Tangent.Out && pos.x < 0f) |
|||
pos.x = 0f; |
|||
|
|||
float tangent; |
|||
|
|||
if (Mathf.Approximately(pos.x, 0f)) |
|||
tangent = pos.y < 0f ? float.PositiveInfinity : float.NegativeInfinity; |
|||
else |
|||
tangent = pos.y / pos.x; |
|||
|
|||
float inTangent = keys[keyframeIndex].inTangent; |
|||
float outTangent = keys[keyframeIndex].outTangent; |
|||
|
|||
if (targetTangent == Tangent.In || linkTangents) |
|||
inTangent = tangent; |
|||
if (targetTangent == Tangent.Out || linkTangents) |
|||
outTangent = tangent; |
|||
|
|||
SetKeyframe(curve, keyframeIndex, new Keyframe(time, value, inTangent, outTangent)); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Maths utilities
|
|||
|
|||
Vector3 CurveToCanvas(Keyframe keyframe) |
|||
{ |
|||
return CurveToCanvas(new Vector3(keyframe.time, keyframe.value)); |
|||
} |
|||
|
|||
Vector3 CurveToCanvas(Vector3 position) |
|||
{ |
|||
var bounds = settings.bounds; |
|||
var output = new Vector3((position.x - bounds.x) / (bounds.xMax - bounds.x), (position.y - bounds.y) / (bounds.yMax - bounds.y)); |
|||
output.x = output.x * (m_CurveArea.xMax - m_CurveArea.xMin) + m_CurveArea.xMin; |
|||
output.y = (1f - output.y) * (m_CurveArea.yMax - m_CurveArea.yMin) + m_CurveArea.yMin; |
|||
return output; |
|||
} |
|||
|
|||
Vector3 CanvasToCurve(Vector3 position) |
|||
{ |
|||
var bounds = settings.bounds; |
|||
var output = position; |
|||
output.x = (output.x - m_CurveArea.xMin) / (m_CurveArea.xMax - m_CurveArea.xMin); |
|||
output.y = (output.y - m_CurveArea.yMin) / (m_CurveArea.yMax - m_CurveArea.yMin); |
|||
output.x = Mathf.Lerp(bounds.x, bounds.xMax, output.x); |
|||
output.y = Mathf.Lerp(bounds.yMax, bounds.y, output.y); |
|||
return output; |
|||
} |
|||
|
|||
Vector3 CurveTangentToCanvas(float tangent) |
|||
{ |
|||
if (!float.IsInfinity(tangent)) |
|||
{ |
|||
var bounds = settings.bounds; |
|||
float ratio = (m_CurveArea.width / m_CurveArea.height) / ((bounds.xMax - bounds.x) / (bounds.yMax - bounds.y)); |
|||
return new Vector3(1f, -tangent / ratio).normalized; |
|||
} |
|||
|
|||
return float.IsPositiveInfinity(tangent) ? Vector3.up : Vector3.down; |
|||
} |
|||
|
|||
Vector3[] BezierSegment(Keyframe start, Keyframe end) |
|||
{ |
|||
var segment = new Vector3[4]; |
|||
|
|||
segment[0] = CurveToCanvas(new Vector3(start.time, start.value)); |
|||
segment[3] = CurveToCanvas(new Vector3(end.time, end.value)); |
|||
|
|||
float middle = start.time + ((end.time - start.time) * 0.333333f); |
|||
float middle2 = start.time + ((end.time - start.time) * 0.666666f); |
|||
|
|||
segment[1] = CurveToCanvas(new Vector3(middle, ProjectTangent(start.time, start.value, start.outTangent, middle))); |
|||
segment[2] = CurveToCanvas(new Vector3(middle2, ProjectTangent(end.time, end.value, end.inTangent, middle2))); |
|||
|
|||
return segment; |
|||
} |
|||
|
|||
Vector3[] HardSegment(Keyframe start, Keyframe end) |
|||
{ |
|||
var segment = new Vector3[3]; |
|||
|
|||
segment[0] = CurveToCanvas(start); |
|||
segment[1] = CurveToCanvas(new Vector3(end.time, start.value)); |
|||
segment[2] = CurveToCanvas(end); |
|||
|
|||
return segment; |
|||
} |
|||
|
|||
float ProjectTangent(float inPosition, float inValue, float inTangent, float projPosition) |
|||
{ |
|||
return inValue + ((projPosition - inPosition) * inTangent); |
|||
} |
|||
|
|||
float EvaluateTangent(AnimationCurve curve, float time) |
|||
{ |
|||
int prev = -1, next = 0; |
|||
for (int i = 0; i < curve.keys.Length; i++) |
|||
{ |
|||
if (time > curve.keys[i].time) |
|||
{ |
|||
prev = i; |
|||
next = i + 1; |
|||
} |
|||
else break; |
|||
} |
|||
|
|||
if (next == 0) |
|||
return 0f; |
|||
|
|||
if (prev == curve.keys.Length - 1) |
|||
return 0f; |
|||
|
|||
const float kD = 1e-3f; |
|||
float tp = Mathf.Max(time - kD, curve.keys[prev].time); |
|||
float tn = Mathf.Min(time + kD, curve.keys[next].time); |
|||
|
|||
float vp = curve.Evaluate(tp); |
|||
float vn = curve.Evaluate(tn); |
|||
|
|||
if (Mathf.Approximately(tn, tp)) |
|||
return (vn - vp > 0f) ? float.PositiveInfinity : float.NegativeInfinity; |
|||
|
|||
return (vn - vp) / (tn - tp); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 09fa5806b3255d5468ab4a0e83c7aeb8 |
|||
timeCreated: 1478175296 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 476e8e3ff454ecb4080c665b1b2f57c7 |
|||
folderAsset: yes |
|||
timeCreated: 1478600131 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: d8f6eb1d1e749d1449301432b2121c19 |
|||
timeCreated: 1478600136 |
|||
licenseType: Pro |
|||
TextureImporter: |
|||
fileIDToRecycleName: {} |
|||
serializedVersion: 4 |
|||
mipmaps: |
|||
mipMapMode: 0 |
|||
enableMipMap: 1 |
|||
sRGBTexture: 1 |
|||
linearTexture: 0 |
|||
fadeOut: 0 |
|||
borderMipMap: 0 |
|||
mipMapFadeDistanceStart: 1 |
|||
mipMapFadeDistanceEnd: 3 |
|||
bumpmap: |
|||
convertToNormalMap: 0 |
|||
externalNormalMap: 0 |
|||
heightScale: 0.25 |
|||
normalMapFilter: 0 |
|||
isReadable: 0 |
|||
grayScaleToAlpha: 0 |
|||
generateCubemap: 6 |
|||
cubemapConvolution: 0 |
|||
seamlessCubemap: 0 |
|||
textureFormat: 1 |
|||
maxTextureSize: 2048 |
|||
textureSettings: |
|||
filterMode: -1 |
|||
aniso: -1 |
|||
mipBias: -1 |
|||
wrapMode: -1 |
|||
nPOTScale: 1 |
|||
lightmap: 0 |
|||
compressionQuality: 50 |
|||
spriteMode: 0 |
|||
spriteExtrude: 1 |
|||
spriteMeshType: 1 |
|||
alignment: 0 |
|||
spritePivot: {x: 0.5, y: 0.5} |
|||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
|||
spritePixelsToUnits: 100 |
|||
alphaUsage: 1 |
|||
alphaIsTransparency: 0 |
|||
spriteTessellationDetail: -1 |
|||
textureType: 0 |
|||
textureShape: 1 |
|||
maxTextureSizeSet: 0 |
|||
compressionQualitySet: 0 |
|||
textureFormatSet: 0 |
|||
platformSettings: |
|||
- buildTarget: DefaultTexturePlatform |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: Standalone |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
spriteSheet: |
|||
serializedVersion: 2 |
|||
sprites: [] |
|||
outline: [] |
|||
spritePackingTag: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 11f66135d9e709f4ab5d2de74addb620 |
|||
folderAsset: yes |
|||
timeCreated: 1471966525 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEngine.VFXToolbox; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public class CurveDrawer |
|||
{ |
|||
private string m_CurveEditName; |
|||
private int m_WidgetDefaultHeight; |
|||
private bool m_WidgetShowToolbar; |
|||
private readonly RectOffset m_CurvePadding = new RectOffset(40, 16, 16, 16); |
|||
private CurveEditor m_Editor; |
|||
private Dictionary<string, SerializedProperty> m_Curves; |
|||
|
|||
public delegate void CurveDrawEventDelegate(Rect renderArea, Rect curveArea); |
|||
public CurveDrawEventDelegate OnPostGUI; |
|||
|
|||
private float lineBrightnessValue { get { return EditorGUIUtility.isProSkin ? 1.0f : 0.0f; } } |
|||
|
|||
public CurveDrawer(string curveEditName, float minInput, float maxInput, float minOutput, float maxOutput, int height, bool showToolbar) |
|||
:this(curveEditName, minInput, maxInput, minOutput, maxOutput) |
|||
{ |
|||
m_WidgetDefaultHeight = height; |
|||
m_WidgetShowToolbar = showToolbar; |
|||
} |
|||
|
|||
public CurveDrawer(string curveEditName, float minInput, float maxInput, float minOutput, float maxOutput) |
|||
{ |
|||
var settings = CurveEditor.Settings.defaultSettings; |
|||
settings.bounds = new Rect(minInput, minOutput, maxInput - minInput, maxOutput - minOutput); |
|||
settings.padding = m_CurvePadding; |
|||
m_Editor = new CurveEditor(settings); |
|||
m_CurveEditName = curveEditName; |
|||
m_WidgetDefaultHeight = 240; |
|||
m_WidgetShowToolbar = true; |
|||
m_Curves = new Dictionary<string, SerializedProperty>(); |
|||
} |
|||
|
|||
public void SetBounds(Rect bounds) |
|||
{ |
|||
m_Editor.SetBounds(bounds); |
|||
} |
|||
|
|||
public void ClearSelection() |
|||
{ |
|||
m_Editor.ClearSelection(); |
|||
} |
|||
|
|||
public void AddCurve(SerializedProperty curveProperty, Color curveColor, string name, bool visible = true) |
|||
{ |
|||
if (m_Curves.ContainsKey(name)) |
|||
return; |
|||
|
|||
var state = CurveEditor.CurveState.defaultState; |
|||
state.color = curveColor; |
|||
state.minPointCount = 2; |
|||
|
|||
m_Curves.Add(name, curveProperty); |
|||
m_Editor.Add(curveProperty, state); |
|||
} |
|||
|
|||
public void RemoveCurve(string name) |
|||
{ |
|||
if (!m_Curves.ContainsKey(name)) |
|||
return; |
|||
m_Editor.Remove(m_Curves[name]); |
|||
} |
|||
|
|||
public void Clear() |
|||
{ |
|||
m_Curves.Clear(); |
|||
m_Editor.RemoveAll(); |
|||
} |
|||
|
|||
public bool OnGUI(Rect drawRect) |
|||
{ |
|||
return m_Editor.OnGUI(drawRect); |
|||
} |
|||
|
|||
public bool OnGUILayout() |
|||
{ |
|||
return OnGUILayout(m_WidgetDefaultHeight, m_WidgetShowToolbar); |
|||
} |
|||
|
|||
public bool OnGUILayout(bool showToolbar) |
|||
{ |
|||
return OnGUILayout(m_WidgetDefaultHeight, showToolbar); |
|||
} |
|||
|
|||
public bool OnGUILayout(float height, bool showToolbar) |
|||
{ |
|||
bool dirty = false; |
|||
using (new GUILayout.VerticalScope()) |
|||
{ |
|||
// Header
|
|||
if(m_CurveEditName != null || m_CurveEditName == "") |
|||
GUILayout.Label(m_CurveEditName); |
|||
|
|||
GUILayout.Space(4.0f); |
|||
|
|||
// Curve Area
|
|||
if(showToolbar) |
|||
{ |
|||
using (new GUILayout.HorizontalScope(EditorStyles.toolbar)) |
|||
{ |
|||
foreach(KeyValuePair<string,SerializedProperty> kvp in m_Curves) |
|||
{ |
|||
string name = kvp.Key; |
|||
SerializedProperty curve = kvp.Value; |
|||
CurveEditor.CurveState state = m_Editor.GetCurveState(curve); |
|||
bool b = GUILayout.Toggle(state.visible, name, EditorStyles.toolbarButton); |
|||
if (b != state.visible) |
|||
{ |
|||
state.visible = b; |
|||
m_Editor.SetCurveState(curve, state); |
|||
} |
|||
} |
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
} |
|||
|
|||
Rect lastRect = GUILayoutUtility.GetLastRect(); |
|||
Rect curveArea = GUILayoutUtility.GetRect(lastRect.width, height); |
|||
|
|||
// Selection
|
|||
CurveEditor.Selection selection = m_Editor.GetSelection(); |
|||
if(selection.curve != null && selection.keyframe != null) |
|||
{ |
|||
EditorGUI.indentLevel ++; |
|||
var key = selection.keyframe.Value; |
|||
Rect range = m_Editor.settings.bounds; |
|||
float t = EditorGUILayout.Slider("Time", key.time, range.xMin, range.xMax); |
|||
float v = EditorGUILayout.Slider("Value", key.value, range.yMin, range.yMax); |
|||
float inTgt = EditorGUILayout.FloatField("In Tangent", key.inTangent); |
|||
float outTgt = EditorGUILayout.FloatField("Out Tangent", key.outTangent); |
|||
|
|||
if(t != key.time || v != key.value || inTgt != key.inTangent || outTgt != key.outTangent) |
|||
{ |
|||
Keyframe newkey = new Keyframe(t,v,inTgt, outTgt); |
|||
m_Editor.SetKeyframe(selection.curve, selection.keyframeIndex, newkey); |
|||
} |
|||
EditorGUI.indentLevel--; |
|||
} |
|||
|
|||
// Canvas
|
|||
DrawCurveCanvas(curveArea); |
|||
dirty = m_Editor.OnGUI(curveArea); |
|||
|
|||
|
|||
} |
|||
return dirty; |
|||
} |
|||
|
|||
public void DrawCurveCanvas(Rect rect) |
|||
{ |
|||
EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.25f)); |
|||
if(Event.current.type == EventType.Layout) |
|||
return; |
|||
|
|||
GUI.BeginClip(rect); |
|||
|
|||
Rect area = new Rect(Vector2.zero, rect.size); |
|||
rect = m_CurvePadding.Remove(area); |
|||
|
|||
Rect bounds = m_Editor.settings.bounds; |
|||
float minInput = bounds.xMin; |
|||
float maxInput = bounds.xMax; |
|||
float minOutput = bounds.yMin; |
|||
float maxOutput = bounds.yMax; |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
// Draw Origins
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
float l = lineBrightnessValue; |
|||
|
|||
Handles.color = new Color(l, l, l, 0.2f); |
|||
Handles.DrawLine(new Vector2(rect.xMin, area.yMin), new Vector2(rect.xMin, area.yMax)); |
|||
Handles.DrawLine(new Vector2(area.xMin, rect.yMax), new Vector2(area.xMax, rect.yMax)); |
|||
Handles.DrawLine(new Vector2(rect.xMax, area.yMin), new Vector2(rect.xMax, area.yMax)); |
|||
Handles.DrawLine(new Vector2(area.xMin, rect.yMin), new Vector2(area.xMax, rect.yMin)); |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
// Draw Zero Axis'es
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
if(minInput < 0 && maxInput > 0) |
|||
{ |
|||
Handles.color = new Color(l, l, l, 0.6f); |
|||
Handles.DrawLine(new Vector2(0,rect.yMin),new Vector2(0,rect.yMax)); |
|||
} |
|||
|
|||
if(minOutput < 0 && maxOutput > 0) |
|||
{ |
|||
Handles.color = new Color(l, l, l, 0.6f); |
|||
Handles.DrawLine(new Vector2(rect.xMin,0),new Vector2(rect.xMax, 0)); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
// Draw Grid By Step
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
Handles.color = new Color(l, l, l, 0.05f); |
|||
|
|||
for(int i = 1; i < 8; i++) // Verticals
|
|||
{ |
|||
float step = Mathf.Lerp(rect.xMin, rect.xMax,(float)i / 8); |
|||
Handles.DrawLine(new Vector2(step, area.yMin), new Vector2(step, area.yMax)); |
|||
} |
|||
|
|||
for(int i = 1; i < 4; i++) // Horizontals
|
|||
{ |
|||
float step = Mathf.Lerp(rect.yMin, rect.yMax,(float)i / 4); |
|||
Handles.DrawLine(new Vector2(area.xMin, step), new Vector2(area.xMax, step)); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
// Texts
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
Rect minInRect = new Rect(rect.xMin, rect.yMax, 40, 12); |
|||
Rect maxInRect = new Rect(rect.xMax-40, rect.yMax, 40, 12); |
|||
Rect minOutRect = new Rect(rect.xMin-40, rect.yMax-12, 40, 12); |
|||
Rect maxOutRect = new Rect(rect.xMin-40, rect.yMin, 40, 12); |
|||
|
|||
GUI.Label(minInRect, minInput.ToString("F2"), styles.smallLabelLeftAlign); |
|||
GUI.Label(maxInRect, maxInput.ToString("F2"), styles.smallLabelRightAlign); |
|||
GUI.Label(minOutRect, minOutput.ToString("F2"), styles.smallLabelRightAlign); |
|||
GUI.Label(maxOutRect, maxOutput.ToString("F2"), styles.smallLabelRightAlign); |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
// Text on Zero Axis'es
|
|||
//////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
if(minInput < 0 && maxInput > 0) |
|||
{ |
|||
Handles.color = new Color(l, l, l, 0.6f); |
|||
Handles.DrawLine(new Vector2(0,rect.yMin), new Vector2(0,rect.yMax)); |
|||
} |
|||
|
|||
if(minOutput < 0 && maxOutput > 0) |
|||
{ |
|||
Handles.color = new Color(l, l, l, 0.6f); |
|||
Handles.DrawLine(new Vector2(rect.xMin,0),new Vector2(rect.xMax, 0)); |
|||
} |
|||
|
|||
// Custom delegate
|
|||
if (OnPostGUI != null) |
|||
OnPostGUI(area, rect); |
|||
|
|||
GUI.EndClip(); |
|||
} |
|||
|
|||
#region Styles
|
|||
public Styles styles { get { if (m_Styles == null) m_Styles = new Styles(); return m_Styles; } } |
|||
private Styles m_Styles; |
|||
|
|||
public class Styles |
|||
{ |
|||
public GUIStyle smallLabelLeftAlign; |
|||
public GUIStyle smallLabelRightAlign; |
|||
|
|||
public Styles() |
|||
{ |
|||
smallLabelLeftAlign = new GUIStyle(EditorStyles.miniLabel); |
|||
smallLabelLeftAlign.alignment = TextAnchor.MiddleLeft; |
|||
smallLabelRightAlign = new GUIStyle(EditorStyles.miniLabel); |
|||
smallLabelRightAlign.alignment = TextAnchor.MiddleRight; |
|||
} |
|||
} |
|||
#endregion
|
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: e70156dc82d6e394ca81e15807862054 |
|||
timeCreated: 1473239983 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public class CurveToTextureUtility |
|||
{ |
|||
public static void CurveToTexture(AnimationCurve curve, ref Texture2D texture) |
|||
{ |
|||
if(texture != null && curve!= null && texture.height == 1 && texture.width > 1) |
|||
{ |
|||
Color[] colors = new Color[texture.width]; |
|||
for (int i = 0; i < texture.width; i++) |
|||
{ |
|||
float t = (float)i / (texture.width - 1); |
|||
float v = curve.Evaluate(t); |
|||
colors[i] = new Color(v, v, v, 1); |
|||
} |
|||
texture.SetPixels(colors); |
|||
texture.Apply(); |
|||
} |
|||
} |
|||
|
|||
public static void GradientToTexture(Gradient gradient, ref Texture2D texture, bool linear = false) |
|||
{ |
|||
if(texture != null && gradient != null && texture.height == 1 && texture.width > 1) |
|||
{ |
|||
Color[] colors = new Color[texture.width]; |
|||
for (int i = 0; i < texture.width; i++) |
|||
{ |
|||
float t = (float)i / (texture.width - 1); |
|||
if(linear) |
|||
colors[i] = gradient.Evaluate(t).linear; |
|||
else |
|||
colors[i] = gradient.Evaluate(t); |
|||
} |
|||
texture.SetPixels(colors); |
|||
texture.Apply(); |
|||
} |
|||
} |
|||
public static void GradientToTexture(Gradient gradient, AnimationCurve curve, ref Texture2D texture, bool linear = false) |
|||
{ |
|||
if (texture != null && gradient != null && texture.height == 1 && texture.width > 1) |
|||
{ |
|||
Color[] colors = new Color[texture.width]; |
|||
for (int i = 0; i < texture.width; i++) |
|||
{ |
|||
float t = (float)i / (texture.width - 1); |
|||
float b = curve.Evaluate(t); |
|||
if (linear) |
|||
colors[i] = b * gradient.Evaluate(t).linear; |
|||
else |
|||
colors[i] = b * gradient.Evaluate(t); |
|||
} |
|||
texture.SetPixels(colors); |
|||
texture.Apply(); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 99231acc997bec24abf2c850c97b869e |
|||
timeCreated: 1474882560 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using UnityEditor; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public interface IProvider |
|||
{ |
|||
void CreateComponentTree(List<FilterPopupWindow.Element> tree); |
|||
bool GoToChild(FilterPopupWindow.Element element, bool addIfComponent); |
|||
} |
|||
|
|||
[InitializeOnLoad] |
|||
public class FilterPopupWindow : EditorWindow |
|||
{ |
|||
public static readonly float DefaultWidth = 240f; |
|||
public static readonly float DefaultHeight = 300f; |
|||
|
|||
#region BaseElements
|
|||
|
|||
public class Element : IComparable |
|||
{ |
|||
public int level; |
|||
public GUIContent content; |
|||
|
|||
public string name |
|||
{ |
|||
get { return content.text; } |
|||
} |
|||
|
|||
public int CompareTo(object o) |
|||
{ |
|||
return name.CompareTo((o as Element).name); |
|||
} |
|||
} |
|||
|
|||
[Serializable] |
|||
public class GroupElement : Element |
|||
{ |
|||
public Vector2 scroll; |
|||
public int selectedIndex = 0; |
|||
|
|||
public GroupElement(int level, string name) |
|||
{ |
|||
this.level = level; |
|||
content = new GUIContent(name); |
|||
} |
|||
|
|||
public bool WantsFocus { get; protected set; } |
|||
|
|||
public virtual bool ShouldDisable |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public virtual bool HandleKeyboard(Event evt, FilterPopupWindow w, Action goToParent) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
public virtual bool OnGUI(FilterPopupWindow sFilterWindow) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
// Styles
|
|||
|
|||
class Styles |
|||
{ |
|||
public GUIStyle header = (GUIStyle)typeof(EditorStyles).GetProperty("inspectorBig", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null); |
|||
public GUIStyle componentButton = new GUIStyle("PR Label"); //new GUIStyle (EditorStyles.label);
|
|||
public GUIStyle groupButton; |
|||
public GUIStyle background = "grey_border"; |
|||
public GUIStyle previewBackground = "PopupCurveSwatchBackground"; |
|||
public GUIStyle previewHeader = new GUIStyle(EditorStyles.label); |
|||
public GUIStyle previewText = new GUIStyle(EditorStyles.wordWrappedLabel); |
|||
public GUIStyle rightArrow = "AC RightArrow"; |
|||
public GUIStyle leftArrow = "AC LeftArrow"; |
|||
|
|||
public Styles() |
|||
{ |
|||
header.font = EditorStyles.boldLabel.font; |
|||
|
|||
componentButton.alignment = TextAnchor.MiddleLeft; |
|||
componentButton.padding.left -= 15; |
|||
componentButton.fixedHeight = 20; |
|||
|
|||
groupButton = new GUIStyle(componentButton); |
|||
groupButton.padding.left += 17; |
|||
|
|||
previewText.padding.left += 3; |
|||
previewText.padding.right += 3; |
|||
previewHeader.padding.left += 3 - 2; |
|||
previewHeader.padding.right += 3; |
|||
previewHeader.padding.top += 3; |
|||
previewHeader.padding.bottom += 2; |
|||
} |
|||
} |
|||
|
|||
// Constants
|
|||
|
|||
private const int kHeaderHeight = 30; |
|||
private const int kWindowHeight = 400 - 80; |
|||
private const int kHelpHeight = 80 * 0; |
|||
private const string kComponentSearch = "NodeSearchString"; |
|||
|
|||
// Static variables
|
|||
|
|||
private static Styles s_Styles; |
|||
private static FilterPopupWindow s_FilterWindow = null; |
|||
private static long s_LastClosedTime; |
|||
private static bool s_DirtyList = false; |
|||
|
|||
// Member variables
|
|||
private IProvider m_Provider; |
|||
|
|||
private Element[] m_Tree; |
|||
private Element[] m_SearchResultTree; |
|||
private List<GroupElement> m_Stack = new List<GroupElement>(); |
|||
|
|||
private float m_Anim = 1; |
|||
private int m_AnimTarget = 1; |
|||
private long m_LastTime = 0; |
|||
private bool m_ScrollToSelected = false; |
|||
private string m_DelayedSearch = null; |
|||
private string m_Search = ""; |
|||
|
|||
// Properties
|
|||
|
|||
private bool hasSearch { get { return !string.IsNullOrEmpty(m_Search); } } |
|||
private GroupElement activeParent { get { return m_Stack[m_Stack.Count - 2 + m_AnimTarget]; } } |
|||
private Element[] activeTree { get { return hasSearch ? m_SearchResultTree : m_Tree; } } |
|||
private Element activeElement |
|||
{ |
|||
get |
|||
{ |
|||
if (activeTree == null) |
|||
return null; |
|||
|
|||
List<Element> children = GetChildren(activeTree, activeParent); |
|||
if (children.Count == 0) |
|||
return null; |
|||
|
|||
return children[activeParent.selectedIndex]; |
|||
} |
|||
} |
|||
private bool isAnimating { get { return m_Anim != m_AnimTarget; } } |
|||
|
|||
// Methods
|
|||
|
|||
static FilterPopupWindow() |
|||
{ |
|||
s_DirtyList = true; |
|||
} |
|||
|
|||
void OnEnable() |
|||
{ |
|||
s_FilterWindow = this; |
|||
m_Search = ""; |
|||
} |
|||
|
|||
void OnDisable() |
|||
{ |
|||
s_LastClosedTime = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; |
|||
s_FilterWindow = null; |
|||
} |
|||
|
|||
internal static bool ValidateAddComponentMenuItem() |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
internal static bool Show(Vector2 position, IProvider provider) |
|||
{ |
|||
// If the window is already open, close it instead.
|
|||
UnityEngine.Object[] wins = Resources.FindObjectsOfTypeAll(typeof(FilterPopupWindow)); |
|||
if (wins.Length > 0) |
|||
{ |
|||
try |
|||
{ |
|||
((EditorWindow)wins[0]).Close(); |
|||
return false; |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
s_FilterWindow = null; |
|||
} |
|||
} |
|||
|
|||
// We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time.
|
|||
long nowMilliSeconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; |
|||
bool justClosed = nowMilliSeconds < s_LastClosedTime + 50; |
|||
if (!justClosed) |
|||
{ |
|||
Event.current.Use(); |
|||
if (s_FilterWindow == null) |
|||
s_FilterWindow = ScriptableObject.CreateInstance<FilterPopupWindow>(); |
|||
s_FilterWindow.Init(position, provider); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private static object Invoke(Type t, object inst, string method, params object[] args) |
|||
{ |
|||
var mi = t.GetMethod(method, (inst == null ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.NonPublic); |
|||
return mi.Invoke(inst, args); |
|||
} |
|||
|
|||
void Init(Vector2 position, IProvider provider) |
|||
{ |
|||
m_Provider = provider; |
|||
// Has to be done before calling Show / ShowWithMode
|
|||
Vector2 pos = GUIUtility.GUIToScreenPoint(position); |
|||
Rect buttonRect = new Rect(pos.x - 32, pos.y - 16, DefaultWidth, 1); |
|||
|
|||
CreateComponentTree(); |
|||
|
|||
ShowAsDropDown(buttonRect, new Vector2(buttonRect.width, kWindowHeight)); |
|||
|
|||
Focus(); |
|||
|
|||
wantsMouseMove = true; |
|||
} |
|||
|
|||
private void CreateComponentTree() |
|||
{ |
|||
var tree = new List<Element>(); |
|||
m_Provider.CreateComponentTree(tree); |
|||
|
|||
|
|||
m_Tree = tree.ToArray(); |
|||
|
|||
// Rebuild stack
|
|||
if (m_Stack.Count == 0) |
|||
m_Stack.Add(m_Tree[0] as GroupElement); |
|||
else |
|||
{ |
|||
// The root is always the match for level 0
|
|||
GroupElement match = m_Tree[0] as GroupElement; |
|||
int level = 0; |
|||
while (true) |
|||
{ |
|||
// Assign the match for the current level
|
|||
GroupElement oldElement = m_Stack[level]; |
|||
m_Stack[level] = match; |
|||
m_Stack[level].selectedIndex = oldElement.selectedIndex; |
|||
m_Stack[level].scroll = oldElement.scroll; |
|||
|
|||
// See if we reached last element of stack
|
|||
level++; |
|||
if (level == m_Stack.Count) |
|||
break; |
|||
|
|||
// Try to find a child of the same name as we had before
|
|||
List<Element> children = GetChildren(activeTree, match); |
|||
Element childMatch = children.FirstOrDefault(c => c.name == m_Stack[level].name); |
|||
if (childMatch != null && childMatch is GroupElement) |
|||
{ |
|||
match = childMatch as GroupElement; |
|||
} |
|||
else |
|||
{ |
|||
// If we couldn't find the child, remove all further elements from the stack
|
|||
while (m_Stack.Count > level) |
|||
m_Stack.RemoveAt(level); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Debug.Log ("Rebuilt tree - "+m_Tree.Length+" elements");
|
|||
s_DirtyList = false; |
|||
RebuildSearch(); |
|||
} |
|||
|
|||
internal void OnGUI() |
|||
{ |
|||
if (s_Styles == null) |
|||
s_Styles = new Styles(); |
|||
|
|||
GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, s_Styles.background); |
|||
|
|||
|
|||
if (s_DirtyList) |
|||
CreateComponentTree(); |
|||
|
|||
// Keyboard
|
|||
HandleKeyboard(); |
|||
|
|||
GUILayout.Space(7); |
|||
|
|||
// Search
|
|||
if (!(activeParent.WantsFocus)) |
|||
EditorGUI.FocusTextInControl("ComponentSearch"); |
|||
Rect searchRect = GUILayoutUtility.GetRect(10, 20); |
|||
searchRect.x += 8; |
|||
searchRect.width -= 16; |
|||
|
|||
GUI.SetNextControlName("ComponentSearch"); |
|||
|
|||
using (new DisabledScope(activeParent.ShouldDisable)) |
|||
{ |
|||
string newSearch = (string)Invoke(typeof(EditorGUI), null, "SearchField", searchRect, m_DelayedSearch ?? m_Search); |
|||
|
|||
if (newSearch != m_Search || m_DelayedSearch != null) |
|||
{ |
|||
if (!isAnimating) |
|||
{ |
|||
m_Search = m_DelayedSearch ?? newSearch; |
|||
EditorPrefs.SetString(kComponentSearch, m_Search); |
|||
RebuildSearch(); |
|||
m_DelayedSearch = null; |
|||
} |
|||
else |
|||
{ |
|||
m_DelayedSearch = newSearch; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Show lists
|
|||
ListGUI(activeTree, m_Anim, GetElementRelative(0), GetElementRelative(-1)); |
|||
if (m_Anim < 1) |
|||
ListGUI(activeTree, m_Anim + 1, GetElementRelative(-1), GetElementRelative(-2)); |
|||
|
|||
// Show help area
|
|||
//DrawHelpArea (new Rect (0, position.height - kHelpHeight, position.width, kHelpHeight));
|
|||
|
|||
// Animate
|
|||
if (isAnimating && Event.current.type == EventType.Repaint) |
|||
{ |
|||
long now = System.DateTime.Now.Ticks; |
|||
float deltaTime = (now - m_LastTime) / (float)System.TimeSpan.TicksPerSecond; |
|||
m_LastTime = now; |
|||
m_Anim = Mathf.MoveTowards(m_Anim, m_AnimTarget, deltaTime * 4); |
|||
if (m_AnimTarget == 0 && m_Anim == 0) |
|||
{ |
|||
m_Anim = 1; |
|||
m_AnimTarget = 1; |
|||
m_Stack.RemoveAt(m_Stack.Count - 1); |
|||
} |
|||
Repaint(); |
|||
} |
|||
} |
|||
|
|||
private void HandleKeyboard() |
|||
{ |
|||
Event evt = Event.current; |
|||
if (evt.type == EventType.KeyDown) |
|||
{ |
|||
// Special handling when in new script panel
|
|||
if (!activeParent.HandleKeyboard(evt, s_FilterWindow, GoToParent)) |
|||
{ |
|||
// Always do these
|
|||
if (evt.keyCode == KeyCode.DownArrow) |
|||
{ |
|||
activeParent.selectedIndex++; |
|||
activeParent.selectedIndex = Mathf.Min(activeParent.selectedIndex, GetChildren(activeTree, activeParent).Count - 1); |
|||
m_ScrollToSelected = true; |
|||
evt.Use(); |
|||
} |
|||
if (evt.keyCode == KeyCode.UpArrow) |
|||
{ |
|||
activeParent.selectedIndex--; |
|||
activeParent.selectedIndex = Mathf.Max(activeParent.selectedIndex, 0); |
|||
m_ScrollToSelected = true; |
|||
evt.Use(); |
|||
} |
|||
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter) |
|||
{ |
|||
GoToChild(activeElement, true); |
|||
evt.Use(); |
|||
} |
|||
|
|||
// Do these if we're not in search mode
|
|||
if (!hasSearch) |
|||
{ |
|||
if (evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.Backspace) |
|||
{ |
|||
GoToParent(); |
|||
evt.Use(); |
|||
} |
|||
if (evt.keyCode == KeyCode.RightArrow) |
|||
{ |
|||
GoToChild(activeElement, false); |
|||
evt.Use(); |
|||
} |
|||
if (evt.keyCode == KeyCode.Escape) |
|||
{ |
|||
Close(); |
|||
evt.Use(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
const string kSearchHeader = "Search"; |
|||
|
|||
private void RebuildSearch() |
|||
{ |
|||
if (!hasSearch) |
|||
{ |
|||
m_SearchResultTree = null; |
|||
if (m_Stack[m_Stack.Count - 1].name == kSearchHeader) |
|||
{ |
|||
m_Stack.Clear(); |
|||
m_Stack.Add(m_Tree[0] as GroupElement); |
|||
} |
|||
m_AnimTarget = 1; |
|||
m_LastTime = System.DateTime.Now.Ticks; |
|||
return; |
|||
} |
|||
|
|||
// Support multiple search words separated by spaces.
|
|||
string[] searchWords = m_Search.ToLower().Split(' '); |
|||
|
|||
// We keep two lists. Matches that matches the start of an item always get first priority.
|
|||
List<Element> matchesStart = new List<Element>(); |
|||
List<Element> matchesWithin = new List<Element>(); |
|||
|
|||
foreach (Element e in m_Tree) |
|||
{ |
|||
if ((e is GroupElement)) //TODO RF
|
|||
continue; |
|||
|
|||
string name = e.name.ToLower().Replace(" ", ""); |
|||
bool didMatchAll = true; |
|||
bool didMatchStart = false; |
|||
|
|||
// See if we match ALL the seaarch words.
|
|||
for (int w = 0; w < searchWords.Length; w++) |
|||
{ |
|||
string search = searchWords[w]; |
|||
if (name.Contains(search)) |
|||
{ |
|||
// If the start of the item matches the first search word, make a note of that.
|
|||
if (w == 0 && name.StartsWith(search)) |
|||
didMatchStart = true; |
|||
} |
|||
else |
|||
{ |
|||
// As soon as any word is not matched, we disregard this item.
|
|||
didMatchAll = false; |
|||
break; |
|||
} |
|||
} |
|||
// We always need to match all search words.
|
|||
// If we ALSO matched the start, this item gets priority.
|
|||
if (didMatchAll) |
|||
{ |
|||
if (didMatchStart) |
|||
matchesStart.Add(e); |
|||
else |
|||
matchesWithin.Add(e); |
|||
} |
|||
} |
|||
|
|||
matchesStart.Sort(); |
|||
matchesWithin.Sort(); |
|||
|
|||
// Create search tree
|
|||
List<Element> tree = new List<Element>(); |
|||
// Add parent
|
|||
tree.Add(new GroupElement(0, kSearchHeader)); |
|||
// Add search results
|
|||
tree.AddRange(matchesStart); |
|||
tree.AddRange(matchesWithin); |
|||
// Add the new script element
|
|||
//tree.Add(m_Tree[m_Tree.Length - 1]);
|
|||
// Create search result tree
|
|||
m_SearchResultTree = tree.ToArray(); |
|||
m_Stack.Clear(); |
|||
m_Stack.Add(m_SearchResultTree[0] as GroupElement); |
|||
|
|||
// Always select the first search result when search is changed (e.g. a character was typed in or deleted),
|
|||
// because it's usually the best match.
|
|||
if (GetChildren(activeTree, activeParent).Count >= 1) |
|||
activeParent.selectedIndex = 0; |
|||
else |
|||
activeParent.selectedIndex = -1; |
|||
} |
|||
|
|||
private GroupElement GetElementRelative(int rel) |
|||
{ |
|||
int i = m_Stack.Count + rel - 1; |
|||
if (i < 0) |
|||
return null; |
|||
return m_Stack[i] as GroupElement; |
|||
} |
|||
|
|||
private void GoToParent() |
|||
{ |
|||
if (m_Stack.Count > 1) |
|||
{ |
|||
m_AnimTarget = 0; |
|||
m_LastTime = System.DateTime.Now.Ticks; |
|||
} |
|||
} |
|||
|
|||
private void ListGUI(Element[] tree, float anim, GroupElement parent, GroupElement grandParent) |
|||
{ |
|||
// Smooth the fractional part of the anim value
|
|||
anim = Mathf.Floor(anim) + Mathf.SmoothStep(0, 1, Mathf.Repeat(anim, 1)); |
|||
|
|||
// Calculate rect for animated area
|
|||
Rect animRect = position; |
|||
animRect.x = position.width * (1 - anim) + 1; |
|||
animRect.y = kHeaderHeight; |
|||
animRect.height -= kHeaderHeight + kHelpHeight; |
|||
animRect.width -= 2; |
|||
|
|||
// Start of animated area (the part that moves left and right)
|
|||
GUILayout.BeginArea(animRect); |
|||
|
|||
// Header
|
|||
Rect headerRect = GUILayoutUtility.GetRect(10, 25); |
|||
string name = parent.name; |
|||
GUI.Label(headerRect, name, s_Styles.header); |
|||
|
|||
// Back button
|
|||
if (grandParent != null) |
|||
{ |
|||
Rect arrowRect = new Rect(headerRect.x + 4, headerRect.y + 7, 13, 13); |
|||
if (Event.current.type == EventType.Repaint) |
|||
s_Styles.leftArrow.Draw(arrowRect, false, false, false, false); |
|||
if (Event.current.type == EventType.MouseDown && headerRect.Contains(Event.current.mousePosition)) |
|||
{ |
|||
GoToParent(); |
|||
Event.current.Use(); |
|||
} |
|||
} |
|||
|
|||
//GUILayout.Space (10);
|
|||
|
|||
if (!parent.OnGUI(s_FilterWindow)) |
|||
ListGUI(tree, parent); |
|||
|
|||
GUILayout.EndArea(); |
|||
} |
|||
|
|||
private void GoToChild(Element e, bool addIfComponent) |
|||
{ |
|||
if(m_Provider.GoToChild(e, addIfComponent)) |
|||
Close(); |
|||
else if (!hasSearch)//TODO RF || e is NewElement)
|
|||
{ |
|||
m_LastTime = System.DateTime.Now.Ticks; |
|||
if (m_AnimTarget == 0) |
|||
m_AnimTarget = 1; |
|||
else if (m_Anim == 1) |
|||
{ |
|||
m_Anim = 0; |
|||
m_Stack.Add(e as FilterPopupWindow.GroupElement); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
private void ListGUI(Element[] tree, GroupElement parent) |
|||
{ |
|||
// Start of scroll view list
|
|||
parent.scroll = GUILayout.BeginScrollView(parent.scroll); |
|||
|
|||
EditorGUIUtility.SetIconSize(new Vector2(16, 16)); |
|||
|
|||
List<Element> children = GetChildren(tree, parent); |
|||
|
|||
Rect selectedRect = new Rect(); |
|||
|
|||
|
|||
// Iterate through the children
|
|||
for (int i = 0; i < children.Count; i++) |
|||
{ |
|||
Element e = children[i]; |
|||
Rect r = GUILayoutUtility.GetRect(16, 20, GUILayout.ExpandWidth(true)); |
|||
|
|||
// Select the element the mouse cursor is over.
|
|||
// Only do it on mouse move - keyboard controls are allowed to overwrite this until the next time the mouse moves.
|
|||
if (Event.current.type == EventType.MouseMove || Event.current.type == EventType.MouseDown) |
|||
{ |
|||
if (parent.selectedIndex != i && r.Contains(Event.current.mousePosition)) |
|||
{ |
|||
parent.selectedIndex = i; |
|||
Repaint(); |
|||
} |
|||
} |
|||
|
|||
bool selected = false; |
|||
// Handle selected item
|
|||
if (i == parent.selectedIndex) |
|||
{ |
|||
selected = true; |
|||
selectedRect = r; |
|||
} |
|||
|
|||
// Draw element
|
|||
if (Event.current.type == EventType.Repaint) |
|||
{ |
|||
GUIStyle labelStyle = (e is GroupElement) ? s_Styles.groupButton : s_Styles.componentButton; |
|||
labelStyle.Draw(r, e.content, false, false, selected, selected); |
|||
if ((e is GroupElement)) |
|||
{ |
|||
Rect arrowRect = new Rect(r.x + r.width - 13, r.y + 4, 13, 13); |
|||
s_Styles.rightArrow.Draw(arrowRect, false, false, false, false); |
|||
} |
|||
} |
|||
if (Event.current.type == EventType.MouseDown && r.Contains(Event.current.mousePosition)) |
|||
{ |
|||
Event.current.Use(); |
|||
parent.selectedIndex = i; |
|||
GoToChild(e, true); |
|||
} |
|||
} |
|||
|
|||
EditorGUIUtility.SetIconSize(Vector2.zero); |
|||
|
|||
GUILayout.EndScrollView(); |
|||
|
|||
// Scroll to show selected
|
|||
if (m_ScrollToSelected && Event.current.type == EventType.Repaint) |
|||
{ |
|||
m_ScrollToSelected = false; |
|||
Rect scrollRect = GUILayoutUtility.GetLastRect(); |
|||
if (selectedRect.yMax - scrollRect.height > parent.scroll.y) |
|||
{ |
|||
parent.scroll.y = selectedRect.yMax - scrollRect.height; |
|||
Repaint(); |
|||
} |
|||
if (selectedRect.y < parent.scroll.y) |
|||
{ |
|||
parent.scroll.y = selectedRect.y; |
|||
Repaint(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private List<Element> GetChildren(Element[] tree, Element parent) |
|||
{ |
|||
List<Element> children = new List<Element>(); |
|||
int level = -1; |
|||
int i = 0; |
|||
for (i = 0; i < tree.Length; i++) |
|||
{ |
|||
if (tree[i] == parent) |
|||
{ |
|||
level = parent.level + 1; |
|||
i++; |
|||
break; |
|||
} |
|||
} |
|||
if (level == -1) |
|||
return children; |
|||
|
|||
for (; i < tree.Length; i++) |
|||
{ |
|||
Element e = tree[i]; |
|||
|
|||
if (e.level < level) |
|||
break; |
|||
if (e.level > level && !hasSearch) |
|||
continue; |
|||
|
|||
children.Add(e); |
|||
} |
|||
|
|||
return children; |
|||
} |
|||
|
|||
} |
|||
|
|||
public struct DisabledScope : IDisposable |
|||
{ |
|||
private static Stack<bool> s_EnabledStack = new Stack<bool>(); |
|||
bool m_Disposed; |
|||
|
|||
public DisabledScope(bool disabled) |
|||
{ |
|||
m_Disposed = false; |
|||
|
|||
s_EnabledStack.Push(GUI.enabled); |
|||
GUI.enabled &= !disabled; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (m_Disposed) |
|||
return; |
|||
m_Disposed = true; |
|||
if (s_EnabledStack.Count > 0) |
|||
GUI.enabled = s_EnabledStack.Pop(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: cf782fd1355b6a14bb9f026693fe111c |
|||
timeCreated: 1485512158 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEngine.VFXToolbox; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
[CustomPropertyDrawer(typeof(FloatSliderAttribute))] |
|||
public class FloatSliderPropertyDrawer : PropertyDrawer |
|||
{ |
|||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) |
|||
{ |
|||
FloatSliderAttribute floatSliderAttribute = attribute as FloatSliderAttribute; |
|||
|
|||
if(property.propertyType == SerializedPropertyType.Float) |
|||
{ |
|||
EditorGUI.Slider(position, property, floatSliderAttribute.m_ValueMin, floatSliderAttribute.m_ValueMax); |
|||
} |
|||
else |
|||
EditorGUI.LabelField(position, label, "(FloatSliderProperty can only be used with float attributes)"); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b906391f203061740a413d469a9fa304 |
|||
timeCreated: 1471966526 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 175bf1ad95f5c7240b11877e3eced98b |
|||
folderAsset: yes |
|||
timeCreated: 1475654884 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/VFXToolbox/BlitRect" |
|||
{ |
|||
Properties |
|||
{ |
|||
_MainTex ("Texture", 2D) = "white" {} |
|||
_Rect ("Rect", Vector) = (0.0,0.0,1.0,1.0) |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} |
|||
LOD 100 |
|||
|
|||
Cull Off |
|||
ZWrite Off |
|||
Blend SrcAlpha OneMinusSrcAlpha |
|||
|
|||
Pass { |
|||
CGPROGRAM |
|||
#pragma vertex vert |
|||
#pragma fragment frag |
|||
|
|||
#include "UnityCG.cginc" |
|||
|
|||
struct appdata_t { |
|||
float4 vertex : POSITION; |
|||
}; |
|||
|
|||
struct v2f { |
|||
float4 vertex : SV_POSITION; |
|||
half2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
sampler2D _MainTex; |
|||
float4 _Rect; |
|||
|
|||
v2f vert (appdata_t v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = v.vertex; |
|||
o.vertex.xy = (v.vertex.xy / _Rect.zw) - _Rect.xy; |
|||
o.texcoord = o.vertex; |
|||
o.vertex.y = 1.0f - o.vertex.y; |
|||
o.vertex = o.vertex * 2 - 1; |
|||
return o; |
|||
} |
|||
|
|||
fixed4 frag (v2f i) : SV_Target |
|||
{ |
|||
fixed4 col = tex2D(_MainTex, i.texcoord); |
|||
return col; |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 8c674c47209b57a4ab266f4371e5501c |
|||
timeCreated: 1475655065 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System; |
|||
using System.IO; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public class Splitter |
|||
{ |
|||
public enum SplitLockMode |
|||
{ |
|||
None = 0, |
|||
BothMinSize = 1, |
|||
LeftMinMax = 2, |
|||
RightMinMax = 3 |
|||
} |
|||
|
|||
public float value |
|||
{ |
|||
get { return m_SplitterValue; } |
|||
set { SetSplitterValue(value); } |
|||
} |
|||
|
|||
public delegate void SplitViewOnGUIDelegate(Rect drawRect); |
|||
|
|||
private SplitViewOnGUIDelegate m_onDrawLeftDelegate; |
|||
private SplitViewOnGUIDelegate m_onDrawRightDelegate; |
|||
|
|||
private float m_SplitterValue; |
|||
private bool m_Resize; |
|||
private SplitLockMode m_LockMode; |
|||
private Vector2 m_LockValues; |
|||
|
|||
public Splitter(float initialLeftWidth, SplitViewOnGUIDelegate onDrawLeftDelegate, SplitViewOnGUIDelegate onDrawRightDelegate, SplitLockMode lockMode, Vector2 lockValues) |
|||
{ |
|||
m_SplitterValue = initialLeftWidth; |
|||
m_onDrawLeftDelegate = onDrawLeftDelegate; |
|||
m_onDrawRightDelegate = onDrawRightDelegate; |
|||
m_LockMode = lockMode; |
|||
|
|||
if (((int)lockMode > 1) && (lockValues.y < lockValues.x)) |
|||
m_LockValues = new Vector2(lockValues.y, lockValues.x); |
|||
else |
|||
m_LockValues = lockValues; |
|||
|
|||
} |
|||
|
|||
public bool DoSplitter(Rect rect) |
|||
{ |
|||
if(m_onDrawLeftDelegate != null) |
|||
{ |
|||
m_onDrawLeftDelegate(new Rect(rect.x, rect.y, m_SplitterValue, rect.height)); |
|||
} |
|||
|
|||
if(m_onDrawRightDelegate != null) |
|||
{ |
|||
m_onDrawRightDelegate(new Rect(rect.x + m_SplitterValue, rect.y, rect.width - m_SplitterValue, rect.height)); |
|||
} |
|||
|
|||
HandlePanelResize(rect); |
|||
|
|||
return m_Resize; |
|||
} |
|||
|
|||
private void SetSplitterValue(float Value) |
|||
{ |
|||
m_SplitterValue = Value; |
|||
} |
|||
|
|||
private void HandlePanelResize(Rect rect) |
|||
{ |
|||
Rect resizeActiveArea = new Rect(rect.x + m_SplitterValue - 8, rect.y, 16, rect.height); |
|||
|
|||
EditorGUIUtility.AddCursorRect(resizeActiveArea, MouseCursor.ResizeHorizontal); |
|||
|
|||
if (Event.current.type == EventType.MouseDown && resizeActiveArea.Contains(Event.current.mousePosition)) |
|||
m_Resize = true; |
|||
|
|||
if (m_Resize) |
|||
{ |
|||
value = Event.current.mousePosition.x; |
|||
} |
|||
|
|||
switch(m_LockMode) |
|||
{ |
|||
case SplitLockMode.BothMinSize: |
|||
m_SplitterValue = Mathf.Clamp(m_SplitterValue, m_LockValues.x, rect.width - m_LockValues.y); |
|||
break; |
|||
case SplitLockMode.LeftMinMax: |
|||
m_SplitterValue = Mathf.Clamp(m_SplitterValue, m_LockValues.x, m_LockValues.y); |
|||
break; |
|||
case SplitLockMode.RightMinMax: |
|||
m_SplitterValue = Mathf.Clamp(m_SplitterValue, rect.width - m_LockValues.y, rect.width - m_LockValues.x); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
RectOffset o = new RectOffset(7, 8, 0, 0); |
|||
EditorGUI.DrawRect(o.Remove(resizeActiveArea), new Color(0,0,0,1.0f)); |
|||
if (Event.current.type == EventType.MouseUp) |
|||
m_Resize = false; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a7133f5114a741e44b47d1111f070e39 |
|||
timeCreated: 1476694621 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
class VFXToolboxGUIUtility |
|||
{ |
|||
#region GUIContent caching
|
|||
private static Dictionary<string, GUIContent> s_GUIContentCache; |
|||
|
|||
public static GUIContent Get(string textAndTooltip) |
|||
{ |
|||
return GetTextAndIcon(textAndTooltip, null); |
|||
} |
|||
|
|||
public static GUIContent GetTextAndIcon(string textAndTooltip, string icon) |
|||
{ |
|||
if (s_GUIContentCache == null) |
|||
s_GUIContentCache = new Dictionary<string, GUIContent>(); |
|||
|
|||
if (string.IsNullOrEmpty(textAndTooltip)) |
|||
return GUIContent.none; |
|||
|
|||
GUIContent content; |
|||
|
|||
if (!s_GUIContentCache.TryGetValue(textAndTooltip, out content)) |
|||
{ |
|||
var s = textAndTooltip.Split('|'); |
|||
|
|||
if (!string.IsNullOrEmpty(icon)) |
|||
{ |
|||
var iconContent = EditorGUIUtility.IconContent(icon); |
|||
content = new GUIContent(s[0], iconContent.image); |
|||
} |
|||
else |
|||
{ |
|||
content = new GUIContent(s[0]); |
|||
} |
|||
|
|||
if (s.Length > 1 && !string.IsNullOrEmpty(s[1])) |
|||
content.tooltip = s[1]; |
|||
|
|||
s_GUIContentCache.Add(textAndTooltip, content); |
|||
} |
|||
|
|||
return content; |
|||
|
|||
} |
|||
|
|||
public static void Clear() |
|||
{ |
|||
s_GUIContentCache.Clear(); |
|||
} |
|||
#endregion
|
|||
|
|||
#region ProgressBar Handling
|
|||
|
|||
private static double s_LastProgressBarTime; |
|||
|
|||
/// <summary>
|
|||
/// Displays a progress bar with delay and optional cancel button
|
|||
/// </summary>
|
|||
/// <param name="title">title of the window</param>
|
|||
/// <param name="message">message</param>
|
|||
/// <param name="progress">progress</param>
|
|||
/// <param name="delay">minimum delay before displaying window</param>
|
|||
/// <param name="cancelable">is the window cancellable?</param>
|
|||
/// <returns>true if cancelled, false otherwise</returns>
|
|||
public static bool DisplayProgressBar(string title, string message, float progress, float delay = 0.0f, bool cancelable = false) |
|||
{ |
|||
if(s_LastProgressBarTime < 0.0) |
|||
s_LastProgressBarTime = EditorApplication.timeSinceStartup; |
|||
|
|||
if (EditorApplication.timeSinceStartup - s_LastProgressBarTime > delay) |
|||
{ |
|||
if(cancelable) |
|||
{ |
|||
return EditorUtility.DisplayCancelableProgressBar(title, message, progress); |
|||
} |
|||
else |
|||
{ |
|||
EditorUtility.DisplayProgressBar(title, message, progress); |
|||
return false; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the current progressbar
|
|||
/// </summary>
|
|||
public static void ClearProgressBar() |
|||
{ |
|||
s_LastProgressBarTime = -1.0; |
|||
EditorUtility.ClearProgressBar(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Other GUI Utils
|
|||
public static void GUIRotatedLabel(Rect position, string label, float angle, GUIStyle style) |
|||
{ |
|||
var matrix = GUI.matrix; |
|||
var rect = new Rect(position.x - 10f, position.y, position.width, position.height); |
|||
GUIUtility.RotateAroundPivot(angle, rect.center); |
|||
GUI.Label(rect, label, style); |
|||
GUI.matrix = matrix; |
|||
} |
|||
#endregion
|
|||
|
|||
#region ToggleableHeader
|
|||
|
|||
public static bool ToggleableHeader(bool enabled, bool bToggleable, string title) |
|||
{ |
|||
Rect rect = GUILayoutUtility.GetRect(16f, 32f, VFXToolboxStyles.Header); |
|||
using (new EditorGUI.DisabledGroupScope(!enabled)) |
|||
{ |
|||
GUI.Box(rect, title, VFXToolboxStyles.Header); |
|||
} |
|||
if(bToggleable) |
|||
{ |
|||
Rect toggleRect = new Rect(rect.x + 10f, rect.y + 6f, 13f, 13f); |
|||
if (Event.current.type == EventType.Repaint) |
|||
VFXToolboxStyles.HeaderCheckBox.Draw(toggleRect, false, false, enabled, false); |
|||
|
|||
Event e = Event.current; |
|||
if (e.type == EventType.MouseDown) |
|||
{ |
|||
if (toggleRect.Contains(e.mousePosition)) |
|||
{ |
|||
enabled = !enabled; |
|||
e.Use(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return enabled; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Tabbed Buttons
|
|||
public static int TabbedButtonsGUILayout(int value, string[] labels, bool[] enabled) |
|||
{ |
|||
int count = labels.Length; |
|||
|
|||
int selected = value; |
|||
|
|||
if (labels.Length != enabled.Length) |
|||
throw new ArgumentException("Labels or enabled arrays does not match count for EnumTabbedButtons()"); |
|||
|
|||
int i = 0; |
|||
|
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
foreach(string label in labels) |
|||
{ |
|||
GUIStyle style = (i == 0) ? VFXToolboxStyles.TabButtonLeft : ((i == count - 1) ? VFXToolboxStyles.TabButtonRight : VFXToolboxStyles.TabButtonMid); |
|||
using (new EditorGUI.DisabledScope(!enabled[i])) |
|||
{ |
|||
bool val = GUILayout.Toggle(selected == i, Get(label), style, GUILayout.Height(24)); |
|||
if(val != (selected == i)) |
|||
{ |
|||
selected = i; |
|||
} |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
return selected; |
|||
} |
|||
#endregion
|
|||
|
|||
#region Custom Scopes
|
|||
|
|||
public class HeaderSectionScope : GUI.Scope |
|||
{ |
|||
public HeaderSectionScope(string headerText) |
|||
{ |
|||
EditorGUILayout.LabelField(Get(headerText), s_Styles.header); |
|||
EditorGUI.indentLevel += 1; |
|||
} |
|||
|
|||
protected override void CloseScope() |
|||
{ |
|||
EditorGUI.indentLevel -= 1; |
|||
GUILayout.Label(GUIContent.none, s_Styles.separator); |
|||
} |
|||
|
|||
static Styles s_Styles = new Styles(); |
|||
|
|||
private class Styles |
|||
{ |
|||
public GUIStyle header; |
|||
public GUIStyle separator; |
|||
|
|||
public Styles() |
|||
{ |
|||
header = new GUIStyle(EditorStyles.boldLabel); |
|||
header.margin = new RectOffset(0, 0, 0, 12); |
|||
separator = new GUIStyle("sv_iconselector_sep"); |
|||
separator.margin = new RectOffset(0, 0, 8, 0); |
|||
} |
|||
} |
|||
} |
|||
#endregion
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 46694eeb11886f24ab5b4ab6f92265e9 |
|||
timeCreated: 1471966525 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public static class VFXToolboxStyles |
|||
{ |
|||
// Custom Toggleable Header (in VFXToolboxGUIUtility)
|
|||
public static GUIStyle Header; |
|||
public static GUIStyle HeaderCheckBox; |
|||
|
|||
// Tab Buttons
|
|||
public static GUIStyle TabButtonLeft; |
|||
public static GUIStyle TabButtonSingle; |
|||
public static GUIStyle TabButtonMid; |
|||
public static GUIStyle TabButtonRight; |
|||
|
|||
// Toolbar Related
|
|||
public static GUIStyle toolbarButton; |
|||
public static GUIStyle toolbarTextField; |
|||
public static GUIStyle toolbarLabelLeft; |
|||
|
|||
// Labels
|
|||
public static GUIStyle LargeLabel; |
|||
public static GUIStyle miniLabel; |
|||
public static GUIStyle miniLabelRight; |
|||
public static GUIStyle miniLabelCenter; |
|||
|
|||
// Misc
|
|||
public static GUIStyle RListLabel; |
|||
|
|||
static VFXToolboxStyles() |
|||
{ |
|||
Header = new GUIStyle("ShurikenModuleTitle"); |
|||
HeaderCheckBox = new GUIStyle("ShurikenCheckMark"); |
|||
|
|||
Header.font = (new GUIStyle("Label")).font; |
|||
Header.fontSize = 12; |
|||
Header.fontStyle = FontStyle.Bold; |
|||
Header.border = new RectOffset(15, 7, 4, 4); |
|||
Header.margin = new RectOffset(0, 0, 16, 0); |
|||
Header.fixedHeight = 28; |
|||
Header.contentOffset = new Vector2(32f, -2f); |
|||
|
|||
TabButtonSingle = new GUIStyle(EditorStyles.miniButton); |
|||
TabButtonLeft = new GUIStyle(EditorStyles.miniButtonLeft); |
|||
TabButtonMid = new GUIStyle(EditorStyles.miniButtonMid); |
|||
TabButtonRight = new GUIStyle(EditorStyles.miniButtonRight); |
|||
TabButtonSingle.fontSize = 12; |
|||
TabButtonLeft.fontSize = 12; |
|||
TabButtonMid.fontSize = 12; |
|||
TabButtonRight.fontSize = 12; |
|||
|
|||
LargeLabel = new GUIStyle(EditorStyles.largeLabel); |
|||
RListLabel = new GUIStyle(EditorStyles.label); |
|||
|
|||
toolbarButton = new GUIStyle(EditorStyles.toolbarButton); |
|||
toolbarButton.padding = new RectOffset(); |
|||
toolbarButton.margin = new RectOffset(); |
|||
|
|||
toolbarLabelLeft = new GUIStyle(EditorStyles.miniLabel); |
|||
toolbarLabelLeft.alignment = TextAnchor.MiddleLeft; |
|||
toolbarLabelLeft.contentOffset = new Vector2(-2, -4); |
|||
|
|||
toolbarTextField = new GUIStyle(EditorStyles.toolbarTextField); |
|||
toolbarTextField.padding = new RectOffset(2,2,2,2); |
|||
toolbarTextField.margin = new RectOffset(2,2,2,2); |
|||
|
|||
LargeLabel.alignment = TextAnchor.UpperRight; |
|||
|
|||
miniLabel = new GUIStyle(EditorStyles.miniLabel); |
|||
miniLabelRight = new GUIStyle(EditorStyles.miniLabel); |
|||
miniLabelRight.alignment = TextAnchor.MiddleRight; |
|||
miniLabelCenter = new GUIStyle(EditorStyles.miniLabel); |
|||
miniLabelCenter.alignment = TextAnchor.MiddleCenter; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: 49a77d36083c9474e9611de5a530eb91 |
|||
timeCreated: 1478251652 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System; |
|||
using System.IO; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public class VFXToolboxUtility |
|||
{ |
|||
#region Readback utils
|
|||
|
|||
public static Color[] ReadBack(RenderTexture renderTexture) |
|||
{ |
|||
RenderTexture backup = RenderTexture.active; |
|||
RenderTexture.active = renderTexture; |
|||
|
|||
bool hdr = false; |
|||
if (renderTexture.format == RenderTextureFormat.ARGBHalf) |
|||
hdr = true; |
|||
|
|||
Texture2D texture = new Texture2D(renderTexture.width, renderTexture.height, hdr ? TextureFormat.RGBAHalf : TextureFormat.RGBA32, false); |
|||
texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); |
|||
//texture.Apply();
|
|||
RenderTexture.active = backup; |
|||
|
|||
return texture.GetPixels(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Asset Utils
|
|||
|
|||
public static bool IsDirectory(string path) |
|||
{ |
|||
if (path.Length > 0 && Directory.Exists(path)) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
public static bool IsDirectorySelected() |
|||
{ |
|||
var path = ""; |
|||
var obj = Selection.activeObject; |
|||
if (obj == null) path = "Assets"; |
|||
else path = AssetDatabase.GetAssetPath(obj.GetInstanceID()); |
|||
return IsDirectory(path); |
|||
} |
|||
|
|||
public static string[] GetAllTexturesInPath(string path) |
|||
{ |
|||
List<string> files = new List<string>(); |
|||
string absolutePath = Application.dataPath + "/" + path.Remove(0, 7); |
|||
string [] fileEntries = Directory.GetFiles(absolutePath); |
|||
int count = fileEntries.Length; |
|||
int i = 0; |
|||
foreach(string fileName in fileEntries) |
|||
{ |
|||
string fname = fileName.Replace('\\', '/'); |
|||
int index = fname.LastIndexOf('/'); |
|||
string localPath = path; |
|||
if (index > 0) |
|||
localPath += fname.Substring(index); |
|||
VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Discovering Assets in folder...", (float)i/count); |
|||
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(localPath); |
|||
if(t != null) |
|||
files.Add(localPath); |
|||
i++; |
|||
} |
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
return files.ToArray(); |
|||
} |
|||
#endregion
|
|||
|
|||
#region ReflectionUtils
|
|||
|
|||
public static IEnumerable<Type> FindConcreteSubclasses<T>() |
|||
{ |
|||
List<Type> types = new List<Type>(); |
|||
foreach (var domainAssembly in AppDomain.CurrentDomain.GetAssemblies()) |
|||
{ |
|||
Type[] assemblyTypes = null; |
|||
try |
|||
{ |
|||
assemblyTypes = domainAssembly.GetTypes(); |
|||
} |
|||
catch(Exception) |
|||
{ |
|||
Debug.LogWarning("Cannot access assembly: " + domainAssembly); |
|||
assemblyTypes = null; |
|||
} |
|||
if (assemblyTypes != null) |
|||
foreach (var assemblyType in assemblyTypes) |
|||
if (assemblyType.IsSubclassOf(typeof(T)) && !assemblyType.IsAbstract) |
|||
types.Add (assemblyType); |
|||
} |
|||
return types; |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region GraphicUtils
|
|||
|
|||
public static void BlitRect(Rect rect, RenderTexture target, Texture texture, Material material = null) |
|||
{ |
|||
RenderTexture backup = RenderTexture.active; |
|||
RenderTexture.active = target; |
|||
GL.PushMatrix(); |
|||
GL.LoadPixelMatrix(0, target.width, target.height, 0); |
|||
Graphics.DrawTexture(rect, texture, material); |
|||
GL.PopMatrix(); |
|||
RenderTexture.active = backup; |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e4d0ab18b4d169e4284296fcddc58096 |
|||
timeCreated: 1471966526 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: c15717f283b1d3e47a49dc05c4e1afe8 |
|||
folderAsset: yes |
|||
timeCreated: 1471965337 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: dbd095a2e7603c644a980b9344813b8d |
|||
folderAsset: yes |
|||
timeCreated: 1478254072 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "VFXToolbox/ImageSequencer/Blur" |
|||
{ |
|||
Properties |
|||
{ |
|||
_Radius("Radius", range(0.0,10.0)) = 3 |
|||
[Enum(RGBA,0,Alpha,1,RGB,2)] _ApplyBlur("Apply Blur", Int) = 0 |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
// 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; |
|||
}; |
|||
|
|||
// 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) |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
float _Radius; |
|||
int _ApplyBlur; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
half4 blur(sampler2D s, float2 t, float2 size, float r) |
|||
{ |
|||
int rad = r; |
|||
half4 col = half4(0,0,0,0); |
|||
|
|||
float2 invSize = 1.0f/size; |
|||
|
|||
for(float i = -rad; i <= rad; i++) |
|||
{ |
|||
for(float j = -rad; j <= rad; j++) |
|||
{ |
|||
col += tex2D(s,t+(invSize*float2(i,j))); |
|||
} |
|||
} |
|||
return col / (((2*rad)+1)*((2*rad)+1)); |
|||
} |
|||
|
|||
half4 frag (v2f input) : SV_Target |
|||
{ |
|||
half4 src = tex2D(_InputFrame,input.uv); |
|||
half4 col = blur(_InputFrame, input.uv, _FrameData.xy, _Radius); |
|||
|
|||
half3 dstRGB = col.rgb; |
|||
half dstA = col.a; |
|||
|
|||
if(_ApplyBlur == 1) dstRGB = src.rgb; |
|||
if(_ApplyBlur == 2) dstA = src.a; |
|||
|
|||
return half4(dstRGB,dstA); |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 67cfb370fc1c4104bbf7930165bb3d4b |
|||
timeCreated: 1478611277 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "VFXToolbox/ImageSequencer/HeightToNormal" |
|||
{ |
|||
Properties |
|||
{ |
|||
[Enum(Red,0,Green,1,Blue,2,Alpha,3)] _HeightSource("Input Channel for Height Source", Int) = 3 |
|||
[Enum(Red,0,Green,1,Blue,2,Alpha,3,None,4)] _AlphaChannel("Input Channel for Alpha Output", Int) = 3 |
|||
_HeightScale("Height Scale", Range(0.0,150.0)) = 25 |
|||
_Radius("Radius", Range(1.0,5.0)) = 1.0 |
|||
_Spherize("Spherize", Range(0.0,1.0)) = 0.0 |
|||
[Enum(Low,4,Medium,8,High,16)] _NumSamples("Sample Count", Int) = 8 |
|||
|
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
// 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; |
|||
}; |
|||
|
|||
// 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) |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
int _HeightSource; |
|||
int _AlphaChannel; |
|||
int _NumSamples; |
|||
float _HeightScale; |
|||
float _Radius; |
|||
float _Spherize; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
half3 position(sampler2D s, float2 t) |
|||
{ |
|||
half4 w = tex2D(s,t); |
|||
float h; |
|||
if(_HeightSource == 0) h = w.r; |
|||
if(_HeightSource == 1) h = w.g; |
|||
if(_HeightSource == 2) h = w.b; |
|||
if(_HeightSource == 3) h = w.a; |
|||
|
|||
float x = t.x *_FrameData.x; |
|||
float y = t.y *_FrameData.y; |
|||
float z = h *_HeightScale; |
|||
|
|||
// Spherize |
|||
float2 ssc = float2(t.x - 0.5f , t.y - 0.5f) * 1.414213562373095f; |
|||
z += _Spherize * (1.0f - dot(ssc, ssc)) * 200; |
|||
|
|||
return float3(x, y, z); |
|||
} |
|||
|
|||
half3 normal(sampler2D s, float2 t, float r) |
|||
{ |
|||
float2 invSize = 1.0f/_FrameData.xy; |
|||
float3 pos = position(s,t); |
|||
float3 nrm = float3(0,0,0); |
|||
|
|||
float step = UNITY_PI*2*(1.0/(_NumSamples*2)); |
|||
int numRings = ceil(r); |
|||
|
|||
for(int j = 1; j <= numRings ; j++) |
|||
{ |
|||
float rad = r * ((float)j/numRings); |
|||
|
|||
for(int i = 0; i < (_NumSamples*2); i+=2) |
|||
{ |
|||
float angle = i*step; |
|||
float2 offset = float2(cos(angle),sin(angle)) * invSize * rad; |
|||
float3 sampleA = position(s, t+offset); |
|||
|
|||
angle = (i+1)*step; |
|||
offset = float2(cos(angle),sin(angle)) * invSize * rad; |
|||
float3 sampleB = position(s, t+offset); |
|||
|
|||
nrm += normalize(cross(sampleA-pos, sampleB-pos)); |
|||
} |
|||
} |
|||
nrm = normalize(nrm); |
|||
//nrm += float3(_Spherize * pow(float2(t - 0.5f)*2.0f,5.0f),0.0f); |
|||
return nrm; |
|||
} |
|||
|
|||
half4 frag (v2f input) : SV_Target |
|||
{ |
|||
float alpha; |
|||
half4 col = tex2D(_InputFrame,input.uv); |
|||
if(_AlphaChannel == 0) alpha = col.r; |
|||
if(_AlphaChannel == 1) alpha = col.g; |
|||
if(_AlphaChannel == 2) alpha = col.b; |
|||
if(_AlphaChannel == 3) alpha = col.a; |
|||
if(_AlphaChannel == 4) alpha = 1.0f; |
|||
|
|||
half3 nrml = normal(_InputFrame, input.uv, _Radius); |
|||
return half4((nrml * 0.5)+0.5,alpha); |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 68f6507ff55de254db370dcb132f5b4e |
|||
timeCreated: 1478611277 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "VFXToolbox/ImageSequencer/LightNormals" |
|||
{ |
|||
Properties |
|||
{ |
|||
_Orientation("Orientation", Range(0.0,360.0)) = 0.0 |
|||
_GrazingAngle("Grazing Angle", Range(0.0,90.0)) = 0.0 |
|||
_BaseColor("Base Color", Color) = (1.0,1.0,1.0,1.0) |
|||
_AmbientColor("Ambient Color", Color) = (0.1,0.15,0.2,1.0) |
|||
_LightColor("Light Color", Color) = (1.0,1.0,1.0,1.0) |
|||
_LightBrightness("Light Brightness", Range(0.0,10.0)) = 1.0 |
|||
_LightExponent("Light Exponent", Range(1.0, 10.0)) = 2.0 |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
// 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; |
|||
}; |
|||
|
|||
// 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) |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
float _Orientation; |
|||
float _GrazingAngle; |
|||
float4 _BaseColor; |
|||
float4 _LightColor; |
|||
float _LightBrightness; |
|||
float _LightExponent; |
|||
float4 _AmbientColor; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
half3 latLonToXYZ(float lat, float lon) |
|||
{ |
|||
lat = (lat / 360) * 2 * UNITY_PI; |
|||
lon = (lon / 360) * 2 * UNITY_PI; |
|||
|
|||
float x = cos(lat)*cos(lon); |
|||
float y = cos(lat)*sin(lon); |
|||
float z = sin(lat); |
|||
|
|||
return half3(x,y,z); |
|||
} |
|||
|
|||
half4 frag (v2f i) : SV_Target |
|||
{ |
|||
half4 tex = tex2D(_InputFrame, i.uv); |
|||
half3 nrm = tex.rgb*2-1; |
|||
|
|||
half3 lightdir = latLonToXYZ(_GrazingAngle, _Orientation); |
|||
|
|||
half3 color = _BaseColor*((pow(saturate(dot(nrm,lightdir)*0.5+0.5),_LightExponent) * _LightColor * _LightBrightness) + _AmbientColor); |
|||
|
|||
return half4(color,tex.a); |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5be8ac80f90d85540845a26c7fafdaa9 |
|||
timeCreated: 1478611277 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "VFXToolbox/ImageSequencer/Negative" |
|||
{ |
|||
Properties |
|||
{ |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
// 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; |
|||
}; |
|||
|
|||
// 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) |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
half4 frag (v2f i) : SV_Target |
|||
{ |
|||
half4 col = tex2D(_InputFrame, i.uv); |
|||
half3 rgb = LinearToGammaSpace(col.rgb); |
|||
return half4(GammaToLinearSpace(1-rgb),col.a); |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0f56c58ead6305e4db16de20ca259856 |
|||
timeCreated: 1478611277 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "VFXToolbox/ImageSequencer/Rotate" |
|||
{ |
|||
Properties |
|||
{ |
|||
_Angle("Angle", Range(0.0,360.0)) = 0.0 |
|||
_RotationCenter("Center of Rotation", Vector) = (0.5,0.5,0.0,0.0) |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
// 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; |
|||
}; |
|||
|
|||
// 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) |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
float _Angle; |
|||
float2 _RotationCenter; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
float2 rotate2D(float2 TexCoord, float2 Center, float Angle) { |
|||
|
|||
float2 AngleCoords = float2(sin(Angle%(2*UNITY_PI)),cos(Angle%(2*UNITY_PI))); |
|||
TexCoord -= Center; |
|||
return Center + |
|||
float2( |
|||
TexCoord.x*AngleCoords.y - TexCoord.y * AngleCoords.x, |
|||
TexCoord.x*AngleCoords.x + TexCoord.y * AngleCoords.y |
|||
); |
|||
} |
|||
|
|||
half4 frag (v2f i) : SV_Target |
|||
{ |
|||
float2 nm = floor(i.uv*_FlipbookData.xy)/_FlipbookData.xy; |
|||
|
|||
float2 frameuv = frac(i.uv * _FlipbookData.xy); |
|||
|
|||
frameuv = saturate(rotate2D(frameuv, _RotationCenter, (_Angle / 360) * UNITY_PI * 2)); |
|||
|
|||
half4 col = tex2D(_InputFrame, (frameuv/_FlipbookData.xy)+nm); |
|||
return col; |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0709f816418bb514e8025d1455fdcc45 |
|||
timeCreated: 1478611277 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: b089fc59037435549b98ce54579918cb |
|||
folderAsset: yes |
|||
timeCreated: 1460544600 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: b2170b9213417b94584e56c4a7f985d4 |
|||
folderAsset: yes |
|||
timeCreated: 1485434907 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class)] |
|||
public class ProcessorAttribute : Attribute |
|||
{ |
|||
public readonly string category; |
|||
public readonly string name; |
|||
public readonly Type processorType; |
|||
|
|||
public ProcessorAttribute(string category, string name, Type processorType) |
|||
{ |
|||
this.category = category; |
|||
this.name = name; |
|||
this.processorType = processorType; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1d6b5f22b3e06424e92e5aa2d216b8bf |
|||
timeCreated: 1485434907 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: fc42ab04392773d40b0121c0abfac68b |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: e444a0e95c20b4442885c0a4f1468245 |
|||
timeCreated: 1485447509 |
|||
licenseType: Pro |
|||
TextureImporter: |
|||
fileIDToRecycleName: {} |
|||
serializedVersion: 4 |
|||
mipmaps: |
|||
mipMapMode: 0 |
|||
enableMipMap: 0 |
|||
sRGBTexture: 0 |
|||
linearTexture: 0 |
|||
fadeOut: 0 |
|||
borderMipMap: 0 |
|||
mipMapFadeDistanceStart: 1 |
|||
mipMapFadeDistanceEnd: 3 |
|||
bumpmap: |
|||
convertToNormalMap: 0 |
|||
externalNormalMap: 0 |
|||
heightScale: 0.25 |
|||
normalMapFilter: 0 |
|||
isReadable: 0 |
|||
grayScaleToAlpha: 0 |
|||
generateCubemap: 6 |
|||
cubemapConvolution: 0 |
|||
seamlessCubemap: 0 |
|||
textureFormat: 1 |
|||
maxTextureSize: 2048 |
|||
textureSettings: |
|||
filterMode: -1 |
|||
aniso: 1 |
|||
mipBias: -1 |
|||
wrapMode: 1 |
|||
nPOTScale: 0 |
|||
lightmap: 0 |
|||
compressionQuality: 50 |
|||
spriteMode: 0 |
|||
spriteExtrude: 1 |
|||
spriteMeshType: 1 |
|||
alignment: 0 |
|||
spritePivot: {x: 0.5, y: 0.5} |
|||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
|||
spritePixelsToUnits: 100 |
|||
alphaUsage: 1 |
|||
alphaIsTransparency: 1 |
|||
spriteTessellationDetail: -1 |
|||
textureType: 2 |
|||
textureShape: 1 |
|||
maxTextureSizeSet: 0 |
|||
compressionQualitySet: 0 |
|||
textureFormatSet: 0 |
|||
platformSettings: |
|||
- buildTarget: DefaultTexturePlatform |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: Standalone |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: Android |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: WebGL |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
spriteSheet: |
|||
serializedVersion: 2 |
|||
sprites: [] |
|||
outline: [] |
|||
spritePackingTag: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 480a6ceec94297a489f2583204dc68f0 |
|||
folderAsset: yes |
|||
timeCreated: 1465462056 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditor.VFXToolbox; |
|||
|
|||
// MiniEXR 2013 by Aras Pranckevicius / Unity Technologies.
|
|||
//
|
|||
// C# conversion by Ilya Suzdalnitski.
|
|||
// Slightly Modified by Thomas Ich�
|
|||
//
|
|||
// Writes OpenEXR RGB files out of half-precision RGBA or RGB data.
|
|||
//
|
|||
|
|||
namespace MiniEXR { |
|||
//Based on source-forge project: http://sourceforge.net/projects/csharp-half/
|
|||
internal static class HalfHelper |
|||
{ |
|||
private static uint[] mantissaTable = GenerateMantissaTable(); |
|||
private static uint[] exponentTable = GenerateExponentTable(); |
|||
private static ushort[] offsetTable = GenerateOffsetTable(); |
|||
private static ushort[] baseTable = GenerateBaseTable(); |
|||
private static sbyte[] shiftTable = GenerateShiftTable(); |
|||
|
|||
// Transforms the subnormal representation to a normalized one.
|
|||
private static uint ConvertMantissa(int i) |
|||
{ |
|||
uint m = (uint)(i << 13); // Zero pad mantissa bits
|
|||
uint e = 0; // Zero exponent
|
|||
|
|||
// While not normalized
|
|||
while ((m & 0x00800000) == 0) |
|||
{ |
|||
e -= 0x00800000; // Decrement exponent (1<<23)
|
|||
m <<= 1; // Shift mantissa
|
|||
} |
|||
m &= unchecked((uint)~0x00800000); // Clear leading 1 bit
|
|||
e += 0x38800000; // Adjust bias ((127-14)<<23)
|
|||
return m | e; // Return combined number
|
|||
} |
|||
|
|||
private static uint[] GenerateMantissaTable() |
|||
{ |
|||
uint[] mantissaTable = new uint[2048]; |
|||
mantissaTable[0] = 0; |
|||
for (int i = 1; i < 1024; i++) |
|||
{ |
|||
mantissaTable[i] = ConvertMantissa(i); |
|||
} |
|||
for (int i = 1024; i < 2048; i++) |
|||
{ |
|||
mantissaTable[i] = (uint)(0x38000000 + ((i - 1024) << 13)); |
|||
} |
|||
|
|||
return mantissaTable; |
|||
} |
|||
private static uint[] GenerateExponentTable() |
|||
{ |
|||
uint[] exponentTable = new uint[64]; |
|||
exponentTable[0] = 0; |
|||
for (int i = 1; i < 31; i++) |
|||
{ |
|||
exponentTable[i] = (uint)(i << 23); |
|||
} |
|||
exponentTable[31] = 0x47800000; |
|||
exponentTable[32] = 0x80000000; |
|||
for (int i = 33; i < 63; i++) |
|||
{ |
|||
exponentTable[i] = (uint)(0x80000000 + ((i - 32) << 23)); |
|||
} |
|||
exponentTable[63] = 0xc7800000; |
|||
|
|||
return exponentTable; |
|||
} |
|||
private static ushort[] GenerateOffsetTable() |
|||
{ |
|||
ushort[] offsetTable = new ushort[64]; |
|||
offsetTable[0] = 0; |
|||
for (int i = 1; i < 32; i++) |
|||
{ |
|||
offsetTable[i] = 1024; |
|||
} |
|||
offsetTable[32] = 0; |
|||
for (int i = 33; i < 64; i++) |
|||
{ |
|||
offsetTable[i] = 1024; |
|||
} |
|||
|
|||
return offsetTable; |
|||
} |
|||
private static ushort[] GenerateBaseTable() |
|||
{ |
|||
ushort[] baseTable = new ushort[512]; |
|||
for (int i = 0; i < 256; ++i) |
|||
{ |
|||
sbyte e = (sbyte)(127 - i); |
|||
if (e > 24) |
|||
{ // Very small numbers map to zero
|
|||
baseTable[i | 0x000] = 0x0000; |
|||
baseTable[i | 0x100] = 0x8000; |
|||
} |
|||
else if (e > 14) |
|||
{ // Small numbers map to denorms
|
|||
baseTable[i | 0x000] = (ushort)(0x0400 >> (18 + e)); |
|||
baseTable[i | 0x100] = (ushort)((0x0400 >> (18 + e)) | 0x8000); |
|||
} |
|||
else if (e >= -15) |
|||
{ // Normal numbers just lose precision
|
|||
baseTable[i | 0x000] = (ushort)((15 - e) << 10); |
|||
baseTable[i | 0x100] = (ushort)(((15 - e) << 10) | 0x8000); |
|||
} |
|||
else if (e > -128) |
|||
{ // Large numbers map to Infinity
|
|||
baseTable[i | 0x000] = 0x7c00; |
|||
baseTable[i | 0x100] = 0xfc00; |
|||
} |
|||
else |
|||
{ // Infinity and NaN's stay Infinity and NaN's
|
|||
baseTable[i | 0x000] = 0x7c00; |
|||
baseTable[i | 0x100] = 0xfc00; |
|||
} |
|||
} |
|||
|
|||
return baseTable; |
|||
} |
|||
private static sbyte[] GenerateShiftTable() |
|||
{ |
|||
sbyte[] shiftTable = new sbyte[512]; |
|||
for (int i = 0; i < 256; ++i) |
|||
{ |
|||
sbyte e = (sbyte)(127 - i); |
|||
if (e > 24) |
|||
{ // Very small numbers map to zero
|
|||
shiftTable[i | 0x000] = 24; |
|||
shiftTable[i | 0x100] = 24; |
|||
} |
|||
else if (e > 14) |
|||
{ // Small numbers map to denorms
|
|||
shiftTable[i | 0x000] = (sbyte)(e - 1); |
|||
shiftTable[i | 0x100] = (sbyte)(e - 1); |
|||
} |
|||
else if (e >= -15) |
|||
{ // Normal numbers just lose precision
|
|||
shiftTable[i | 0x000] = 13; |
|||
shiftTable[i | 0x100] = 13; |
|||
} |
|||
else if (e > -128) |
|||
{ // Large numbers map to Infinity
|
|||
shiftTable[i | 0x000] = 24; |
|||
shiftTable[i | 0x100] = 24; |
|||
} |
|||
else |
|||
{ // Infinity and NaN's stay Infinity and NaN's
|
|||
shiftTable[i | 0x000] = 13; |
|||
shiftTable[i | 0x100] = 13; |
|||
} |
|||
} |
|||
|
|||
return shiftTable; |
|||
} |
|||
|
|||
public static float HalfToSingle(ushort half) |
|||
{ |
|||
uint result = mantissaTable[offsetTable[half >> 10] + (half & 0x3ff)] + exponentTable[half >> 10]; |
|||
|
|||
return System.BitConverter.ToSingle( System.BitConverter.GetBytes( result ), 0 ); |
|||
|
|||
//return *((float*)&result);
|
|||
} |
|||
public static ushort SingleToHalf(float single) |
|||
{ |
|||
//uint value = *((uint*)&single);
|
|||
|
|||
uint value = System.BitConverter.ToUInt32( System.BitConverter.GetBytes( single ), 0 ); |
|||
|
|||
ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23])); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public static class MiniEXR { |
|||
|
|||
// Writes EXR into a memory buffer.
|
|||
// Input:
|
|||
// - (width) x (height) image,
|
|||
// - channels=4: 8 bytes per pixel (R,G,B,A order, 16 bit float per channel; alpha ignored), or
|
|||
// - channels=3: 6 bytes per pixel (R,G,B order, 16 bit float per channel).
|
|||
// Returns memory buffer with .EXR contents and buffer size in outSize. free() the buffer when done with it.
|
|||
|
|||
public static void MiniEXRWrite (string _filePath, uint _width, uint _height, bool _ExportAlpha, Color[] _colorArray, bool bFlipVertical) { |
|||
|
|||
byte[] bytes = MiniEXRWrite(_width, _height, _ExportAlpha, _colorArray, bFlipVertical); |
|||
|
|||
System.IO.File.WriteAllBytes(_filePath, bytes ); |
|||
} |
|||
|
|||
public static byte[] MiniEXRWrite (uint _width, uint _height, bool _ExportAlpha, Color[] _colorArray, bool bFlipVertical = false) |
|||
{ |
|||
if (bFlipVertical) |
|||
_colorArray = FlipVertical(_colorArray, _width, _height); |
|||
|
|||
byte stride = (byte)(_ExportAlpha ? 4 : 3); |
|||
float[] rgbaArray = new float[ _colorArray.Length * stride ]; |
|||
|
|||
|
|||
for (int i = 0; i < _colorArray.Length; i++) |
|||
{ |
|||
rgbaArray[i * stride] = _colorArray[i].r; |
|||
rgbaArray[i * stride + 1] = _colorArray[i].g; |
|||
rgbaArray[i * stride + 2] = _colorArray[i].b; |
|||
if(_ExportAlpha) |
|||
rgbaArray[i * stride + 3] = _colorArray[i].a; |
|||
} |
|||
|
|||
return MiniEXRWrite(_width, _height, stride, rgbaArray); |
|||
} |
|||
|
|||
private static Color[] FlipVertical(Color[] input, uint _width, uint _height) |
|||
{ |
|||
Color[] output = new Color[input.Length]; |
|||
|
|||
uint k = 0; |
|||
for(int j = (int)_height-1; j >= 0; j--) |
|||
{ |
|||
for (int i = 0; i < _width; i++) |
|||
{ |
|||
int idx = i + (j * (int)_width); |
|||
output[k] = input[idx]; |
|||
k++; |
|||
} |
|||
} |
|||
|
|||
return output; |
|||
} |
|||
|
|||
public static byte[] MiniEXRWrite (uint _width, uint _height, uint _channels, float[] _rgbaArray) |
|||
{ |
|||
//const void* rgba16f
|
|||
uint ww = _width-1; |
|||
uint hh = _height-1; |
|||
byte[] kHeader; |
|||
if(_channels == 3) |
|||
{ |
|||
kHeader = new byte[] { |
|||
0x76, 0x2f, 0x31, 0x01, // magic
|
|||
2, 0, 0, 0, // version, scanline
|
|||
// channels
|
|||
(byte)'c',(byte)'h',(byte)'a',(byte)'n',(byte)'n',(byte)'e',(byte)'l',(byte)'s',0, |
|||
(byte)'c',(byte)'h',(byte)'l',(byte)'i',(byte)'s',(byte)'t',0, |
|||
55,0,0,0, |
|||
(byte)'B',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // R, half
|
|||
(byte)'G',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // G, half
|
|||
(byte)'R',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // B, half
|
|||
0, |
|||
// compression
|
|||
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0, |
|||
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0, |
|||
1,0,0,0, |
|||
0, // no compression
|
|||
// dataWindow
|
|||
(byte)'d',(byte)'a',(byte)'t',(byte)'a',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0, |
|||
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0, |
|||
16,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF), |
|||
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF), |
|||
// displayWindow
|
|||
(byte)'d',(byte)'i',(byte)'s',(byte)'p',(byte)'l',(byte)'a',(byte)'y',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0, |
|||
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0, |
|||
16,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF), |
|||
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF), |
|||
// lineOrder
|
|||
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0, |
|||
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0, |
|||
1,0,0,0, |
|||
0, // increasing Y
|
|||
// pixelAspectRatio
|
|||
(byte)'p',(byte)'i',(byte)'x',(byte)'e',(byte)'l',(byte)'A',(byte)'s',(byte)'p',(byte)'e',(byte)'c',(byte)'t',(byte)'R',(byte)'a',(byte)'t',(byte)'i',(byte)'o',0, |
|||
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0, |
|||
4,0,0,0, |
|||
0,0,0x80,0x3f, // 1.0f
|
|||
// screenWindowCenter
|
|||
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'C',(byte)'e',(byte)'n',(byte)'t',(byte)'e',(byte)'r',0, |
|||
(byte)'v',(byte)'2',(byte)'f',0, |
|||
8,0,0,0, |
|||
0,0,0,0, 0,0,0,0, |
|||
// screenWindowWidth
|
|||
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'W',(byte)'i',(byte)'d',(byte)'t',(byte)'h',0, |
|||
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0, |
|||
4,0,0,0, |
|||
0,0,0x80,0x3f, // 1.0f
|
|||
// end of header
|
|||
0, |
|||
}; |
|||
} |
|||
else |
|||
{ |
|||
kHeader = new byte[] { |
|||
0x76, 0x2f, 0x31, 0x01, // magic
|
|||
2, 0, 0, 0, // version, scanline
|
|||
// channels
|
|||
(byte)'c',(byte)'h',(byte)'a',(byte)'n',(byte)'n',(byte)'e',(byte)'l',(byte)'s',0, |
|||
(byte)'c',(byte)'h',(byte)'l',(byte)'i',(byte)'s',(byte)'t',0, |
|||
55,0,0,0, |
|||
(byte)'A',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // A, half
|
|||
(byte)'B',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // R, half
|
|||
(byte)'G',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // G, half
|
|||
(byte)'R',0, 1,0,0,0, 0, 0,0,0,1,0,0,0,1,0,0,0, // B, half
|
|||
0, |
|||
// compression
|
|||
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0, |
|||
(byte)'c',(byte)'o',(byte)'m',(byte)'p',(byte)'r',(byte)'e',(byte)'s',(byte)'s',(byte)'i',(byte)'o',(byte)'n',0, |
|||
1,0,0,0, |
|||
0, // no compression
|
|||
// dataWindow
|
|||
(byte)'d',(byte)'a',(byte)'t',(byte)'a',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0, |
|||
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0, |
|||
16,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF), |
|||
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF), |
|||
// displayWindow
|
|||
(byte)'d',(byte)'i',(byte)'s',(byte)'p',(byte)'l',(byte)'a',(byte)'y',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',0, |
|||
(byte)'b',(byte)'o',(byte)'x',(byte)'2',(byte)'i',0, |
|||
16,0,0,0, |
|||
0,0,0,0,0,0,0,0, |
|||
(byte)(ww&0xFF), (byte)((ww>>8)&0xFF), (byte)((ww>>16)&0xFF), (byte)((ww>>24)&0xFF), |
|||
(byte)(hh&0xFF), (byte)((hh>>8)&0xFF), (byte)((hh>>16)&0xFF), (byte)((hh>>24)&0xFF), |
|||
// lineOrder
|
|||
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0, |
|||
(byte)'l',(byte)'i',(byte)'n',(byte)'e',(byte)'O',(byte)'r',(byte)'d',(byte)'e',(byte)'r',0, |
|||
1,0,0,0, |
|||
0, // increasing Y
|
|||
// pixelAspectRatio
|
|||
(byte)'p',(byte)'i',(byte)'x',(byte)'e',(byte)'l',(byte)'A',(byte)'s',(byte)'p',(byte)'e',(byte)'c',(byte)'t',(byte)'R',(byte)'a',(byte)'t',(byte)'i',(byte)'o',0, |
|||
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0, |
|||
4,0,0,0, |
|||
0,0,0x80,0x3f, // 1.0f
|
|||
// screenWindowCenter
|
|||
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'C',(byte)'e',(byte)'n',(byte)'t',(byte)'e',(byte)'r',0, |
|||
(byte)'v',(byte)'2',(byte)'f',0, |
|||
8,0,0,0, |
|||
0,0,0,0, 0,0,0,0, |
|||
// screenWindowWidth
|
|||
(byte)'s',(byte)'c',(byte)'r',(byte)'e',(byte)'e',(byte)'n',(byte)'W',(byte)'i',(byte)'n',(byte)'d',(byte)'o',(byte)'w',(byte)'W',(byte)'i',(byte)'d',(byte)'t',(byte)'h',0, |
|||
(byte)'f',(byte)'l',(byte)'o',(byte)'a',(byte)'t',0, |
|||
4,0,0,0, |
|||
0,0,0x80,0x3f, // 1.0f
|
|||
// end of header
|
|||
0, |
|||
}; |
|||
} |
|||
|
|||
|
|||
uint kHeaderSize = (uint)kHeader.Length; |
|||
|
|||
uint kScanlineTableSize = 8 * _height; |
|||
uint pixelRowSize = _width * _channels * 2; |
|||
uint fullRowSize = pixelRowSize + 8; |
|||
|
|||
uint bufSize = kHeaderSize + kScanlineTableSize + _height * fullRowSize; |
|||
|
|||
byte[] buf = new byte[bufSize]; |
|||
|
|||
// copy in header
|
|||
|
|||
int bufI = 0; |
|||
|
|||
for (int i = 0; i < kHeaderSize; i++) { |
|||
buf[ bufI ] = kHeader[i]; |
|||
|
|||
bufI++; |
|||
} |
|||
|
|||
// line offset table
|
|||
uint ofs = kHeaderSize + kScanlineTableSize; |
|||
|
|||
for (int y = 0; y < _height; ++y) |
|||
{ |
|||
buf[ bufI++ ] = (byte)(ofs & 0xFF); |
|||
buf[ bufI++ ] = (byte)((ofs >> 8) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((ofs >> 16) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((ofs >> 24) & 0xFF); |
|||
buf[ bufI++ ] = 0; |
|||
buf[ bufI++ ] = 0; |
|||
buf[ bufI++ ] = 0; |
|||
buf[ bufI++ ] = 0; |
|||
|
|||
ofs += fullRowSize; |
|||
} |
|||
|
|||
//Convert float to half float
|
|||
ushort[] srcHalf = new ushort[_rgbaArray.Length]; |
|||
|
|||
for (int i = 0; i < _rgbaArray.Length; i++) { |
|||
//Gamma encode before converting : no
|
|||
//_rgbaArray[i] = Mathf.Pow(_rgbaArray[i], 2.2f);
|
|||
srcHalf[i] = HalfHelper.SingleToHalf( _rgbaArray[i] ); |
|||
} |
|||
|
|||
uint srcDataI = 0; |
|||
|
|||
for (int y = 0; y < _height; y++) |
|||
{ |
|||
// coordinate
|
|||
buf[ bufI++ ] = (byte)(y & 0xFF); |
|||
buf[ bufI++ ] = (byte)((y >> 8) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((y >> 16) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((y >> 24) & 0xFF); |
|||
// data size
|
|||
buf[ bufI++ ] = (byte)(pixelRowSize & 0xFF); |
|||
buf[ bufI++ ] = (byte)((pixelRowSize >> 8) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((pixelRowSize >> 16) & 0xFF); |
|||
buf[ bufI++ ] = (byte)((pixelRowSize >> 24) & 0xFF); |
|||
// B, G, R
|
|||
//memcpy (ptr, src, width*6); //Copy first line - 6 bits, 2 bits per channel
|
|||
|
|||
uint tempSrcI; |
|||
|
|||
//If _channels == 4, write Alpha
|
|||
if(_channels == 4) |
|||
{ |
|||
tempSrcI = srcDataI; |
|||
for (int x = 0; x < _width; ++x) |
|||
{ |
|||
//Blue
|
|||
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 3 ] ); |
|||
buf[ bufI++ ] = halfBytes[0]; |
|||
buf[ bufI++ ] = halfBytes[1]; |
|||
|
|||
tempSrcI += _channels; |
|||
} |
|||
} |
|||
|
|||
|
|||
//First copy a line of B
|
|||
tempSrcI = srcDataI; |
|||
for (int x = 0; x < _width; ++x) |
|||
{ |
|||
//Blue
|
|||
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 2 ] ); |
|||
buf[ bufI++ ] = halfBytes[0]; |
|||
buf[ bufI++ ] = halfBytes[1]; |
|||
|
|||
tempSrcI += _channels; |
|||
} |
|||
|
|||
//Then copy a line of G
|
|||
tempSrcI = srcDataI; |
|||
for (int x = 0; x < _width; ++x) |
|||
{ |
|||
//Blue
|
|||
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI + 1 ] ); |
|||
buf[ bufI++ ] = halfBytes[0]; |
|||
buf[ bufI++ ] = halfBytes[1]; |
|||
|
|||
tempSrcI += _channels; |
|||
} |
|||
|
|||
//Finally copy a line of R
|
|||
tempSrcI = srcDataI; |
|||
for (int x = 0; x < _width; ++x) |
|||
{ |
|||
//Blue
|
|||
byte[] halfBytes = System.BitConverter.GetBytes( srcHalf[ tempSrcI ] ); |
|||
buf[ bufI++ ] = halfBytes[0]; |
|||
buf[ bufI++ ] = halfBytes[1]; |
|||
|
|||
tempSrcI += _channels; |
|||
} |
|||
|
|||
srcDataI += _width * _channels; |
|||
} |
|||
|
|||
return buf; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: be9b913bdcccabf4aa59739f61686a41 |
|||
timeCreated: 1465462056 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using UnityEngine; |
|||
using UnityEditor.VFXToolbox; |
|||
|
|||
namespace MiniTGA |
|||
{ |
|||
public static class MiniTGA |
|||
{ |
|||
// Writes TGA into a memory buffer.
|
|||
// Input:
|
|||
// - (width) x (height) image,
|
|||
// - channels=4: RGBA 32bit
|
|||
// - channels=3: RGB 24bit
|
|||
// Returns memory buffer with uncompressed, unpalettized Targa contents and buffer size in outSize. free() the buffer when done with it.
|
|||
|
|||
public static void MiniTGAWrite (string _filePath, ushort _width, ushort _height, bool _exportalpha, Color[] _colorArray) { |
|||
|
|||
byte[] bytes = MiniTGAWrite(_width, _height, _exportalpha, _colorArray); |
|||
System.IO.File.WriteAllBytes(_filePath, bytes); |
|||
} |
|||
|
|||
public static byte[] MiniTGAWrite (ushort _width, ushort _height, bool _exportalpha, Color[] _colorArray) |
|||
{ |
|||
byte[] kHeader = // TRUEVISION TARGA HEADER 18 bytes
|
|||
{ |
|||
0, // No ID Field 1
|
|||
0, // No ColorMap 1
|
|||
2, // Uncompressed 1
|
|||
0,0,0,0,0, // Null Dummies for Color Map 5
|
|||
0,0, // X Origin = 0 2
|
|||
0,0, // Y Origin = 0 2
|
|||
(byte)(_width % 256), (byte)(_width >> 8), // Width 2
|
|||
(byte)(_height % 256), (byte)(_height >> 8), // Height 2
|
|||
(byte)(_exportalpha ? 32 : 24), // Bit depth 1
|
|||
0 // End Descriptor 1
|
|||
}; |
|||
|
|||
byte stride = (byte)(_exportalpha ? 4 : 3); |
|||
int size = kHeader.Length + (_width * _height * stride); |
|||
|
|||
byte[] buffer = new byte[size]; |
|||
|
|||
// Copy Header into buffer
|
|||
Buffer.BlockCopy(kHeader, 0, buffer, 0, kHeader.Length); |
|||
|
|||
// Image Positionning
|
|||
int pos = kHeader.Length; |
|||
|
|||
int count = _colorArray.Length; |
|||
int i = 0; |
|||
foreach(Color c in _colorArray) |
|||
{ |
|||
buffer[pos] = (byte)(Mathf.Clamp01(c.b) * 255); |
|||
buffer[pos+1] = (byte)(Mathf.Clamp01(c.g) * 255); |
|||
buffer[pos+2] = (byte)(Mathf.Clamp01(c.r) * 255); |
|||
if(_exportalpha) |
|||
buffer[pos+3] = (byte)(Mathf.Clamp01(c.a) * 255); |
|||
pos += stride; |
|||
i++; |
|||
} |
|||
|
|||
return buffer; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 335ff1d947896f2428a2587cc539d0a6 |
|||
timeCreated: 1465462056 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 412b3158d6b5a3140b557976c0a4f63e |
|||
folderAsset: yes |
|||
timeCreated: 1485512158 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public class ProcessorDataProvider : IProvider |
|||
{ |
|||
private Dictionary<Type, ProcessorAttribute> m_dataSource; |
|||
private FrameProcessorStack m_processorStack; |
|||
private ImageSequence m_CurrentAsset; |
|||
|
|||
public class ProcessorElement : FilterPopupWindow.Element |
|||
{ |
|||
public Action<ProcessorElement> m_SpawnCallback; |
|||
public ProcessorAttribute m_Desc; |
|||
public Type m_ProcessorSettingType; |
|||
|
|||
public ProcessorElement(int level, KeyValuePair<Type, ProcessorAttribute> desc, Action<ProcessorElement> spawncallback) |
|||
{ |
|||
this.level = level; |
|||
|
|||
content = new GUIContent(EditorGUIUtility.IconContent("SceneViewFx")); |
|||
content.text = desc.Value.name; |
|||
|
|||
m_Desc = desc.Value; |
|||
m_ProcessorSettingType = desc.Key; |
|||
m_SpawnCallback = spawncallback; |
|||
} |
|||
} |
|||
|
|||
internal ProcessorDataProvider(FrameProcessorStack stack, ImageSequence asset) |
|||
{ |
|||
m_dataSource = stack.settingsDefinitions; |
|||
m_processorStack = stack; |
|||
m_CurrentAsset = asset; |
|||
} |
|||
|
|||
public void CreateComponentTree(List<FilterPopupWindow.Element> tree) |
|||
{ |
|||
tree.Add(new FilterPopupWindow.GroupElement(0, "Add new Processor...")); |
|||
|
|||
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); |
|||
}); |
|||
|
|||
HashSet<string> categories = new HashSet<string>(); |
|||
|
|||
foreach( KeyValuePair<Type, ProcessorAttribute> desc in processors) |
|||
{ |
|||
int i = 0; |
|||
|
|||
if(!categories.Contains(desc.Value.category) && desc.Value.category != "") |
|||
{ |
|||
string[] split = desc.Value.category.Split('/'); |
|||
string current = ""; |
|||
|
|||
while(i < split.Length) |
|||
{ |
|||
current += split[i]; |
|||
if(!categories.Contains(current)) |
|||
tree.Add(new FilterPopupWindow.GroupElement(i+1,split[i])); |
|||
i++; |
|||
current += "/"; |
|||
} |
|||
categories.Add(desc.Value.category); |
|||
} |
|||
else |
|||
{ |
|||
i = desc.Value.category.Split('/').Length; |
|||
} |
|||
|
|||
if (desc.Value.category != "") |
|||
i++; |
|||
|
|||
tree.Add(new ProcessorElement(i, desc, AddProcessor)); |
|||
|
|||
} |
|||
} |
|||
|
|||
public void AddProcessor(ProcessorElement element) |
|||
{ |
|||
var settingType = element.m_ProcessorSettingType; |
|||
|
|||
// Add Element
|
|||
Undo.RecordObject(m_CurrentAsset, "Add Processor"); |
|||
|
|||
FrameProcessor processor = null; |
|||
|
|||
// Reflection Stuff here
|
|||
ProcessorAttribute attribute = m_processorStack.settingsDefinitions[settingType]; |
|||
Type processorType = attribute.processorType; |
|||
|
|||
var info = ProcessorInfo.CreateDefault(attribute.name, true, settingType); |
|||
|
|||
processor = (FrameProcessor)Activator.CreateInstance(processorType, m_processorStack, info); |
|||
|
|||
if(processor != null) |
|||
{ |
|||
m_processorStack.AddProcessor(processor, m_CurrentAsset); |
|||
m_processorStack.InvalidateAll(); |
|||
} |
|||
} |
|||
|
|||
public bool GoToChild(FilterPopupWindow.Element element, bool addIfComponent) |
|||
{ |
|||
if (element is ProcessorElement) |
|||
{ |
|||
((ProcessorElement)element).m_SpawnCallback.Invoke((ProcessorElement)element); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ad769a5a4d1115645bd4121e6d470566 |
|||
timeCreated: 1485512158 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public abstract class FrameProcessor |
|||
{ |
|||
public int OutputWidth |
|||
{ |
|||
get { |
|||
if (Enabled) |
|||
return GetOutputWidth(); |
|||
else |
|||
return |
|||
InputSequence.width; |
|||
} |
|||
} |
|||
public int OutputHeight |
|||
{ |
|||
get |
|||
{ |
|||
if (Enabled) |
|||
return GetOutputHeight(); |
|||
else |
|||
return |
|||
InputSequence.width; |
|||
} |
|||
} |
|||
|
|||
public int NumU |
|||
{ |
|||
get { |
|||
if (Enabled) |
|||
return GetNumU(); |
|||
else |
|||
return InputSequence.numU; |
|||
} |
|||
} |
|||
public int NumV |
|||
{ |
|||
get { |
|||
if (Enabled) |
|||
return GetNumV(); |
|||
else |
|||
return InputSequence.numV; |
|||
} |
|||
} |
|||
|
|||
public bool GenerateMipMaps; |
|||
public bool Linear; |
|||
|
|||
public bool Enabled { get{ return m_bEnabled; } set {SetEnabled(value); } } |
|||
|
|||
public ProcessingFrameSequence InputSequence |
|||
{ |
|||
get { return m_ProcessorStack.GetInputSequence(this); } |
|||
} |
|||
public ProcessingFrameSequence OutputSequence |
|||
{ |
|||
get { if (m_bEnabled) return m_OutputSequence; else return InputSequence; } |
|||
} |
|||
|
|||
public ProcessorInfo ProcessorInfo |
|||
{ |
|||
get { return m_ProcessorInfo; } |
|||
} |
|||
|
|||
protected FrameProcessorStack m_ProcessorStack; |
|||
protected ProcessingFrameSequence m_OutputSequence; |
|||
|
|||
protected bool m_bEnabled; |
|||
|
|||
protected int m_OutputWidth; |
|||
protected int m_OutputHeight; |
|||
|
|||
protected ProcessorInfo m_ProcessorInfo; |
|||
|
|||
public FrameProcessor(FrameProcessorStack processorStack, ProcessorInfo info) |
|||
{ |
|||
m_ProcessorInfo = info; |
|||
m_ProcessorInfo.ProcessorName = GetName(); |
|||
m_bEnabled = m_ProcessorInfo.Enabled; |
|||
m_ProcessorStack = processorStack; |
|||
m_OutputSequence = new ProcessingFrameSequence(this); |
|||
Linear = true; |
|||
GenerateMipMaps = true; |
|||
} |
|||
|
|||
public void SetEnabled(bool value) |
|||
{ |
|||
m_bEnabled = value; |
|||
var info = new SerializedObject(m_ProcessorInfo); |
|||
info.Update(); |
|||
info.FindProperty("Enabled").boolValue = value; |
|||
info.ApplyModifiedProperties(); |
|||
} |
|||
|
|||
public virtual void Dispose() |
|||
{ |
|||
m_OutputSequence.Dispose(); |
|||
} |
|||
|
|||
public void Refresh() |
|||
{ |
|||
if(Enabled != m_ProcessorInfo.Enabled) |
|||
Enabled = m_ProcessorInfo.Enabled; |
|||
UpdateSequenceLength(); |
|||
UpdateOutputSize(); |
|||
} |
|||
|
|||
protected virtual void UpdateOutputSize() |
|||
{ |
|||
SetOutputSize(InputSequence.width, InputSequence.height); |
|||
} |
|||
|
|||
protected virtual int GetOutputWidth() |
|||
{ |
|||
UpdateOutputSize(); |
|||
return m_OutputWidth; |
|||
} |
|||
protected virtual int GetOutputHeight() |
|||
{ |
|||
UpdateOutputSize(); |
|||
return m_OutputHeight; |
|||
} |
|||
|
|||
public void SetOutputSize(int width, int height) |
|||
{ |
|||
if(m_OutputWidth != width || m_OutputHeight != height) |
|||
{ |
|||
m_OutputWidth = Mathf.Clamp(width,1,8192); |
|||
m_OutputHeight = Mathf.Clamp(height,1,8192); |
|||
} |
|||
} |
|||
|
|||
protected abstract int GetNumU(); |
|||
protected abstract int GetNumV(); |
|||
|
|||
protected bool DrawSidePanelHeader() |
|||
{ |
|||
bool bHasChanged = false; |
|||
bool previousEnabled = Enabled; |
|||
Enabled = VFXToolboxGUIUtility.ToggleableHeader(Enabled, false, GetName()); |
|||
|
|||
if(previousEnabled != Enabled) |
|||
{ |
|||
SerializedObject o = new SerializedObject(m_ProcessorInfo); |
|||
o.FindProperty("Enabled").boolValue = Enabled; |
|||
o.ApplyModifiedProperties(); |
|||
m_ProcessorStack.Invalidate(this); |
|||
bHasChanged = true; |
|||
} |
|||
return bHasChanged; |
|||
} |
|||
|
|||
protected abstract bool DrawSidePanelContent(bool hasChanged); |
|||
|
|||
public abstract bool OnSidePanelGUI(ImageSequence asset, int ProcessorIndex); |
|||
|
|||
public abstract bool OnCanvasGUI(ImageSequencerCanvas canvas); |
|||
|
|||
public virtual void RequestProcessOneFrame(int currentFrame) |
|||
{ |
|||
int length = OutputSequence.length; |
|||
|
|||
int i = (currentFrame + 1) % length; |
|||
|
|||
while (i != currentFrame) |
|||
{ |
|||
bool advance = false; |
|||
if(OutputSequence.frames[i].dirty) |
|||
{ |
|||
advance = OutputSequence.Process(i); |
|||
if(advance) return; |
|||
} |
|||
|
|||
i = (i + 1); |
|||
i %= length; |
|||
} |
|||
} |
|||
|
|||
public abstract bool Process(int frame); |
|||
|
|||
public virtual int GetProcessorSequenceLength() |
|||
{ |
|||
return InputSequence.length; |
|||
} |
|||
|
|||
public bool Process(ProcessingFrame frame) |
|||
{ |
|||
return Process(OutputSequence.frames.IndexOf(frame)); |
|||
} |
|||
|
|||
public void UpdateSequenceLength() |
|||
{ |
|||
int currentCount = m_OutputSequence.frames.Count; |
|||
int requiredCount = GetProcessorSequenceLength(); |
|||
|
|||
if (currentCount == requiredCount) |
|||
return; |
|||
|
|||
if(currentCount > requiredCount) |
|||
{ |
|||
for(int i = requiredCount - 1; i < currentCount - 1; i++) |
|||
{ |
|||
m_OutputSequence.frames[i].Dispose(); |
|||
} |
|||
|
|||
m_OutputSequence.frames.RemoveRange(requiredCount - 1, currentCount - requiredCount); |
|||
} |
|||
else |
|||
{ |
|||
for(int i = 0; i < requiredCount - currentCount; i++) |
|||
{ |
|||
m_OutputSequence.frames.Add(new ProcessingFrame(this)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public virtual void Invalidate() |
|||
{ |
|||
UpdateSequenceLength(); |
|||
SetOutputSize(GetOutputWidth(), GetOutputHeight()); |
|||
m_OutputSequence.InvalidateAll(); |
|||
|
|||
FrameProcessor next = m_ProcessorStack.GetNextProcessor(this); |
|||
if(next != null) |
|||
next.Invalidate(); |
|||
} |
|||
|
|||
public abstract string GetName(); |
|||
|
|||
public virtual string GetLabel() |
|||
{ |
|||
return GetName(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return GetLabel() + (Enabled ? "" : " (Disabled)"); |
|||
} |
|||
|
|||
public abstract ProcessorSettingsBase GetSettingsAbstract(); |
|||
|
|||
} |
|||
|
|||
public 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) |
|||
{ |
|||
m_ProcessorInfo = info; |
|||
settings = (T)m_ProcessorInfo.Settings; |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 41d9351ae1420d640af2102cd1a09969 |
|||
timeCreated: 1460732283 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class FrameProcessorStack |
|||
{ |
|||
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(FrameProcessor processor, ImageSequence asset) |
|||
{ |
|||
AddProcessorInfoObjectToAsset(asset, processor.ProcessorInfo); |
|||
asset.processorInfos.Add(processor.ProcessorInfo); |
|||
|
|||
ProcessorSettingsBase settings = processor.GetSettingsAbstract(); |
|||
if (settings != null) |
|||
{ |
|||
AddSettingsObjectToAsset(asset, settings); |
|||
processor.ProcessorInfo.Settings = settings; |
|||
} |
|||
m_Processors.Add(processor); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void RemoveAllProcessors(ImageSequence asset) |
|||
{ |
|||
asset.processorInfos.Clear(); |
|||
m_Processors.Clear(); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void RemoveProcessor(int index, ImageSequence asset) |
|||
{ |
|||
asset.processorInfos.RemoveAt(index); |
|||
m_Processors.RemoveAt(index); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void ReorderProcessors(ImageSequence asset) |
|||
{ |
|||
if(m_Processors.Count > 0) |
|||
{ |
|||
List<FrameProcessor> old = new List<FrameProcessor>(); |
|||
foreach(FrameProcessor p in m_Processors) |
|||
{ |
|||
old.Add(p); |
|||
} |
|||
|
|||
m_Processors.Clear(); |
|||
foreach(ProcessorInfo info in asset.processorInfos) |
|||
{ |
|||
foreach(FrameProcessor p in old) |
|||
{ |
|||
if(p.ProcessorInfo.Equals(info)) |
|||
{ |
|||
m_Processors.Add(p); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
} |
|||
|
|||
public void LoadProcessorsFromAsset(ImageSequence asset) |
|||
{ |
|||
m_Processors.Clear(); |
|||
|
|||
var infos = asset.processorInfos; |
|||
|
|||
UpdateProcessorsFromAssembly(); |
|||
|
|||
// Creating Runtime
|
|||
foreach(ProcessorInfo procInfo in infos) |
|||
{ |
|||
Type processorType = settingsDefinitions[procInfo.Settings.GetType()].processorType; |
|||
var processor = (FrameProcessor)Activator.CreateInstance(processorType, this, procInfo); |
|||
m_Processors.Add(processor); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a1e35fc6527c7334baf151504ef8930a |
|||
timeCreated: 1463737821 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class FrameProcessorStack |
|||
{ |
|||
public ProcessingFrameSequence inputSequence |
|||
{ |
|||
get |
|||
{ |
|||
return m_InputSequence; |
|||
} |
|||
} |
|||
|
|||
public ProcessingFrameSequence outputSequence |
|||
{ |
|||
get |
|||
{ |
|||
if (m_Processors.Count > 0) |
|||
return m_Processors[m_Processors.Count - 1].OutputSequence; |
|||
else |
|||
return m_InputSequence; |
|||
} |
|||
} |
|||
|
|||
public ImageSequencer imageSequencer |
|||
{ |
|||
get { return m_ImageSequencer; } |
|||
} |
|||
|
|||
public List<FrameProcessor> processors |
|||
{ |
|||
get |
|||
{ |
|||
return m_Processors; |
|||
} |
|||
} |
|||
|
|||
private List<FrameProcessor> m_Processors; |
|||
private ProcessingFrameSequence m_InputSequence; |
|||
private ImageSequencer m_ImageSequencer; |
|||
|
|||
public FrameProcessorStack(ProcessingFrameSequence inputSequence, ImageSequencer imageSequencer) |
|||
{ |
|||
m_InputSequence = inputSequence; |
|||
m_Processors = new List<FrameProcessor>(); |
|||
m_ImageSequencer = imageSequencer; |
|||
|
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
foreach(FrameProcessor p in m_Processors) |
|||
{ |
|||
p.Dispose(); |
|||
} |
|||
m_Processors.Clear(); |
|||
} |
|||
|
|||
public ProcessingFrameSequence GetOutputSequence() |
|||
{ |
|||
if(m_Processors.Count > 0) |
|||
{ |
|||
return m_Processors[m_Processors.Count - 1].OutputSequence; |
|||
} |
|||
else |
|||
{ |
|||
return inputSequence; |
|||
} |
|||
} |
|||
|
|||
public ProcessingFrameSequence GetInputSequence(FrameProcessor processor) |
|||
{ |
|||
int index = m_Processors.IndexOf(processor); |
|||
|
|||
if (index > 0) |
|||
{ |
|||
return m_Processors[index - 1].OutputSequence; |
|||
} |
|||
else |
|||
return m_InputSequence; |
|||
} |
|||
|
|||
public FrameProcessor GetNextProcessor(FrameProcessor processor) |
|||
{ |
|||
int index = m_Processors.IndexOf(processor); |
|||
if(index < m_Processors.Count-1) |
|||
{ |
|||
return m_Processors[index + 1]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public void Invalidate(FrameProcessor processor) |
|||
{ |
|||
int index = m_Processors.IndexOf(processor); |
|||
if(index != -1) |
|||
m_Processors[index].Invalidate(); |
|||
} |
|||
|
|||
public void InvalidateAll() |
|||
{ |
|||
if (m_Processors.Count > 0) |
|||
m_Processors[0].Invalidate(); |
|||
} |
|||
|
|||
|
|||
public Dictionary<Type, ProcessorAttribute> settingsDefinitions { get; private set; } |
|||
|
|||
public void UpdateProcessorsFromAssembly() |
|||
{ |
|||
settingsDefinitions = new Dictionary<Type, ProcessorAttribute>(); |
|||
|
|||
var assembly = Assembly.GetAssembly(typeof(ProcessorSettingsBase)); |
|||
var types = assembly.GetTypes(); |
|||
var processorSettingsType = typeof(ProcessorSettingsBase); |
|||
var attrType = typeof(ProcessorAttribute); |
|||
|
|||
var processorSettingTypes = types |
|||
.Where(t => t.IsClass |
|||
&& !t.IsAbstract |
|||
&& t.IsSubclassOf(processorSettingsType) |
|||
&& t.IsDefined(attrType, false)); |
|||
|
|||
foreach (var processorSettingType in processorSettingTypes) |
|||
{ |
|||
var attr = (ProcessorAttribute)processorSettingType.GetCustomAttributes(attrType, false)[0]; |
|||
Type processorType = attr.processorType; |
|||
|
|||
if (!processorType.IsClass || !processorType.IsSubclassOf(typeof(FrameProcessor)) || processorType.IsAbstract) |
|||
throw new InvalidOperationException("Invalid RendererAttribute parameter, type must be a non-abstract class that extends GPUFrameProcessor<>"); |
|||
|
|||
settingsDefinitions.Add(processorSettingType, attr); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3ce623adbc8851e47b42e1381950be6d |
|||
timeCreated: 1461142428 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public abstract class GPUFrameProcessor<T> : FrameProcessor<T> where T : ProcessorSettingsBase |
|||
{ |
|||
protected Shader m_Shader; |
|||
protected Material m_Material; |
|||
|
|||
public GPUFrameProcessor(string shaderPath, FrameProcessorStack processorStack, ProcessorInfo info ) |
|||
: this(AssetDatabase.LoadAssetAtPath<Shader>(shaderPath),processorStack, info) |
|||
{ } |
|||
|
|||
public GPUFrameProcessor(Shader shader, FrameProcessorStack processorStack, ProcessorInfo info ) : base(processorStack, info) |
|||
{ |
|||
m_Shader = shader; |
|||
m_Material = new Material(m_Shader) { hideFlags = HideFlags.DontSave }; |
|||
m_Material.hideFlags = HideFlags.DontSave; |
|||
} |
|||
|
|||
public void ExecuteShaderAndDump(int outputframe, Texture mainTex) |
|||
{ |
|||
ExecuteShaderAndDump(outputframe, mainTex, m_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 override void Dispose() |
|||
{ |
|||
Material.DestroyImmediate(m_Material); |
|||
base.Dispose(); |
|||
} |
|||
|
|||
protected override int GetNumU() |
|||
{ |
|||
if (InputSequence.processor == null) |
|||
return 1; |
|||
return InputSequence.numU; |
|||
} |
|||
|
|||
protected override int GetNumV() |
|||
{ |
|||
if (InputSequence.processor == null) |
|||
return 1; |
|||
return InputSequence.numV; |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a00044f5e3e7b924e925a45bcb585727 |
|||
timeCreated: 1461162132 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
[CustomEditor(typeof(ImageSequence))] |
|||
public class ImageSequenceAssetEditor : Editor |
|||
{ |
|||
private bool m_PreviewInput = false; |
|||
private bool m_PreviewOutput = false; |
|||
private bool m_RequireConstantRepaint = false; |
|||
|
|||
public override bool RequiresConstantRepaint() |
|||
{ |
|||
return m_RequireConstantRepaint; |
|||
} |
|||
|
|||
protected override void OnHeaderGUI() |
|||
{ |
|||
base.OnHeaderGUI(); |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
serializedObject.Update(); |
|||
|
|||
m_RequireConstantRepaint = false; |
|||
|
|||
using (new EditorGUILayout.VerticalScope()) |
|||
{ |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Edit Sequence"), GUILayout.Height(40))) |
|||
{ |
|||
ImageSequencer toolbox = EditorWindow.GetWindow<ImageSequencer>(); |
|||
toolbox.LoadAsset((ImageSequence)Selection.activeObject); |
|||
} |
|||
|
|||
VFXToolboxGUIUtility.ToggleableHeader(true, false, "Input Frames"); |
|||
{ |
|||
var inputFrames = serializedObject.FindProperty("inputFrameGUIDs"); |
|||
int inputFrameCount = inputFrames.arraySize; |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Label("Input sequence contains " + inputFrameCount + " frame(s)."); |
|||
GUILayout.FlexibleSpace(); |
|||
m_PreviewInput = GUILayout.Toggle(m_PreviewInput, VFXToolboxGUIUtility.Get("Preview"), EditorStyles.miniButton); |
|||
} |
|||
|
|||
if(inputFrameCount > 0 && m_PreviewInput) |
|||
{ |
|||
int index; |
|||
|
|||
if(inputFrameCount > 1) |
|||
{ |
|||
m_RequireConstantRepaint = true; |
|||
float time = (float)EditorApplication.timeSinceStartup; |
|||
index = (int)Mathf.Floor((time * 30) % inputFrameCount); |
|||
} |
|||
else |
|||
{ |
|||
index = 0; |
|||
} |
|||
|
|||
var frame = inputFrames.GetArrayElementAtIndex(index); |
|||
string guid = frame.stringValue; |
|||
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(guid)); |
|||
DrawAnimatedPreviewLayout(texture, ((float)index / inputFrameCount)); |
|||
} |
|||
else |
|||
{ |
|||
m_PreviewInput = false; |
|||
} |
|||
} |
|||
|
|||
|
|||
GUILayout.Space(24); |
|||
VFXToolboxGUIUtility.ToggleableHeader(true, false, "Processors"); |
|||
{ |
|||
var processors = serializedObject.FindProperty("processorInfos"); |
|||
int processorsCount = processors.arraySize; |
|||
EditorGUILayout.LabelField("Asset contains " + processorsCount + " Processor (s)."); |
|||
EditorGUI.indentLevel++; |
|||
for(int i = 0; i < processorsCount; i++) |
|||
{ |
|||
var item = processors.GetArrayElementAtIndex(i).objectReferenceValue as ProcessorInfo; |
|||
EditorGUILayout.LabelField("#"+i+" - " + item.ProcessorName + (item.Enabled?"":" (Disabled)")); |
|||
} |
|||
EditorGUI.indentLevel--; |
|||
} |
|||
|
|||
|
|||
GUILayout.Space(24); |
|||
VFXToolboxGUIUtility.ToggleableHeader(true, false, "Export Settings"); |
|||
|
|||
var exportSettings = serializedObject.FindProperty("exportSettings"); |
|||
|
|||
string fileName = exportSettings.FindPropertyRelative("fileName").stringValue; |
|||
var mode = (ImageSequence.ExportMode)exportSettings.FindPropertyRelative("exportMode").enumValueIndex; |
|||
var frameCount = exportSettings.FindPropertyRelative("frameCount"); |
|||
|
|||
EditorGUI.BeginDisabledGroup(true); |
|||
EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Export Format"), mode); |
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
if (fileName != "") |
|||
{ |
|||
EditorGUI.BeginDisabledGroup(true); |
|||
EditorGUILayout.TextField("Exporting to ", fileName); |
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
Rect r = GUILayoutUtility.GetLastRect(); |
|||
r.width += EditorGUIUtility.fieldWidth; |
|||
if (Event.current.rawType == EventType.MouseDown && r.Contains(Event.current.mousePosition)) |
|||
{ |
|||
ImageSequencer.PingOutputTexture(fileName); |
|||
} |
|||
|
|||
string dir = System.IO.Path.GetDirectoryName(fileName); |
|||
string file = System.IO.Path.GetFileNameWithoutExtension(fileName); |
|||
|
|||
string[] assets; |
|||
|
|||
if(!fileName.StartsWith("Assets/")) |
|||
{ |
|||
EditorGUILayout.HelpBox("The output sequence has been exported outside the project, preview will be unavailable", MessageType.Warning); |
|||
return; |
|||
} |
|||
|
|||
if(fileName.Contains("#")) |
|||
{ |
|||
if(System.IO.Directory.Exists(dir)) |
|||
{ |
|||
string[] guids = AssetDatabase.FindAssets(file.Replace('#', '*'), new string[] { dir }); |
|||
assets = new string[guids.Length]; |
|||
for(int i = 0; i < guids.Length; i++) |
|||
{ |
|||
assets[i] = AssetDatabase.GUIDToAssetPath(guids[i]); |
|||
} |
|||
} |
|||
else |
|||
assets = new string[] { }; |
|||
} |
|||
else |
|||
{ |
|||
assets = new string[] { fileName }; |
|||
} |
|||
|
|||
int outputFrameCount; |
|||
if (frameCount.intValue == assets.Length) |
|||
outputFrameCount = frameCount.intValue; |
|||
else |
|||
outputFrameCount = 0; // Something went wrong
|
|||
|
|||
if(outputFrameCount > 0) |
|||
{ |
|||
if(outputFrameCount > 1) |
|||
{ |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Label("Output sequence contains " + assets.Length + " frame(s)."); |
|||
GUILayout.FlexibleSpace(); |
|||
m_PreviewOutput = GUILayout.Toggle(m_PreviewOutput, VFXToolboxGUIUtility.Get("Preview"), EditorStyles.miniButton); |
|||
} |
|||
|
|||
if(m_PreviewOutput) |
|||
{ |
|||
m_RequireConstantRepaint = true; |
|||
float time = (float)EditorApplication.timeSinceStartup; |
|||
int index = (int)Mathf.Floor((time * 30) % outputFrameCount); |
|||
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assets[index]); |
|||
DrawAnimatedPreviewLayout(texture, ((float)index / outputFrameCount)); |
|||
} |
|||
else |
|||
{ |
|||
m_PreviewOutput = false; |
|||
} |
|||
} |
|||
else // Only one frame
|
|||
{ |
|||
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(assets[0]); |
|||
if (texture != null) |
|||
DrawAnimatedPreviewLayout(texture, 0.0f); |
|||
else |
|||
EditorGUILayout.HelpBox("Output Texture could not be loaded, maybe the file was deleted. Please export again using the editor", MessageType.Error); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.HelpBox("The output sequence does not match the number of files on disk, you probably need to export your sequence again", MessageType.Warning); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.HelpBox("This asset has not yet been exported. Please open editor and export it to generate a sequence.",MessageType.None); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void DrawAnimatedPreviewLayout(Texture2D texture, float progress) |
|||
{ |
|||
float ratio = (float)texture.height / (float)texture.width; |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
float width = EditorGUIUtility.currentViewWidth-32; |
|||
float height = 240; |
|||
GUILayout.FlexibleSpace(); |
|||
Rect texture_rect; |
|||
if(ratio >= 1) |
|||
texture_rect = GUILayoutUtility.GetRect(height / ratio, height); |
|||
else |
|||
texture_rect = GUILayoutUtility.GetRect(width, width * ratio); |
|||
|
|||
GUILayout.FlexibleSpace(); |
|||
EditorGUI.DrawTextureTransparent(texture_rect, texture); |
|||
EditorGUI.DrawRect(new Rect(texture_rect.x, texture_rect.y, progress * 200.0f / ratio, 4.0f), new Color(0.3f, 0.5f, 1.0f)); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: aff58427bba3d4a429ffc73dc97bdc6b |
|||
timeCreated: 1471965338 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditor.Callbacks; |
|||
using UnityEditor.ProjectWindowCallback; |
|||
using System.IO; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public class ImageSequenceAssetFactory |
|||
{ |
|||
[MenuItem("Assets/Create/Visual Effects/Image Sequence", priority = 301)] |
|||
private static void MenuCreatePostProcessingProfile() |
|||
{ |
|||
var icon = EditorGUIUtility.FindTexture("ImageSequence Icon"); |
|||
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance<DoCreateImageSequenceAsset>(), "New Image Sequence.asset", icon, null); |
|||
} |
|||
|
|||
public static ImageSequence CreateImageSequenceAtPath(string path) |
|||
{ |
|||
ImageSequence asset = ScriptableObject.CreateInstance<ImageSequence>(); |
|||
asset.name = Path.GetFileName(path); |
|||
AssetDatabase.CreateAsset(asset, path); |
|||
return asset; |
|||
} |
|||
} |
|||
|
|||
internal class DoCreateImageSequenceAsset : EndNameEditAction |
|||
{ |
|||
public override void Action(int instanceId, string pathName, string resourceFile) |
|||
{ |
|||
ImageSequence asset = ImageSequenceAssetFactory.CreateImageSequenceAtPath(pathName); |
|||
ProjectWindowUtil.ShowCreatedAsset(asset); |
|||
} |
|||
} |
|||
|
|||
public class ImageSequenceAssetCallbackHandler |
|||
{ |
|||
[OnOpenAsset(1)] |
|||
public static bool OpenImageSequenceAsset(int instanceID, int line) |
|||
{ |
|||
ImageSequence asset = EditorUtility.InstanceIDToObject(instanceID) as ImageSequence; |
|||
if(asset != null) // We opened an image sequence asset, open the editor.
|
|||
{ |
|||
ImageSequencer.OpenEditor(); |
|||
ImageSequencer window = EditorWindow.GetWindow<ImageSequencer>(); |
|||
window.Focus(); |
|||
return true; |
|||
} |
|||
else |
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a7fed52c3c7c62541a07cb519bfcbc27 |
|||
timeCreated: 1471965338 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using System.IO; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class ImageSequencer : EditorWindow |
|||
{ |
|||
private string GetNumberedFileName(string pattern, int number, int maxFrames) |
|||
{ |
|||
int numbering = (int)Mathf.Floor(Mathf.Log10(maxFrames))+1; |
|||
return pattern.Replace("#", number.ToString("D" + numbering.ToString())); |
|||
} |
|||
|
|||
public string ExportToFile(bool useCurrentFileName) |
|||
{ |
|||
bool bIsInsideProject = true; |
|||
string path; |
|||
if(useCurrentFileName) |
|||
{ |
|||
path = m_CurrentAsset.exportSettings.fileName; |
|||
} |
|||
else |
|||
{ |
|||
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; |
|||
|
|||
string defaultDir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_CurrentAsset)); |
|||
|
|||
defaultFileName = m_CurrentAsset.name; |
|||
|
|||
if (count > 1) |
|||
defaultFileName += "_#"; |
|||
|
|||
if(numU * numV != 1) |
|||
defaultFileName += "_"+numU+"x"+numV; |
|||
|
|||
switch (m_CurrentAsset.exportSettings.exportMode) |
|||
{ |
|||
case ImageSequence.ExportMode.EXR: |
|||
defaultFileName += ".exr"; |
|||
extension = "exr"; |
|||
|
|||
break; |
|||
case ImageSequence.ExportMode.Targa: |
|||
defaultFileName += ".tga"; |
|||
extension = "tga"; |
|||
|
|||
break; |
|||
case ImageSequence.ExportMode.PNG: |
|||
defaultFileName += ".png"; |
|||
extension = "png"; |
|||
|
|||
break; |
|||
default: return null; |
|||
} |
|||
|
|||
path = EditorUtility.SaveFilePanel(title, defaultDir, defaultFileName, extension); |
|||
if (path == null || path == "") |
|||
return ""; |
|||
|
|||
if (path.Contains(Application.dataPath)) |
|||
path = path.Replace(Application.dataPath, "Assets"); |
|||
|
|||
} |
|||
|
|||
if(!path.StartsWith("Assets/")) |
|||
{ |
|||
bIsInsideProject = false; |
|||
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; |
|||
|
|||
if(frameCount > 1 && !Path.GetFileNameWithoutExtension(path).Contains("#")) |
|||
{ |
|||
if (!EditorUtility.DisplayDialog("VFX Toolbox", "You are currently exporting a sequence of images with no # in filename for numbering, do you want to add _# as a suffix of the filename?", "Add Postfix", "Cancel Export")) |
|||
return ""; |
|||
|
|||
string newpath = Path.GetDirectoryName(path) + "\\" + Path.GetFileNameWithoutExtension(path) + "_#" + Path.GetExtension(path); |
|||
path = newpath; |
|||
} |
|||
|
|||
ImageSequence.ExportSettings settings = m_CurrentAsset.exportSettings; |
|||
bool bCanceled = false; |
|||
|
|||
try |
|||
{ |
|||
int i = 1; |
|||
foreach (ProcessingFrame frame in m_processorStack.outputSequence.frames) |
|||
{ |
|||
if(VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Exporting Frame #" + i + "/" + frameCount, (float)i / frameCount, 0, true)) |
|||
{ |
|||
bCanceled = true; |
|||
break; |
|||
} |
|||
|
|||
// Export frame : first, dump data into color array
|
|||
|
|||
Color[] inputs; |
|||
if (frame.texture is Texture2D) // if using input frame
|
|||
{ |
|||
RenderTexture temp = RenderTexture.GetTemporary(frame.texture.width, frame.texture.height, 0, RenderTextureFormat.ARGBHalf); |
|||
Graphics.Blit((Texture2D)frame.texture, temp); |
|||
inputs = ReadBack(temp); |
|||
} |
|||
else // frame.texture is RenderTexture
|
|||
{ |
|||
frame.Process(); |
|||
inputs = ReadBack((RenderTexture)frame.texture); |
|||
} |
|||
|
|||
string fileName = GetNumberedFileName(path, i, frameCount); |
|||
|
|||
// Dump data
|
|||
byte[] bytes; |
|||
|
|||
switch(m_CurrentAsset.exportSettings.exportMode) |
|||
{ |
|||
case ImageSequence.ExportMode.EXR: |
|||
#if UNITY_5_6_OR_NEWER
|
|||
// New Exporter
|
|||
{ |
|||
Texture2D texture = new Texture2D(frame.texture.width, frame.texture.height, TextureFormat.RGBAHalf, settings.generateMipMaps, !settings.sRGB); |
|||
texture.SetPixels(inputs); |
|||
texture.Apply(true); |
|||
bytes = texture.EncodeToEXR(); |
|||
} |
|||
#else
|
|||
// Old Exporter
|
|||
{ |
|||
bytes = MiniEXR.MiniEXR.MiniEXRWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs, true); |
|||
} |
|||
#endif
|
|||
break; |
|||
case ImageSequence.ExportMode.Targa: |
|||
{ |
|||
bytes = MiniTGA.MiniTGA.MiniTGAWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs); |
|||
} |
|||
break; |
|||
case ImageSequence.ExportMode.PNG: |
|||
{ |
|||
Texture2D texture = new Texture2D(frame.texture.width, frame.texture.height, TextureFormat.RGBA32, settings.generateMipMaps, !settings.sRGB); |
|||
texture.SetPixels(inputs); |
|||
texture.Apply(true); |
|||
bytes = texture.EncodeToPNG(); |
|||
} |
|||
break; |
|||
default: |
|||
{ |
|||
bytes = new byte[0] { }; // Empty file that should not happen
|
|||
} |
|||
break; |
|||
} |
|||
File.WriteAllBytes(fileName, bytes); |
|||
|
|||
AssetDatabase.Refresh(); |
|||
|
|||
// Process Import if saved inside project
|
|||
if(bIsInsideProject) |
|||
{ |
|||
TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(fileName); |
|||
importer.wrapMode = m_CurrentAsset.exportSettings.wrapMode; |
|||
importer.filterMode = m_CurrentAsset.exportSettings.filterMode; |
|||
switch(m_CurrentAsset.exportSettings.dataContents) |
|||
{ |
|||
case ImageSequence.DataContents.Color: |
|||
importer.textureType = TextureImporterType.Default; |
|||
break; |
|||
case ImageSequence.DataContents.NormalMap: |
|||
importer.textureType = TextureImporterType.NormalMap; |
|||
importer.convertToNormalmap = false; |
|||
break; |
|||
case ImageSequence.DataContents.NormalMapFromGrayscale: |
|||
importer.textureType = TextureImporterType.NormalMap; |
|||
importer.convertToNormalmap = true; |
|||
break; |
|||
case ImageSequence.DataContents.Sprite: |
|||
importer.textureType = TextureImporterType.Sprite; |
|||
importer.spriteImportMode = SpriteImportMode.Multiple; |
|||
importer.spritesheet = GetSpriteMetaData(frame, m_processorStack.outputSequence.numU, m_processorStack.outputSequence.numV ); |
|||
break; |
|||
} |
|||
importer.mipmapEnabled = m_CurrentAsset.exportSettings.generateMipMaps; |
|||
|
|||
switch(m_CurrentAsset.exportSettings.exportMode) |
|||
{ |
|||
case ImageSequence.ExportMode.Targa: |
|||
importer.sRGBTexture = m_CurrentAsset.exportSettings.sRGB; |
|||
importer.alphaSource = m_CurrentAsset.exportSettings.exportAlpha ? TextureImporterAlphaSource.FromInput : TextureImporterAlphaSource.None; |
|||
importer.textureCompression = m_CurrentAsset.exportSettings.compress ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; |
|||
break; |
|||
case ImageSequence.ExportMode.EXR: |
|||
importer.sRGBTexture = false; |
|||
importer.alphaSource = (m_CurrentAsset.exportSettings.exportAlpha && !m_CurrentAsset.exportSettings.compress) ? TextureImporterAlphaSource.FromInput : TextureImporterAlphaSource.None; |
|||
importer.textureCompression = m_CurrentAsset.exportSettings.compress ? TextureImporterCompression.CompressedHQ : TextureImporterCompression.Uncompressed; |
|||
break; |
|||
case ImageSequence.ExportMode.PNG: |
|||
importer.sRGBTexture = m_CurrentAsset.exportSettings.sRGB; |
|||
importer.alphaSource = m_CurrentAsset.exportSettings.exportAlpha ? TextureImporterAlphaSource.FromInput : TextureImporterAlphaSource.None; |
|||
importer.textureCompression = m_CurrentAsset.exportSettings.compress ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; |
|||
break; |
|||
} |
|||
|
|||
AssetDatabase.ImportAsset(fileName, ImportAssetOptions.ForceUpdate); |
|||
} |
|||
|
|||
// Separate Alpha
|
|||
if (m_CurrentAsset.exportSettings.exportSeparateAlpha) |
|||
{ |
|||
string alphaFilename = fileName.Substring(0, fileName.Length - 4) + "_alpha.tga"; |
|||
// build alpha
|
|||
for(int k = 0; k < inputs.Length; k++) |
|||
{ |
|||
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); |
|||
|
|||
AssetDatabase.Refresh(); |
|||
|
|||
// Process Importer for alpha if inside project
|
|||
if(bIsInsideProject) |
|||
{ |
|||
TextureImporter alphaImporter = (TextureImporter)TextureImporter.GetAtPath(alphaFilename); |
|||
|
|||
if (m_CurrentAsset.exportSettings.dataContents == ImageSequence.DataContents.Sprite) |
|||
{ |
|||
alphaImporter.textureType = TextureImporterType.Sprite; |
|||
alphaImporter.spriteImportMode = SpriteImportMode.Multiple; |
|||
alphaImporter.spritesheet = GetSpriteMetaData(frame, m_processorStack.outputSequence.numU, m_processorStack.outputSequence.numV); |
|||
alphaImporter.alphaSource = TextureImporterAlphaSource.None; |
|||
} |
|||
else |
|||
{ |
|||
alphaImporter.textureType = TextureImporterType.SingleChannel; |
|||
alphaImporter.alphaSource = TextureImporterAlphaSource.FromGrayScale; |
|||
} |
|||
|
|||
alphaImporter.wrapMode = m_CurrentAsset.exportSettings.wrapMode; |
|||
alphaImporter.filterMode = m_CurrentAsset.exportSettings.filterMode; |
|||
alphaImporter.sRGBTexture = false; |
|||
alphaImporter.mipmapEnabled = m_CurrentAsset.exportSettings.generateMipMaps; |
|||
alphaImporter.textureCompression = m_CurrentAsset.exportSettings.compress ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; |
|||
|
|||
AssetDatabase.ImportAsset(alphaFilename, ImportAssetOptions.ForceUpdate); |
|||
} |
|||
} |
|||
|
|||
i++; |
|||
} |
|||
} |
|||
catch(System.Exception e) |
|||
{ |
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
Debug.LogError(e.Message); |
|||
} |
|||
|
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
|
|||
if(bCanceled) |
|||
return ""; |
|||
else |
|||
return path; |
|||
} |
|||
|
|||
public static void PingOutputTexture(string fileName) |
|||
{ |
|||
|
|||
if (fileName == "") |
|||
return; |
|||
|
|||
string dir = System.IO.Path.GetDirectoryName(fileName); |
|||
string file = System.IO.Path.GetFileNameWithoutExtension(fileName); |
|||
|
|||
if(!fileName.StartsWith("Assets/")) |
|||
return; |
|||
|
|||
if(fileName.Contains("#")) |
|||
{ |
|||
if(System.IO.Directory.Exists(dir)) |
|||
{ |
|||
string[] guids = AssetDatabase.FindAssets(file.Replace('#', '*'), new string[] { dir }); |
|||
fileName = AssetDatabase.GUIDToAssetPath(guids[0]); |
|||
} |
|||
} |
|||
|
|||
bool fileFound = (fileName != "")&&(System.IO.File.Exists(fileName)); |
|||
|
|||
if(fileFound) |
|||
{ |
|||
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(fileName); |
|||
if (texture != null) EditorGUIUtility.PingObject(texture); |
|||
} |
|||
else |
|||
{ |
|||
Debug.LogWarning("Could not ping output texture, either the file was moved or removed, you probably need to export your sequence again"); |
|||
} |
|||
} |
|||
|
|||
private void PingCurrentAsset() |
|||
{ |
|||
EditorGUIUtility.PingObject(m_CurrentAsset); |
|||
} |
|||
|
|||
private void UpdateExportedAssets() |
|||
{ |
|||
if (ExportToFile(true) != "") |
|||
m_CurrentAsset.exportSettings.frameCount = (ushort)m_processorStack.outputSequence.frames.Count; |
|||
else |
|||
m_CurrentAsset.exportSettings.frameCount = 0; |
|||
} |
|||
|
|||
private Color[] ReadBack(RenderTexture renderTexture) |
|||
{ |
|||
Color[] inputs = VFXToolboxUtility.ReadBack(renderTexture); |
|||
|
|||
if(QualitySettings.activeColorSpace == ColorSpace.Linear && m_CurrentAsset.exportSettings.sRGB) |
|||
{ |
|||
Color[] outputs = new Color[inputs.Length]; |
|||
for (int j = 0; j < inputs.Length; j++) |
|||
{ |
|||
outputs[j] = inputs[j].gamma; |
|||
} |
|||
return outputs; |
|||
} |
|||
return inputs; |
|||
} |
|||
|
|||
private SpriteMetaData[] GetSpriteMetaData(ProcessingFrame frame, int numU, int numV) |
|||
{ |
|||
SpriteMetaData[] result = new SpriteMetaData[numU * numV]; |
|||
|
|||
float width = (float)frame.texture.width / numU; |
|||
float height = (float)frame.texture.height / numV; |
|||
|
|||
for(int i = 0; i < numU; i++) |
|||
for(int j = 0; j < numV; j++) |
|||
{ |
|||
SpriteMetaData data = new SpriteMetaData(); |
|||
data.name = "Frame_" + (i + (j * numU)); |
|||
data.rect = new Rect(i * width, (numV - j - 1) * height, width, height); |
|||
result[i + (j * numU)] = data; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private static GUIContent[] GetExportModeFriendlyNames() |
|||
{ |
|||
return new GUIContent[] { VFXToolboxGUIUtility.Get("Targa"), VFXToolboxGUIUtility.Get("OpenEXR (HDR)"), VFXToolboxGUIUtility.Get("PNG") }; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 3a6ece5b6c8d2084aaef5221050b8cfa |
|||
timeCreated: 1471965338 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditorInternal; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class ImageSequencer : EditorWindow |
|||
{ |
|||
private Splitter m_Splitter; |
|||
private ReorderableList m_InputFramesReorderableList; |
|||
private ReorderableList m_ProcessorsReorderableList; |
|||
private Vector2 m_OptionsViewScroll = Vector2.zero; |
|||
private Vector2 m_MinimumSize; |
|||
private SidePanelMode m_SidePanelViewMode = 0; |
|||
private bool m_Dirty = true; |
|||
private bool m_NeedRedraw = false; |
|||
|
|||
public void InitializeGUI() |
|||
{ |
|||
minSize = m_MinimumSize; |
|||
|
|||
if(m_Splitter == null) |
|||
{ |
|||
m_Splitter = new Splitter(360, DrawEditPanelGUI, DrawCanvasGUI, Splitter.SplitLockMode.LeftMinMax, new Vector2(320.0f, 480.0f)); |
|||
} |
|||
|
|||
if(m_PreviewCanvas == null) |
|||
{ |
|||
m_PreviewCanvas = new ImageSequencerCanvas(new Rect(0, 16, position.width - m_Splitter.value, position.height - 16),this); |
|||
} |
|||
|
|||
CheckGraphicsSettings(); |
|||
} |
|||
|
|||
public void OnGUI() |
|||
{ |
|||
titleContent = styles.title; |
|||
m_MinimumSize = new Vector2(880, 320); |
|||
|
|||
InitializeGUI(); |
|||
|
|||
if(m_CurrentAsset == null) |
|||
{ |
|||
OnNoAssetGUI(); |
|||
return; |
|||
} |
|||
|
|||
m_Dirty = false; |
|||
|
|||
m_CurrentAssetSerializedObject.Update(); |
|||
|
|||
UpdateCanvasRect(); |
|||
|
|||
if(HandleDropData()) return; |
|||
|
|||
DrawToolbar(); |
|||
|
|||
Rect rect = new Rect(0,18, position.width, position.height-18); |
|||
if (m_Splitter.DoSplitter(rect)) |
|||
Invalidate(); |
|||
|
|||
// Processing Play Mode, Cooking & Autocooking
|
|||
if (previewCanvas.isPlaying && previewCanvas.sequence.length > 1) |
|||
Invalidate(); |
|||
else |
|||
{ |
|||
if (Event.current.type == EventType.Repaint) |
|||
{ |
|||
if(m_NeedRedraw) |
|||
{ |
|||
previewCanvas.UpdateCanvasSequence(); |
|||
Invalidate(); |
|||
m_NeedRedraw = false; |
|||
} |
|||
else if((m_AutoCook && m_CurrentProcessor != null)) |
|||
{ |
|||
m_CurrentProcessor.RequestProcessOneFrame(previewCanvas.currentFrameIndex); |
|||
Invalidate(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// if Invalidated this frame, repaint.
|
|||
if (m_Dirty) Repaint(); |
|||
} |
|||
|
|||
public void OnNoAssetGUI() |
|||
{ |
|||
UpdateCanvasRect(); |
|||
|
|||
if(HandleDropData()) return; |
|||
|
|||
using (new EditorGUILayout.VerticalScope()) |
|||
{ |
|||
GUILayout.FlexibleSpace(); |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.FlexibleSpace(); |
|||
EditorGUILayout.HelpBox("No Image Sequence is currently selected.\nPlease create one within your Assets then select It in the project view.", MessageType.Info); |
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
GUILayout.Space(8); |
|||
|
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.FlexibleSpace(); |
|||
if (GUILayout.Button("Create Image Sequence", GUILayout.Width(160))) |
|||
{ |
|||
string file = EditorUtility.SaveFilePanelInProject("Create Image Sequence", "New Image Sequence", "asset", "Create Image Sequence?"); |
|||
if (file != string.Empty) |
|||
{ |
|||
var sequence = ImageSequenceAssetFactory.CreateImageSequenceAtPath(file); |
|||
AssetDatabase.ImportAsset(file); |
|||
LoadAsset(sequence); |
|||
} |
|||
} |
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
} |
|||
|
|||
private bool HandleDropData() |
|||
{ |
|||
if(m_CurrentAsset == null) |
|||
return false; |
|||
|
|||
if(sidePanelViewMode == SidePanelMode.InputFrames && DragAndDrop.paths.Length > 0) |
|||
{ |
|||
DragAndDrop.visualMode = DragAndDropVisualMode.Link; |
|||
if( Event.current.type == EventType.DragExited) |
|||
{ |
|||
List<string> texturePaths = new List<string>(); |
|||
foreach(string path in DragAndDrop.paths) |
|||
{ |
|||
if (VFXToolboxUtility.IsDirectory(path)) |
|||
texturePaths.AddRange(VFXToolboxUtility.GetAllTexturesInPath(path)); |
|||
else |
|||
{ |
|||
VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Discovering Assets...", 0.5f); |
|||
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(path); |
|||
if(t != null) |
|||
texturePaths.Add(path); |
|||
} |
|||
} |
|||
AddInputFrame(m_InputFramesReorderableList, texturePaths); |
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private void DrawToolbar() |
|||
{ |
|||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar)) |
|||
{ |
|||
EditorGUI.BeginChangeCheck(); |
|||
bool prev; |
|||
|
|||
bool bMaskR = m_PreviewCanvas.maskR; |
|||
bool bMaskG = m_PreviewCanvas.maskG; |
|||
bool bMaskB = m_PreviewCanvas.maskB; |
|||
bool bMaskA = m_PreviewCanvas.maskA; |
|||
bool bMaskRGB = bMaskR && bMaskG && bMaskB; |
|||
|
|||
//GUILayout.Space(m_Splitter.value);
|
|||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Width(m_Splitter.value))) |
|||
{ |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Current Sequence: "+m_CurrentAsset.name), EditorStyles.toolbarButton)) |
|||
{ |
|||
PingCurrentAsset(); |
|||
} |
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
Rect r = GUILayoutUtility.GetRect(VFXToolboxGUIUtility.GetTextAndIcon(" ", "SceneviewFx"), EditorStyles.toolbarPopup); |
|||
if (GUI.Button(r, VFXToolboxGUIUtility.GetTextAndIcon(" ", "SceneviewFx"), EditorStyles.toolbarPopup)) |
|||
{ |
|||
PopupWindow.Show(r, (PopupWindowContent) new CanvasConfigPopupWindowContent(this)); |
|||
} |
|||
|
|||
GUILayout.Space(20); |
|||
|
|||
bMaskRGB = GUILayout.Toggle(bMaskRGB, styles.iconRGB, EditorStyles.toolbarButton); |
|||
|
|||
if(bMaskRGB != (bMaskR && bMaskG && bMaskB)) |
|||
{ |
|||
bMaskR = bMaskG = bMaskB = bMaskRGB; |
|||
|
|||
m_PreviewCanvas.maskR = bMaskR; |
|||
m_PreviewCanvas.maskG = bMaskG; |
|||
m_PreviewCanvas.maskB = bMaskB; |
|||
|
|||
} |
|||
|
|||
prev = bMaskR; |
|||
bMaskR = GUILayout.Toggle(bMaskR, VFXToolboxGUIUtility.Get("R"),styles.MaskRToggle); |
|||
|
|||
if (bMaskR != prev) |
|||
m_PreviewCanvas.maskR = bMaskR; |
|||
|
|||
prev = bMaskG; |
|||
bMaskG = GUILayout.Toggle(bMaskG, VFXToolboxGUIUtility.Get("G"),styles.MaskGToggle); |
|||
if (bMaskG != prev) |
|||
m_PreviewCanvas.maskG = bMaskG; |
|||
|
|||
prev = bMaskB; |
|||
bMaskB = GUILayout.Toggle(bMaskB, VFXToolboxGUIUtility.Get("B"),styles.MaskBToggle); |
|||
if (bMaskB != prev) |
|||
m_PreviewCanvas.maskB = bMaskB; |
|||
|
|||
prev = bMaskA; |
|||
bMaskA = GUILayout.Toggle(bMaskA, VFXToolboxGUIUtility.Get("A"),styles.MaskAToggle); |
|||
if (bMaskA != prev) |
|||
m_PreviewCanvas.maskA = bMaskA; |
|||
|
|||
if(m_PreviewCanvas.sequence != null && m_PreviewCanvas.numFrames > 0 && m_PreviewCanvas.currentFrame != null) |
|||
{ |
|||
GUILayout.Space(20.0f); |
|||
|
|||
if(m_PreviewCanvas.mipMapCount > 0) |
|||
{ |
|||
int currentMip = m_PreviewCanvas.mipMap; |
|||
int newMip = currentMip; |
|||
|
|||
{ |
|||
Rect mipRect = GUILayoutUtility.GetRect(164, 24); |
|||
GUI.Box(mipRect, GUIContent.none, VFXToolboxStyles.toolbarButton); |
|||
|
|||
GUI.Label(new RectOffset(0, 0, 0, 0).Remove(mipRect), styles.iconMipMapDown); |
|||
newMip = (int)Mathf.Round(GUI.HorizontalSlider(new RectOffset(24,64,0,0).Remove(mipRect), (float)newMip, 0.0f, (float)m_PreviewCanvas.mipMapCount-1)); |
|||
GUI.Label(new RectOffset(100, 0, 0, 0).Remove(mipRect), styles.iconMipMapUp); |
|||
if (newMip != currentMip) |
|||
{ |
|||
m_PreviewCanvas.mipMap = newMip; |
|||
} |
|||
GUI.Label(new RectOffset(124, 0, 0, 0).Remove(mipRect), (m_PreviewCanvas.mipMap+1)+"/"+m_PreviewCanvas.mipMapCount, VFXToolboxStyles.toolbarLabelLeft); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(EditorGUI.EndChangeCheck()) |
|||
{ |
|||
m_PreviewCanvas.UpdateCanvasSequence(); // Reblit if changed the flags.
|
|||
} |
|||
|
|||
GUILayout.Space(20); |
|||
|
|||
{ |
|||
Rect brightnessRect = GUILayoutUtility.GetRect(160, 24); |
|||
GUI.Box(brightnessRect, GUIContent.none, VFXToolboxStyles.toolbarButton); |
|||
GUI.Label(new RectOffset(4, 0, 0, 0).Remove(brightnessRect), VFXToolboxGUIUtility.GetTextAndIcon("Background|Sets the Background Brightness", "CheckerFloor"), VFXToolboxStyles.toolbarLabelLeft); |
|||
|
|||
float newBrightness = GUI.HorizontalSlider(new RectOffset(82, 4, 0, 0).Remove(brightnessRect), previewCanvas.BackgroundBrightness, 0.0f, 1.0f); |
|||
if (previewCanvas.BackgroundBrightness != newBrightness) |
|||
previewCanvas.BackgroundBrightness = newBrightness; |
|||
} |
|||
|
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
} |
|||
|
|||
private void DrawCanvasGUI(Rect rect) |
|||
{ |
|||
|
|||
if (previewCanvas.sequence.length > 1) |
|||
previewCanvas.displayRect = new Rect(m_Splitter.value, 16, position.width - m_Splitter.value, position.height - 116); |
|||
else |
|||
previewCanvas.displayRect = new Rect(m_Splitter.value, 16, position.width - m_Splitter.value, position.height - 16); |
|||
|
|||
previewCanvas.OnGUI(this); |
|||
|
|||
// Draw Update Button
|
|||
if(m_CurrentAsset.exportSettings.fileName != "") |
|||
{ |
|||
Rect exportButtonRect = new Rect(position.width - 100, 24, 74, 24); |
|||
if (GUI.Button(exportButtonRect, VFXToolboxGUIUtility.GetTextAndIcon("Update", "SaveActive"), VFXToolboxStyles.TabButtonSingle)) |
|||
{ |
|||
UpdateExportedAssets(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void DrawEditPanelGUI(Rect rect) |
|||
{ |
|||
using (new GUILayout.AreaScope(rect)) |
|||
{ |
|||
m_OptionsViewScroll = EditorGUILayout.BeginScrollView(m_OptionsViewScroll, styles.scrollView, GUILayout.Width(m_Splitter.value)); |
|||
|
|||
using (new EditorGUILayout.VerticalScope()) |
|||
{ |
|||
GUILayout.Space(16); |
|||
// Three Button Tabs : Mode Selection
|
|||
DrawTabbedPanelSelector(); |
|||
GUILayout.Space(16); |
|||
|
|||
switch (m_SidePanelViewMode) |
|||
{ |
|||
case SidePanelMode.InputFrames: |
|||
// Draw Input Frames Panel
|
|||
DrawInputFramesPanelContent(); |
|||
break; |
|||
case SidePanelMode.Processors: |
|||
// Draw Processors Edit Panel
|
|||
DrawProcessorsPanelContent(); |
|||
break; |
|||
case SidePanelMode.Export: |
|||
// Draw Export Panel
|
|||
DrawExportPanelContent(); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
GUILayout.Space(32); |
|||
} |
|||
EditorGUILayout.EndScrollView(); |
|||
|
|||
if(QualitySettings.activeColorSpace == ColorSpace.Gamma) |
|||
{ |
|||
EditorGUILayout.HelpBox("Your project is configured to use Gamma color space. While this is not a breaking setting for the ImageSequencer to work, It will produce a different and unexpected results than when used in linear color space.", MessageType.Warning); |
|||
GUILayout.Space(8); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void DrawTabbedPanelSelector() |
|||
{ |
|||
SidePanelMode prevMode = m_SidePanelViewMode; |
|||
bool hasInputFrames = m_processorStack.inputSequence.frames.Count > 0; |
|||
SidePanelMode newMode = (SidePanelMode)VFXToolboxGUIUtility.TabbedButtonsGUILayout( |
|||
(int)prevMode, |
|||
new string[] { "Input Frames", "Processors", "Export"}, |
|||
new bool [] { true, hasInputFrames, hasInputFrames} |
|||
); |
|||
|
|||
if(prevMode != newMode) |
|||
{ |
|||
m_SidePanelViewMode = newMode; |
|||
|
|||
switch(m_SidePanelViewMode) |
|||
{ |
|||
case SidePanelMode.InputFrames: |
|||
|
|||
m_PreviewCanvas.sequence = m_processorStack.inputSequence; |
|||
|
|||
break; |
|||
|
|||
case SidePanelMode.Processors: |
|||
|
|||
if (m_LockedPreviewProcessor != null) |
|||
m_PreviewCanvas.sequence = m_LockedPreviewProcessor.OutputSequence; |
|||
else |
|||
{ |
|||
if(m_CurrentProcessor != null) |
|||
m_PreviewCanvas.sequence = m_CurrentProcessor.OutputSequence; |
|||
else |
|||
{ |
|||
if (m_processorStack.processors.Count > 0) |
|||
m_PreviewCanvas.sequence = m_processorStack.processors[m_processorStack.processors.Count - 1].OutputSequence; |
|||
else |
|||
m_PreviewCanvas.sequence = m_processorStack.inputSequence; |
|||
} |
|||
} |
|||
|
|||
break; |
|||
|
|||
case SidePanelMode.Export: |
|||
|
|||
m_PreviewCanvas.sequence = m_processorStack.outputSequence; |
|||
|
|||
break; |
|||
} |
|||
|
|||
m_PreviewCanvas.InvalidateRenderTarget(); |
|||
m_PreviewCanvas.UpdateCanvasSequence(); |
|||
m_PreviewCanvas.Invalidate(true); |
|||
} |
|||
|
|||
} |
|||
|
|||
private void DrawInputFramesPanelContent() |
|||
{ |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Label(VFXToolboxGUIUtility.Get("Input Frames"),EditorStyles.boldLabel); |
|||
GUILayout.FlexibleSpace(); |
|||
|
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Actions"), EditorStyles.popup, GUILayout.Width(80), GUILayout.Height(20))) |
|||
{ |
|||
GenericMenu menu = new GenericMenu(); |
|||
menu.AddItem(VFXToolboxGUIUtility.Get("Clear"), false, MenuClearInputFrames); |
|||
menu.AddItem(VFXToolboxGUIUtility.Get("Sort All"), false, MenuSortInputFrames); |
|||
menu.AddItem(VFXToolboxGUIUtility.Get("Reverse Oder"), false, MenuReverseInputFrames); |
|||
menu.ShowAsContext(); |
|||
} |
|||
|
|||
} |
|||
GUILayout.Space(8); |
|||
m_InputFramesReorderableList.DoLayoutList(); |
|||
|
|||
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_processorStack.inputSequence.length > 0) |
|||
{ |
|||
RemoveInputFrame(m_InputFramesReorderableList); |
|||
Event.current.Use(); |
|||
} |
|||
} |
|||
|
|||
private void DrawProcessorsPanelContent() |
|||
{ |
|||
ImageSequence seq = (ImageSequence)EditorGUILayout.ObjectField(VFXToolboxGUIUtility.Get("Inherit processors from"), m_CurrentAsset.inheritSettingsReference, typeof(ImageSequence), false); |
|||
|
|||
if (m_IgnoreInheritSettings) |
|||
EditorGUILayout.HelpBox("Warning : Dependency Loop found when inheriting these settings, ignoring...", MessageType.Warning); |
|||
|
|||
if(seq != m_CurrentAsset.inheritSettingsReference && m_CurrentAsset != seq) |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "use processor settings from other ImageSequence"); |
|||
m_CurrentAsset.inheritSettingsReference = seq; |
|||
if(seq != null) |
|||
{ |
|||
m_CurrentAsset.editSettings.selectedProcessor = seq.editSettings.selectedProcessor; |
|||
m_CurrentAsset.editSettings.lockedProcessor = -1; |
|||
} |
|||
EditorUtility.SetDirty(m_CurrentAsset); |
|||
LoadAsset(m_CurrentAsset); |
|||
} |
|||
GUILayout.Space(10); |
|||
|
|||
using (new EditorGUI.DisabledScope(m_CurrentAsset.inheritSettingsReference != null)) |
|||
{ |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Label(VFXToolboxGUIUtility.Get("Frame Processor Stack"),EditorStyles.boldLabel,GUILayout.Width(180)); |
|||
GUILayout.FlexibleSpace(); |
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Clear"), GUILayout.Width(80))) |
|||
{ |
|||
// Delete everything
|
|||
Undo.RecordObject(m_CurrentAsset, "Clear All Processors"); |
|||
m_processorStack.RemoveAllProcessors(m_CurrentAsset); |
|||
// Update UI
|
|||
m_ProcessorsReorderableList.index = -1; |
|||
m_CurrentProcessor = null; |
|||
m_LockedPreviewProcessor = null; |
|||
m_CurrentAsset.editSettings.lockedProcessor = -1; |
|||
m_CurrentAsset.editSettings.selectedProcessor = -1; |
|||
m_PreviewCanvas.sequence = m_processorStack.inputSequence; |
|||
EditorUtility.SetDirty(m_CurrentAsset); |
|||
// Request Repaint
|
|||
Invalidate(); |
|||
RefreshCanvas(); |
|||
return; |
|||
} |
|||
} |
|||
GUILayout.Space(8); |
|||
} |
|||
|
|||
m_ProcessorsReorderableList.DoLayoutList(); |
|||
|
|||
if(m_IgnoreInheritSettings || m_CurrentAsset.inheritSettingsReference == null) |
|||
{ |
|||
GUILayout.Space(10); |
|||
|
|||
// Draw inspector and Invalidates whatever needs to.
|
|||
for(int i = 0; i < m_processorStack.processors.Count; i++) |
|||
{ |
|||
if(m_ProcessorsReorderableList.index == i) |
|||
{ |
|||
bool changed = m_processorStack.processors[i].OnSidePanelGUI(m_CurrentAsset,i); |
|||
if (changed) |
|||
{ |
|||
m_processorStack.processors[i].Invalidate(); |
|||
UpdateViewport(); |
|||
} |
|||
m_Dirty = m_Dirty || changed; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.HelpBox("Settings cannot be accessed when linked from external Image Sequence", MessageType.Info); |
|||
} |
|||
|
|||
|
|||
// Handle final keyboard events (delete)
|
|||
if(Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete && m_processorStack.processors.Count > 0) |
|||
{ |
|||
MenuRemoveProcessor(m_ProcessorsReorderableList); |
|||
Event.current.Use(); |
|||
} |
|||
} |
|||
|
|||
private void DrawExportPanelContent() |
|||
{ |
|||
int length = m_processorStack.outputSequence.length; |
|||
|
|||
if(length > 0) |
|||
{ |
|||
m_CurrentAssetSerializedObject.Update(); |
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
ImageSequence.ExportSettings prevState = m_CurrentAsset.exportSettings; |
|||
|
|||
using (new VFXToolboxGUIUtility.HeaderSectionScope("File Export Options")) |
|||
{ |
|||
|
|||
ImageSequence.ExportMode prevMode = m_CurrentAsset.exportSettings.exportMode; |
|||
|
|||
m_CurrentAsset.exportSettings.exportMode = (ImageSequence.ExportMode)EditorGUILayout.Popup(VFXToolboxGUIUtility.Get("Export Format"), (int)m_CurrentAsset.exportSettings.exportMode, GetExportModeFriendlyNames()); |
|||
|
|||
if (prevMode != m_CurrentAsset.exportSettings.exportMode) |
|||
{ |
|||
m_CurrentAsset.exportSettings.fileName = ""; |
|||
} |
|||
|
|||
switch(m_CurrentAsset.exportSettings.exportMode) |
|||
{ |
|||
|
|||
case ImageSequence.ExportMode.EXR: |
|||
m_CurrentAsset.exportSettings.highDynamicRange = true; |
|||
m_CurrentAsset.exportSettings.sRGB = false; |
|||
break; |
|||
case ImageSequence.ExportMode.PNG: |
|||
case ImageSequence.ExportMode.Targa: |
|||
m_CurrentAsset.exportSettings.highDynamicRange = false; |
|||
break; |
|||
} |
|||
|
|||
|
|||
EditorGUI.BeginDisabledGroup(true); |
|||
EditorGUILayout.TextField(VFXToolboxGUIUtility.Get("File Name|File name or pattern of the export sequence, using # characters will add frame number to the file name, use multiple ### to ensure leading zeroes."), m_CurrentAsset.exportSettings.fileName); |
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
Rect r = GUILayoutUtility.GetLastRect(); |
|||
r.width += EditorGUIUtility.fieldWidth; |
|||
|
|||
if (Event.current.rawType == EventType.MouseDown && r.Contains(Event.current.mousePosition)) |
|||
{ |
|||
PingOutputTexture(m_CurrentAsset.exportSettings.fileName); |
|||
} |
|||
|
|||
if(!m_CurrentAsset.exportSettings.highDynamicRange) |
|||
m_CurrentAsset.exportSettings.sRGB = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("sRGB (Color Data)|Whether the texture contains color (or not), HDR Data is always non sRGB."), m_CurrentAsset.exportSettings.sRGB); |
|||
|
|||
EditorGUI.BeginDisabledGroup(m_CurrentAsset.exportSettings.compress && m_CurrentAsset.exportSettings.highDynamicRange); |
|||
m_CurrentAsset.exportSettings.exportAlpha = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("Export Alpha|Whether to export the alpha channel"), m_CurrentAsset.exportSettings.exportAlpha); |
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
m_CurrentAsset.exportSettings.exportSeparateAlpha = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("Separate Alpha|Export the alpha channel as a separate TGA Grayscale file with a \"_alpha\" suffix."), m_CurrentAsset.exportSettings.exportSeparateAlpha); |
|||
|
|||
} |
|||
|
|||
using (new VFXToolboxGUIUtility.HeaderSectionScope("Texture Import Options")) |
|||
{ |
|||
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); |
|||
} |
|||
|
|||
switch(m_CurrentAsset.exportSettings.dataContents) |
|||
{ |
|||
case ImageSequence.DataContents.NormalMapFromGrayscale: |
|||
case ImageSequence.DataContents.NormalMap: |
|||
m_CurrentAsset.exportSettings.sRGB = false; |
|||
m_CurrentAsset.exportSettings.exportAlpha = false; |
|||
break; |
|||
default: break; |
|||
} |
|||
|
|||
if(!m_CurrentAsset.exportSettings.highDynamicRange) |
|||
m_CurrentAsset.exportSettings.sRGB = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("sRGB (Color Data)|Whether the texture contains color (or not), HDR Data is always non sRGB."), m_CurrentAsset.exportSettings.sRGB); |
|||
|
|||
m_CurrentAsset.exportSettings.compress = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("Compress|Whether to apply texture compression (HDR Compressed Data does not support alpha channel)"), m_CurrentAsset.exportSettings.compress); |
|||
m_CurrentAsset.exportSettings.generateMipMaps = EditorGUILayout.Toggle(VFXToolboxGUIUtility.Get("Generate MipMaps|Whether generate mipmaps."), m_CurrentAsset.exportSettings.generateMipMaps); |
|||
m_CurrentAsset.exportSettings.wrapMode = (TextureWrapMode)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Wrap Mode|Texture Wrap mode"), m_CurrentAsset.exportSettings.wrapMode); |
|||
m_CurrentAsset.exportSettings.filterMode = (FilterMode)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Filter Mode|Texture Filter mode"), m_CurrentAsset.exportSettings.filterMode); |
|||
|
|||
if(m_CurrentAsset.exportSettings.compress && m_CurrentAsset.exportSettings.highDynamicRange) |
|||
{ |
|||
m_CurrentAsset.exportSettings.exportAlpha = false; |
|||
} |
|||
|
|||
} |
|||
|
|||
if(GUILayout.Button("Export as New...", GUILayout.Height(24))) |
|||
{ |
|||
string fileName = ""; |
|||
|
|||
fileName = ExportToFile(false); |
|||
|
|||
if (fileName != "") |
|||
{ |
|||
m_CurrentAsset.exportSettings.fileName = fileName; |
|||
m_CurrentAsset.exportSettings.frameCount = (ushort)m_processorStack.outputSequence.frames.Count; |
|||
} |
|||
} |
|||
// Export Again
|
|||
if( m_CurrentAsset.exportSettings.fileName != null && |
|||
((m_CurrentAsset.exportSettings.fileName.EndsWith(".tga") && m_CurrentAsset.exportSettings.exportMode == ImageSequence.ExportMode.Targa) |
|||
|| (m_CurrentAsset.exportSettings.fileName.EndsWith(".exr") && m_CurrentAsset.exportSettings.exportMode == ImageSequence.ExportMode.EXR) |
|||
|| (m_CurrentAsset.exportSettings.fileName.EndsWith(".png") && m_CurrentAsset.exportSettings.exportMode == ImageSequence.ExportMode.PNG) |
|||
)) |
|||
{ |
|||
if(GUILayout.Button("Update Exported Assets", GUILayout.Height(24))) |
|||
{ |
|||
UpdateExportedAssets(); |
|||
} |
|||
} |
|||
|
|||
if (m_CurrentAsset.exportSettings.dataContents == ImageSequence.DataContents.NormalMap) |
|||
EditorGUILayout.HelpBox("The selected import mode assumes that the frame data is a normal map. To generate a normal map from grayscale, use Normal Map From Grayscale instead.",MessageType.Info); |
|||
|
|||
if(EditorGUI.EndChangeCheck()) |
|||
{ |
|||
ImageSequence.ExportSettings curState = m_CurrentAsset.exportSettings; |
|||
m_CurrentAsset.exportSettings = prevState; |
|||
Undo.RecordObject(m_CurrentAsset, "Update Export Settings"); |
|||
m_CurrentAsset.exportSettings = curState; |
|||
m_CurrentAssetSerializedObject.ApplyModifiedProperties(); |
|||
EditorUtility.SetDirty(m_CurrentAsset); |
|||
|
|||
AssetDatabase.Refresh(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
EditorGUILayout.HelpBox("You do not have any frames to export.", MessageType.Warning); |
|||
} |
|||
|
|||
} |
|||
|
|||
private void UpdateCanvasRect() |
|||
{ |
|||
previewCanvas.displayRect = new Rect(m_Splitter.value, 16, position.width - m_Splitter.value, position.height - 16 ); |
|||
} |
|||
|
|||
public void Invalidate() |
|||
{ |
|||
m_Dirty = true; |
|||
} |
|||
|
|||
public void UpdateViewport() |
|||
{ |
|||
m_NeedRedraw = true; |
|||
} |
|||
|
|||
private class CanvasConfigPopupWindowContent : PopupWindowContent |
|||
{ |
|||
private static Styles s_Styles; |
|||
private ImageSequencer m_Window; |
|||
|
|||
public CanvasConfigPopupWindowContent(ImageSequencer window) |
|||
{ |
|||
m_Window = window; |
|||
} |
|||
|
|||
public override Vector2 GetWindowSize() |
|||
{ |
|||
return new Vector2(200, 292); |
|||
} |
|||
|
|||
public override void OnGUI(Rect rect) |
|||
{ |
|||
if (s_Styles == null) |
|||
s_Styles = new Styles(); |
|||
|
|||
using (new GUILayout.AreaScope(rect)) |
|||
{ |
|||
bool needRepaint = false; |
|||
|
|||
using (new GUILayout.VerticalScope()) |
|||
{ |
|||
EditorGUI.BeginChangeCheck(); |
|||
DoHeaderLayout("Viewport Options"); |
|||
m_Window.previewCanvas.showGrid = GUILayout.Toggle(m_Window.previewCanvas.showGrid, VFXToolboxGUIUtility.Get("Grid Outline"), s_Styles.menuItem ); |
|||
m_Window.previewCanvas.showExtraInfo = GUILayout.Toggle(m_Window.previewCanvas.showExtraInfo, VFXToolboxGUIUtility.Get("Frame Processor Overlays"), s_Styles.menuItem ); |
|||
m_Window.previewCanvas.filter = GUILayout.Toggle(m_Window.previewCanvas.filter, VFXToolboxGUIUtility.Get("Texture Filtering"), s_Styles.menuItem ); |
|||
|
|||
DoHeaderLayout("Center View"); |
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Fit to Window"),s_Styles.menuItem)) |
|||
{ |
|||
m_Window.previewCanvas.Recenter(true); |
|||
needRepaint = true; |
|||
} |
|||
|
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Reset Zoom"),s_Styles.menuItem)) |
|||
{ |
|||
m_Window.previewCanvas.Recenter(false); |
|||
needRepaint = true; |
|||
} |
|||
|
|||
DoHeaderLayout("Background Options"); |
|||
|
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Reset Brightness"),s_Styles.menuItem)) |
|||
{ |
|||
m_Window.previewCanvas.ResetBrightness(); |
|||
needRepaint = true; |
|||
} |
|||
|
|||
DoHeaderLayout("Processing Options"); |
|||
m_Window.m_AutoCook = GUILayout.Toggle(m_Window.m_AutoCook, VFXToolboxGUIUtility.Get("AutoCook"), s_Styles.menuItem ); |
|||
|
|||
DoHeaderLayout("Find in Project..."); |
|||
|
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("This Image Sequence"),s_Styles.menuItem)) |
|||
{ |
|||
m_Window.PingCurrentAsset(); |
|||
|
|||
} |
|||
|
|||
if(m_Window.m_CurrentAsset.exportSettings.fileName != "") |
|||
{ |
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Exported Texture"),s_Styles.menuItem)) |
|||
{ |
|||
PingOutputTexture(m_Window.m_CurrentAsset.exportSettings.fileName); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
using (new EditorGUI.DisabledScope(true)) |
|||
{ |
|||
GUILayout.Button(VFXToolboxGUIUtility.Get("Exported Texture"),s_Styles.menuItem); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
DoHeaderLayout("Help and Feedback"); |
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Documentation"),s_Styles.menuItem)) |
|||
{ |
|||
Application.OpenURL("https://drive.google.com/open?id=1YUwzA1mGvzWRpajDV-XF0iUd4RhW--bhMpqo-gmj9B8"); |
|||
} |
|||
|
|||
if(GUILayout.Button(VFXToolboxGUIUtility.Get("Forums (for Feedback)"),s_Styles.menuItem)) |
|||
{ |
|||
Application.OpenURL("https://forum.unity3d.com/forums/vfx-toolbox.119/"); |
|||
} |
|||
*/ |
|||
|
|||
if(EditorGUI.EndChangeCheck()) |
|||
{ |
|||
needRepaint = true; |
|||
} |
|||
} |
|||
|
|||
if (needRepaint) |
|||
m_Window.Repaint(); |
|||
} |
|||
|
|||
if (Event.current.type == EventType.MouseMove) |
|||
Event.current.Use(); |
|||
|
|||
if (Event.current.type != EventType.KeyDown || Event.current.keyCode != KeyCode.Escape) |
|||
return; |
|||
this.editorWindow.Close(); |
|||
GUIUtility.ExitGUI(); |
|||
} |
|||
|
|||
private void DoHeaderLayout(string headerText) |
|||
{ |
|||
GUILayout.Label(GUIContent.none, s_Styles.separator); |
|||
GUILayout.Label(VFXToolboxGUIUtility.Get(headerText), EditorStyles.boldLabel); |
|||
} |
|||
|
|||
private class Styles |
|||
{ |
|||
public readonly GUIStyle menuItem = (GUIStyle)"MenuItem"; |
|||
public readonly GUIStyle separator = (GUIStyle)"sv_iconselector_sep"; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c6c1b3a301eecfe478e5fbb2575daaa2 |
|||
timeCreated: 1471965339 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditorInternal; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class ImageSequencer : EditorWindow |
|||
{ |
|||
|
|||
private int m_InputFramesHashCode; |
|||
|
|||
private void AddInputFrame(ReorderableList list, List<string> names) |
|||
{ |
|||
if(names.Count> 0) |
|||
{ |
|||
names.Sort(); |
|||
|
|||
foreach (string s in names) |
|||
{ |
|||
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(s); |
|||
if(t != null) m_processorStack.inputSequence.frames.Add(new ProcessingFrame(t)); |
|||
} |
|||
|
|||
previewCanvas.currentFrameIndex = 0; |
|||
m_processorStack.InvalidateAll(); |
|||
UpdateViewport(); |
|||
m_processorStack.SyncFramesToAsset(m_CurrentAsset); |
|||
UpdateInputTexturesHash(); |
|||
} |
|||
} |
|||
|
|||
private void AddInputFrame(ReorderableList list) |
|||
{ |
|||
if (Selection.activeObject == null) |
|||
{ |
|||
Debug.LogWarning("Could not add frames with no selection : please select input frames to add in the project view and click the add button. Or drag & drop directly into the Image Sequencer Editor Window"); |
|||
return; |
|||
} |
|||
|
|||
string[] guids; |
|||
List<string> names = new List<string>(); |
|||
|
|||
if(VFXToolboxUtility.IsDirectorySelected()) |
|||
{ |
|||
names.AddRange(VFXToolboxUtility.GetAllTexturesInPath(AssetDatabase.GetAssetPath(Selection.activeObject))); |
|||
} |
|||
else |
|||
{ |
|||
guids = Selection.assetGUIDs; |
|||
foreach (string s in guids) |
|||
{ |
|||
string path = AssetDatabase.GUIDToAssetPath(s); |
|||
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(path); |
|||
if(t != null) |
|||
names.Add(path); |
|||
} |
|||
} |
|||
|
|||
if(names.Count > 0) |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Add Input Frames"); |
|||
AddInputFrame(list, names); |
|||
} |
|||
else |
|||
{ |
|||
Debug.LogWarning("No suitable textures found in selection, make sure you selected either a directory containing textures or texture themselves in project view."); |
|||
} |
|||
|
|||
} |
|||
|
|||
private void ReorderInputFrame(ReorderableList list) |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Reorder Input Frames"); |
|||
UpdateViewport(); |
|||
m_processorStack.SyncFramesToAsset(m_CurrentAsset); |
|||
UpdateInputTexturesHash(); |
|||
} |
|||
|
|||
private void RemoveInputFrame(ReorderableList list) |
|||
{ |
|||
int index = list.index; |
|||
previewCanvas.sequence.frames.RemoveAt(index); |
|||
|
|||
if (list.count == 0) |
|||
previewCanvas.currentFrame = null; |
|||
else |
|||
{ |
|||
if(previewCanvas.currentFrameIndex == index) |
|||
{ |
|||
previewCanvas.currentFrameIndex = Mathf.Max(0, index - 1); |
|||
previewCanvas.currentFrame = previewCanvas.sequence.frames[previewCanvas.currentFrameIndex]; |
|||
} |
|||
} |
|||
Undo.RecordObject(m_CurrentAsset, "Remove Input Frames"); |
|||
m_processorStack.SyncFramesToAsset(m_CurrentAsset); |
|||
UpdateViewport(); |
|||
UpdateInputTexturesHash(); |
|||
|
|||
if(m_processorStack.inputSequence.length > 0) |
|||
m_processorStack.InvalidateAll(); |
|||
} |
|||
|
|||
public void DrawInputFrameRListElement(Rect rect, int index, bool isActive, bool isFocused) |
|||
{ |
|||
int numbering = (int)Mathf.Floor(Mathf.Log10(m_InputFramesReorderableList.list.Count))+1; |
|||
GUI.Label(rect, new GUIContent("#" + (index+1).ToString("D"+numbering.ToString())+ " - " + m_InputFramesReorderableList.list[index].ToString())); |
|||
} |
|||
|
|||
public void SelectInputFrameRListElement(ReorderableList list) |
|||
{ |
|||
if (list.count > 0 && list.index != -1) |
|||
{ |
|||
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(m_CurrentAsset.inputFrameGUIDs[list.index])); |
|||
if (texture != null) |
|||
EditorGUIUtility.PingObject(texture); |
|||
m_PreviewCanvas.currentFrameIndex = list.index; |
|||
} |
|||
} |
|||
|
|||
private int GetInputTexturesHashCode() |
|||
{ |
|||
if(m_CurrentAsset != null) |
|||
{ |
|||
var builder = new System.Text.StringBuilder(); |
|||
foreach (string s in m_CurrentAsset.inputFrameGUIDs) |
|||
builder.Append(s); |
|||
return builder.ToString().GetHashCode(); |
|||
} |
|||
else |
|||
return 0; |
|||
} |
|||
|
|||
public void UpdateInputTexturesHash() |
|||
{ |
|||
m_InputFramesHashCode = GetInputTexturesHashCode(); |
|||
} |
|||
|
|||
#region menu actions
|
|||
|
|||
private void MenuClearInputFrames() |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Clear All Input Frames"); |
|||
// Remove frames and update hash
|
|||
m_processorStack.RemoveAllInputFrames(m_CurrentAsset); |
|||
m_processorStack.SyncFramesToAsset(m_CurrentAsset); |
|||
m_InputFramesHashCode = GetInputTexturesHashCode(); |
|||
// Update view
|
|||
sidePanelViewMode = SidePanelMode.InputFrames; |
|||
m_CurrentProcessor = null; |
|||
m_LockedPreviewProcessor = null; |
|||
m_CurrentAsset.editSettings.lockedProcessor = -1; |
|||
m_CurrentAsset.editSettings.selectedProcessor = -1; |
|||
m_PreviewCanvas.sequence = m_processorStack.inputSequence; |
|||
// Request an update
|
|||
Invalidate(); |
|||
RefreshCanvas(); |
|||
} |
|||
|
|||
|
|||
private void MenuSortInputFrames() |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Sort All Input Frames"); |
|||
// Sort frames and update hash
|
|||
m_processorStack.SortAllInputFrames(m_CurrentAsset); |
|||
m_InputFramesHashCode = GetInputTexturesHashCode(); |
|||
|
|||
LoadAsset(m_CurrentAsset); |
|||
|
|||
// Request an update
|
|||
Invalidate(); |
|||
RefreshCanvas(); |
|||
} |
|||
|
|||
|
|||
private void MenuReverseInputFrames() |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Reverse Input Frames Order"); |
|||
// Inverse frame order and update hash
|
|||
m_processorStack.ReverseAllInputFrames(m_CurrentAsset); |
|||
m_InputFramesHashCode = GetInputTexturesHashCode(); |
|||
|
|||
LoadAsset(m_CurrentAsset); |
|||
|
|||
// Request an update
|
|||
Invalidate(); |
|||
RefreshCanvas(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 028e010e3dbf80043b38a23c11c3836f |
|||
timeCreated: 1471965338 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine; |
|||
using UnityEditorInternal; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
{ |
|||
public partial class ImageSequencer : EditorWindow |
|||
{ |
|||
[System.NonSerialized] |
|||
ProcessorDataProvider m_ProcessorDataProvider; |
|||
|
|||
private void ShowAddProcessorMenu(ReorderableList list) |
|||
{ |
|||
if(m_ProcessorDataProvider == null) |
|||
{ |
|||
m_ProcessorDataProvider = new ProcessorDataProvider(m_processorStack, m_CurrentAsset); |
|||
} |
|||
|
|||
FilterPopupWindow.Show(Event.current.mousePosition, m_ProcessorDataProvider); |
|||
|
|||
} |
|||
|
|||
private void MenuSelectProcessor(ReorderableList list) |
|||
{ |
|||
if (m_CurrentAsset.editSettings.selectedProcessor == list.index) |
|||
return; |
|||
|
|||
if (list.count > 0 && list.index != -1) |
|||
{ |
|||
SetCurrentFrameProcessor(m_processorStack.processors[list.index], false); |
|||
} |
|||
else |
|||
SetCurrentFrameProcessor(null, false); |
|||
} |
|||
|
|||
private void ReorderProcessor(ReorderableList list) |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Reorder Processors"); |
|||
m_processorStack.ReorderProcessors(m_CurrentAsset); |
|||
m_processorStack.InvalidateAll(); |
|||
UpdateViewport(); |
|||
|
|||
// If locked processor is present, update its index
|
|||
if(m_LockedPreviewProcessor != null) |
|||
{ |
|||
m_CurrentAsset.editSettings.lockedProcessor = m_processorStack.processors.IndexOf(m_LockedPreviewProcessor); |
|||
EditorUtility.SetDirty(m_CurrentAsset); |
|||
} |
|||
|
|||
} |
|||
|
|||
private void MenuRemoveProcessor(ReorderableList list) |
|||
{ |
|||
int idx = list.index; |
|||
|
|||
Undo.RecordObject(m_CurrentAsset, "Remove Processor : " + m_processorStack.processors[idx].GetName()); |
|||
m_processorStack.RemoveProcessor(idx,m_CurrentAsset); |
|||
|
|||
// If was locked, unlock beforehand
|
|||
if (idx == m_CurrentAsset.editSettings.lockedProcessor) |
|||
SetCurrentFrameProcessor(null, true); |
|||
else if (idx < m_CurrentAsset.editSettings.lockedProcessor) |
|||
m_CurrentAsset.editSettings.lockedProcessor--; |
|||
|
|||
if(m_processorStack.processors.Count > 0) |
|||
{ |
|||
int newIdx = Mathf.Clamp(idx - 1, 0, m_processorStack.processors.Count - 1); |
|||
|
|||
SetCurrentFrameProcessor(m_processorStack.processors[newIdx], false); |
|||
list.index = newIdx; |
|||
} |
|||
else |
|||
{ |
|||
SetCurrentFrameProcessor(null, false); |
|||
list.index = -1; |
|||
} |
|||
|
|||
previewCanvas.currentFrameIndex = 0; |
|||
m_processorStack.InvalidateAll(); |
|||
UpdateViewport(); |
|||
} |
|||
|
|||
public void RefreshCanvas() |
|||
{ |
|||
if(m_CurrentProcessor != null) |
|||
previewCanvas.sequence = m_CurrentProcessor.OutputSequence; |
|||
else |
|||
previewCanvas.sequence = m_processorStack.inputSequence; |
|||
|
|||
previewCanvas.currentFrameIndex = Mathf.Clamp(previewCanvas.currentFrameIndex, 0, previewCanvas.sequence.length - 1); |
|||
|
|||
UpdateViewport(); |
|||
Invalidate(); |
|||
} |
|||
|
|||
public void SetCurrentFrameProcessor(FrameProcessor processor, bool wantLock) |
|||
{ |
|||
if(wantLock) |
|||
{ |
|||
m_LockedPreviewProcessor = processor; |
|||
if(processor != null) |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Lock Processor"); |
|||
m_CurrentProcessor = processor; |
|||
m_CurrentAsset.editSettings.lockedProcessor = m_processorStack.processors.IndexOf(processor); |
|||
} |
|||
else |
|||
{ |
|||
Undo.RecordObject(m_CurrentAsset, "Unlock Processor"); |
|||
if(m_ProcessorsReorderableList.index != -1) |
|||
m_CurrentProcessor = m_processorStack.processors[Mathf.Min(m_ProcessorsReorderableList.index, m_processorStack.processors.Count-1)]; |
|||
m_CurrentAsset.editSettings.lockedProcessor = -1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
bool needChange = (m_CurrentProcessor != processor); |
|||
|
|||
if(needChange) |
|||
Undo.RecordObject(m_CurrentAsset, "Select Processor"); |
|||
|
|||
if(m_LockedPreviewProcessor == null) |
|||
m_CurrentProcessor = processor; |
|||
else |
|||
m_CurrentProcessor = m_LockedPreviewProcessor; |
|||
} |
|||
|
|||
m_CurrentAsset.editSettings.selectedProcessor = m_processorStack.processors.IndexOf(processor); |
|||
RefreshCanvas(); |
|||
EditorUtility.SetDirty(m_CurrentAsset); |
|||
} |
|||
|
|||
public void DrawRListPreviewProcessorElement(Rect rect, int index, bool isActive, bool isFocused) |
|||
{ |
|||
Rect toggle_rect = new Rect(rect.x + 4, rect.y, 16, rect.height); |
|||
Rect label_rect = new Rect(rect.x + 24, rect.y, rect.width - 24, rect.height); |
|||
|
|||
using (new EditorGUI.DisabledScope(true)) |
|||
{ |
|||
GUI.Toggle(toggle_rect, m_processorStack.processors[index].Enabled, ""); |
|||
} |
|||
|
|||
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_processorStack.processors[index].ToString()), VFXToolboxStyles.RListLabel); |
|||
} |
|||
|
|||
|
|||
public void DrawRListProcessorElement(Rect rect, int index, bool isActive, bool isFocused) |
|||
{ |
|||
Rect toggle_rect = new Rect(rect.x + 4, rect.y, 16, rect.height); |
|||
Rect label_rect = new Rect(rect.x + 24, rect.y, rect.width - 24, rect.height); |
|||
Rect view_rect = new Rect(rect.x + rect.width - 37, rect.y, 19, 18); |
|||
Rect lock_rect = new Rect(rect.x + rect.width - 16, rect.y+1, 16, 14); |
|||
|
|||
bool enabled = GUI.Toggle(toggle_rect, m_processorStack.processors[index].Enabled,""); |
|||
if(enabled != m_processorStack.processors[index].Enabled) |
|||
{ |
|||
m_processorStack.processors[index].Enabled = enabled; |
|||
m_processorStack.processors[index].Invalidate(); |
|||
RefreshCanvas(); |
|||
} |
|||
|
|||
GUI.Label( label_rect, string.Format("#{0} - {1} ",index+1, m_processorStack.processors[index].ToString()), VFXToolboxStyles.RListLabel); |
|||
|
|||
if((m_LockedPreviewProcessor == null && isActive) || m_processorStack.processors.IndexOf(m_LockedPreviewProcessor) == index) |
|||
GUI.DrawTexture(view_rect, (Texture2D)EditorGUIUtility.LoadRequired("ViewToolOrbit On.png")); |
|||
|
|||
bool locked = (m_LockedPreviewProcessor != null) && index == m_processorStack.processors.IndexOf(m_LockedPreviewProcessor); |
|||
|
|||
if(isActive || locked) |
|||
{ |
|||
bool b = GUI.Toggle(lock_rect, locked,"", styles.LockToggle); |
|||
if(b != locked) |
|||
{ |
|||
if(b) |
|||
SetCurrentFrameProcessor(m_processorStack.processors[index],true); |
|||
else |
|||
SetCurrentFrameProcessor(null, true); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue