浏览代码

Added Support for Texture2D Array Import (2020.2+) (#20)

* Added Support for Texture2DArrays

* Added Documentation
/main
GitHub 4 年前
当前提交
77d47bb7
共有 8 个文件被更改,包括 272 次插入177 次删除
  1. 4
      CHANGELOG.md
  2. 27
      Documentation~/ImageSequencer.md
  3. 148
      Documentation~/images/ImageSequencer-Export.png
  4. 185
      Editor/ImageSequencer/ImageSequenceAssetEditor.cs
  5. 58
      Editor/ImageSequencer/ImageSequencer.Export.cs
  6. 13
      Editor/ImageSequencer/ImageSequencer.GUI.cs
  7. 10
      Editor/ImageSequencer/Serialization/ImageSequence.cs
  8. 4
      package.json

4
CHANGELOG.md


The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.1.0-preview] - 2020 - XX - XX
* Added support for Texture2D Arrays
## [2.0.0-preview] - 2020-03-23
* Refactored Processor API

27
Documentation~/ImageSequencer.md


#### Exporting Images
![](images/ImageSequencer-Export.png)
> For more information, see the Export Workflow section of this document.
>
For more information, see the Export Workflow section of this document.
## Image Sequence Inspector

### Exporting the Image Sequence
After working on an image sequence, and adding processors, you will end up in a state where you want to generate a texture out of the result of this image sequence.
The Export Tab enables option in order to generate an image file and import it into the project. Once exported once, the texture can be updated by clicking the Update Exported Assets button, or the Update button in the Viewport.
![](Images/ImageSequencer-Export.png)
**Export Options:**
* Export Format : lets you choose between Targa, PNG and EXR file formats. EXR file format will output an HDR texture.
* File Name (read only) : lets you review the file name of the texture.
* sRGB : will perform a sRGB conversion of the color data while writing the file (does not apply to EXR)
* Export Alpha : will export the alpha channel as part of the main texture.
* Separate Alpha : will export the alpha channel as separate, grayscale texture
**Texture Import Options:**
* Output Shape : Whether to export as simple Texture 2D or Texture 2D Array. In the case of Texture 2D Array, the texture importer will be set from given rows and columns defined in the image sequence. In case of incorrect size, a warning will be displayed.
* Import As : Whether to import the texture as Color, Sprite or Normal map
* sRGB: will set the sRGB import flag accordingly.
* Compress: whether the imported texture needs to be compressed.
* Generate MipMaps: whether the imported texture needs to have mip-maps generated.
* Wrap Mode: the wrap mode applied to the imported texture
* Filter Mode: the filter mode applied to the imported texture
## Built-In Processors

148
Documentation~/images/ImageSequencer-Export.png

之前 之后
宽度: 351  |  高度: 405  |  大小: 23 KiB

185
Editor/ImageSequencer/ImageSequenceAssetEditor.cs


[CustomEditor(typeof(ImageSequence))]
internal class ImageSequenceAssetEditor : Editor
{
ImageSequence sequence;
private bool m_PreviewInput = false;
private bool m_PreviewOutput = false;
private bool m_RequireConstantRepaint = false;

return m_RequireConstantRepaint;
}
private void OnEnable()
{
sequence = serializedObject.targetObject as ImageSequence;
InitializePreview();
}
protected override void OnHeaderGUI()

m_PreviewInput = GUILayout.Toggle(m_PreviewInput, VFXToolboxGUIUtility.Get("Preview"), EditorStyles.miniButton);
}
if(inputFrameCount > 0 && m_PreviewInput)
if (inputFrameCount > 0 && m_PreviewInput)
if(inputFrameCount > 1)
if (inputFrameCount > 1)
index = (int)Mathf.Floor((time * 30) % inputFrameCount);
index = (int)Mathf.Floor((time * 30) % inputFrameCount);
}
else
{

}
}
GUILayout.Space(24);
VFXToolboxGUIUtility.ToggleableHeader(true, false, "Processors");
{

EditorGUI.indentLevel++;
for(int i = 0; i < processorsCount; i++)
for (int i = 0; i < processorsCount; i++)
EditorGUILayout.LabelField("#"+i+" - " + item.Settings.label + (item.Enabled?"":" (Disabled)"));
EditorGUILayout.LabelField("#" + i + " - " + item.Settings.label + (item.Enabled ? "" : " (Disabled)"));
GUILayout.Space(24);
VFXToolboxGUIUtility.ToggleableHeader(true, false, "Export Settings");

EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Export Format"), mode);
EditorGUI.EndDisabledGroup();
EditorGUILayout.TextField("Exporting to ", fileName);
EditorGUILayout.TextField("Export Path", fileName);
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);
}
EditorGUILayout.HelpBox("This asset has not yet been exported. Please open editor and export it to generate a sequence.",MessageType.None);
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)
private void DrawAnimatedPreviewLayout(Texture texture, float progress)
float width = EditorGUIUtility.currentViewWidth-32;
float width = EditorGUIUtility.currentViewWidth - 32;
if(ratio >= 1)
if (ratio >= 1)
texture_rect = GUILayoutUtility.GetRect(height / ratio, height);
else
texture_rect = GUILayoutUtility.GetRect(width, width * ratio);

}
}
#region PREVIEW
public int previewFrame = 0;
Texture previewTexture;
public Material arrayPreviewMaterial;
static readonly int s_ShaderColorMask = Shader.PropertyToID("_ColorMask");
static readonly int s_ShaderSliceIndex = Shader.PropertyToID("_SliceIndex");
static readonly int s_ShaderMip = Shader.PropertyToID("_Mip");
static readonly int s_ShaderToSrgb = Shader.PropertyToID("_ToSRGB");
static readonly int s_ShaderIsNormalMap = Shader.PropertyToID("_IsNormalMap");
void InitializePreview()
{
if (HasPreviewGUI())
previewTexture = AssetDatabase.LoadAssetAtPath<Texture>(sequence.exportSettings.fileName);
arrayPreviewMaterial = (Material)EditorGUIUtility.LoadRequired("Previews/Preview2DTextureArrayMaterial.mat");
arrayPreviewMaterial.SetInt(s_ShaderColorMask, 15);
arrayPreviewMaterial.SetInt(s_ShaderMip, 0);
arrayPreviewMaterial.SetInt(s_ShaderToSrgb, QualitySettings.activeColorSpace == ColorSpace.Linear ? 1 : 0);
arrayPreviewMaterial.SetInt(s_ShaderIsNormalMap, 0);
}
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (previewTexture == null)
InitializePreview();
base.OnPreviewGUI(r, background);
if (previewTexture is Texture2D)
{
EditorGUI.DrawTextureTransparent(r, previewTexture, ScaleMode.ScaleToFit, (float)previewTexture.width / previewTexture.height);
}
else if (previewTexture is Texture2DArray)
{
EditorGUI.DrawPreviewTexture(r, previewTexture, arrayPreviewMaterial, ScaleMode.ScaleToFit, (float)previewTexture.width/previewTexture.height, 0);
}
}
public override void OnPreviewSettings()
{
if (previewTexture == null)
InitializePreview();
if (previewTexture is Texture2DArray)
{
Texture2DArray array = previewTexture as Texture2DArray;
GUILayout.Label("Frame");
previewFrame = EditorGUILayout.IntSlider(previewFrame, 0, array.depth-1);
arrayPreviewMaterial.SetInt(s_ShaderSliceIndex, previewFrame);
}
}
public override bool HasPreviewGUI()
{
if (serializedObject.targetObjects.Length > 1) // No Multiple Preview
return false;
ImageSequence.ExportSettings exportSettings = sequence.exportSettings;
if (exportSettings.fileName == null // No Preview if not exported
|| !exportSettings.fileName.StartsWith("Assets/") // No External Preview
|| exportSettings.fileName.Contains("#")) // No Multiple Frame Preview
return false;
return true;
}
#endregion
}
}

58
Editor/ImageSequencer/ImageSequencer.Export.cs


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

Graphics.Blit((Texture2D)frame.texture, temp);
Graphics.Blit((Texture2D)frame.texture, temp);
inputs = ReadBack(temp);
}
else // frame.texture is RenderTexture

// Dump data
byte[] bytes;
switch(m_CurrentAsset.exportSettings.exportMode)
switch (m_CurrentAsset.exportSettings.exportMode)
{
case ImageSequence.ExportMode.EXR:
#if UNITY_5_6_OR_NEWER

bytes = texture.EncodeToEXR();
}
#else
// Old Exporter
{
bytes = MiniEXR.MiniEXR.MiniEXRWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs, true);
}
// Old Exporter
{
bytes = MiniEXR.MiniEXR.MiniEXRWrite((ushort)frame.texture.width, (ushort)frame.texture.height, settings.exportAlpha, inputs, true);
}
#endif
break;
case ImageSequence.ExportMode.Targa:

AssetDatabase.Refresh();
// Process Import if saved inside project
if(bIsInsideProject)
if (bIsInsideProject)
switch(m_CurrentAsset.exportSettings.dataContents)
switch (m_CurrentAsset.exportSettings.dataContents)
{
case ImageSequence.DataContents.Color:
importer.textureType = TextureImporterType.Default;

importer.convertToNormalmap = false;
break;
case ImageSequence.DataContents.NormalMapFromGrayscale:
importer.textureType = TextureImporterType.NormalMap;
importer.convertToNormalmap = true;
importer.convertToNormalmap = false;
break;
case ImageSequence.DataContents.NormalMapFromGrayscale:
importer.textureType = TextureImporterType.NormalMap;
importer.convertToNormalmap = true;
importer.spritesheet = GetSpriteMetaData(frame, m_ProcessingNodeStack.outputSequence.numU, m_ProcessingNodeStack.outputSequence.numV );
importer.spritesheet = GetSpriteMetaData(frame, m_ProcessingNodeStack.outputSequence.numU, m_ProcessingNodeStack.outputSequence.numV);
TextureImporterSettings importerSettings = new TextureImporterSettings();
importer.ReadTextureSettings(importerSettings);
if (m_CurrentAsset.exportSettings.outputShape == ImageSequence.OutputMode.Texture2DArray)
{
importerSettings.textureShape = TextureImporterShape.Texture2DArray;
importerSettings.flipbookColumns = m_ProcessingNodeStack.outputSequence.numU;
importerSettings.flipbookRows = m_ProcessingNodeStack.outputSequence.numV;
}
else if (m_CurrentAsset.exportSettings.outputShape == ImageSequence.OutputMode.Texture2D)
{
importerSettings.textureShape = TextureImporterShape.Texture2D;
}
importer.SetTextureSettings(importerSettings);
switch(m_CurrentAsset.exportSettings.exportMode)
switch (m_CurrentAsset.exportSettings.exportMode)
{
case ImageSequence.ExportMode.Targa:
importer.sRGBTexture = m_CurrentAsset.exportSettings.sRGB;

{
string alphaFilename = fileName.Substring(0, fileName.Length - 4) + "_alpha.tga";
// build alpha
for(int k = 0; k < inputs.Length; k++)
for (int k = 0; k < inputs.Length; k++)
MiniTGA.MiniTGAWrite(alphaFilename,(ushort)frame.texture.width, (ushort)frame.texture.height, false, inputs);
MiniTGA.MiniTGAWrite(alphaFilename, (ushort)frame.texture.width, (ushort)frame.texture.height, false, inputs);
if(bIsInsideProject)
if (bIsInsideProject)
{
TextureImporter alphaImporter = (TextureImporter)TextureImporter.GetAtPath(alphaFilename);

if(fileFound)
{
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(fileName);
Texture texture = AssetDatabase.LoadAssetAtPath<Texture>(fileName);
if (texture != null) EditorGUIUtility.PingObject(texture);
}
else

13
Editor/ImageSequencer/ImageSequencer.GUI.cs


using (new VFXToolboxGUIUtility.HeaderSectionScope("Texture Import Options"))
{
m_CurrentAsset.exportSettings.outputShape = (ImageSequence.OutputMode)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Output Shape|Selects whether export as simple 2D Texture, flipbook as 2D texture, or full sequence as 2D texture"), m_CurrentAsset.exportSettings.outputShape);
if(m_CurrentAsset.exportSettings.outputShape == ImageSequence.OutputMode.Texture2DArray)
{
ProcessingNode n = m_ProcessingNodeStack.nodes[m_ProcessingNodeStack.nodes.Count - 1];
if(((float)n.OutputWidth / n.NumU)%1.0f > 0.0f || ((float)n.OutputHeight / n.NumV) % 1.0f > 0.0f)
{
EditorGUILayout.HelpBox("Row and Column Counts are not exact multiples of the resolution, some padding will occur in the export", MessageType.Warning);
} else if ((!Mathf.IsPowerOfTwo(n.OutputWidth / n.NumU) || !Mathf.IsPowerOfTwo(n.OutputHeight / n.NumV)) && m_CurrentAsset.exportSettings.compress && m_CurrentAsset.exportSettings.generateMipMaps)
{
EditorGUILayout.HelpBox("Texture 2D Arrays with mip maps cannot be compressed if U and V dimensions are not power of two", MessageType.Warning);
}
}
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)
{

10
Editor/ImageSequencer/Serialization/ImageSequence.cs


{
public string fileName;
public ushort frameCount;
public OutputMode outputShape;
public ExportMode exportMode;
public bool exportAlpha;
public bool exportSeparateAlpha;

public FilterMode filterMode;
public DataContents dataContents;
}
public static ExportSettings defaultExportSettings
{

Targa = 0,
EXR = 1,
PNG = 2
}
[System.Serializable]
public enum OutputMode
{
Texture2D = 0,
Texture2DArray = 1
}
[System.Serializable]

4
package.json


{
"name": "com.unity.vfx-toolbox",
"displayName": "VFX Toolbox",
"version": "2.0.0-preview",
"version": "2.1.0-preview",
"description": "Additional Tools for VFX Artists.\n\n* Image Sequencer\n* Houdini Exporters for Visual Effect Graph",
"keywords": [
"vfx",

],
"type": "tool",
"hideInEditor": false,
"unity": "2019.3"
"unity": "2020.2"
}
正在加载...
取消
保存