浏览代码
Merge pull request #11 from Unity-Technologies/refactor/simplify-processors
Merge pull request #11 from Unity-Technologies/refactor/simplify-processors
Refactor/Simplify Processor API + Class Accessibility + Package conformance/main
GitHub
5 年前
当前提交
b0ccf1ba
共有 161 个文件被更改,包括 8021 次插入 和 362 次删除
-
4LICENSE.md
-
97Editor/Common/Canvas/VFXToolboxCanvas.cs
-
2Editor/Common/External/CurveEditor.cs
-
2Editor/Common/Utility/VFXToolboxUtility.cs
-
2Editor/Common/Utility/VFXToolboxStyles.cs
-
2Editor/Common/Utility/VFXToolboxGUIUtility.cs
-
2Editor/Common/Utility/Splitter.cs
-
2Editor/Common/Utility/FloatSliderPropertyDrawer.cs
-
4Editor/Common/Utility/FilterPopupWindow.cs
-
2Editor/Common/Utility/CurveToTextureUtility.cs
-
2Editor/Common/Utility/CurveDrawer.cs
-
2Editor/Common/PropertyAttributes/FloatSliderAttribute.cs
-
175Editor/ImageSequencer/ProcessingNode.cs
-
12Editor/ImageSequencer/Serialization/ProcessorInfo.cs
-
9Editor/ImageSequencer/Serialization/ImageSequence.cs
-
36Editor/ImageSequencer/ProcessingFrameSequence.cs
-
26Editor/ImageSequencer/ProcessingFrame.cs
-
80Editor/ImageSequencer/ImageSequencerCanvas.cs
-
101Editor/ImageSequencer/ImageSequencer.cs
-
6Editor/ImageSequencer/ImageSequencer.Styles.cs
-
71Editor/ImageSequencer/ImageSequencer.Processors.cs
-
28Editor/ImageSequencer/ImageSequencer.InputFrames.cs
-
55Editor/ImageSequencer/ImageSequencer.GUI.cs
-
23Editor/ImageSequencer/ImageSequencer.Export.cs
-
4Editor/ImageSequencer/ImageSequenceAssetFactory.cs
-
4Editor/ImageSequencer/ImageSequenceAssetEditor.cs
-
25Editor/ImageSequencer/FilterPopup/ProcessorDataProvider.cs
-
6Editor/ImageSequencer/Exporters/MiniTGA.cs
-
8Editor/ImageSequencer/Exporters/MiniEXR.cs
-
22Editor/ImageSequencer/Attributes/ProcessorAttribute.cs
-
9CHANGELOG.md
-
7CHANGELOG.md.meta
-
8Editor/Common.meta
-
4Documentation~/TableOfContents.md
-
12Documentation~/index.md
-
15Documentation~/DCCTools.md
-
22Documentation~/images/ImageSequenceAssets.png
-
571Documentation~/images/ImageSequenceInspector.png
-
86Documentation~/images/ImageSequencer-AddProcessorMenu.png
-
21Documentation~/images/ImageSequencer-AlphaFromRGB.png
-
49Documentation~/images/ImageSequencer-AssembleFlipbook.png
-
14Documentation~/images/ImageSequencer-BreakFlipbook.png
-
10Documentation~/images/ImageSequencer-ClearProcessors.png
-
76Documentation~/images/ImageSequencer-ColorCorrection.png
-
120Documentation~/images/ImageSequencer-Crop.png
-
86Documentation~/images/ImageSequencer-CustomMaterial.png
-
19Documentation~/images/ImageSequencer-Decimate.png
-
58Documentation~/images/ImageSequencer-Export.png
-
56Documentation~/images/ImageSequencer-Fade.png
-
25Documentation~/images/ImageSequencer-FixBorders.png
-
49Documentation~/images/ImageSequencer-FrameProcessorList.png
-
9Documentation~/images/ImageSequencer-InheritProcessors.png
-
42Documentation~/images/ImageSequencer-InputFrames.png
-
34Documentation~/images/ImageSequencer-Looping.png
-
66Documentation~/images/ImageSequencer-OptionsPopup.png
-
6Documentation~/images/ImageSequencer-PremultiplyAlpha.png
-
19Documentation~/images/ImageSequencer-ProcessorPreview.png
-
18Documentation~/images/ImageSequencer-ProcessorPreviewLock.png
-
131Documentation~/images/ImageSequencer-Processors.png
-
76Documentation~/images/ImageSequencer-RemapColor.png
-
89Documentation~/images/ImageSequencer-RemoveBackground.png
-
53Documentation~/images/ImageSequencer-Resize.png
-
94Documentation~/images/ImageSequencer-Retime.png
-
6Documentation~/images/ImageSequencer-Rotate.png
-
10Documentation~/images/ImageSequencer-Tabs.png
-
18Documentation~/images/ImageSequencer-Toolbar.png
-
10Documentation~/images/ImageSequencer-UpdateButton.png
-
940Documentation~/images/ImageSequencer.png
-
11Documentation~/images/ImageSequencerNoAsset.png
-
649Documentation~/images/ImageSequencerWindow.png
-
317Documentation~/images/ImageSequencerWorkflow.png
-
854Documentation~/ImageSequencer.md
-
157Editor/ImageSequencer/ProcessingNodeStack.Serialization.cs
-
149Editor/ImageSequencer/ProcessingNodeStack.cs
-
203Editor/ImageSequencer/Serialization/ProcessorBase.cs
-
48Editor/ImageSequencer/Serialization/Processors/AlphaFromRGBProcessor.cs
-
319Editor/ImageSequencer/Serialization/Processors/AssembleProcessor.cs
-
101Editor/ImageSequencer/Serialization/Processors/BreakFlipbookProcessor.cs
-
113Editor/ImageSequencer/Serialization/Processors/ColorCorrectionProcessor.cs
-
230Editor/ImageSequencer/Serialization/Processors/CropProcessor.cs
-
84Editor/ImageSequencer/Serialization/Processors/CustomMaterialProcessor.cs
-
55Editor/ImageSequencer/Serialization/Processors/DecimateProcessor.cs
-
101Editor/ImageSequencer/Serialization/Processors/FadeProcessor.cs
-
137Editor/ImageSequencer/Serialization/Processors/FixBordersProcessor.cs
-
301Editor/ImageSequencer/Serialization/Processors/LoopingProcessor.cs
-
50Editor/ImageSequencer/Serialization/Processors/PremultiplyAlphaProcessor.cs
-
85Editor/ImageSequencer/Serialization/Processors/RemapColorProcessor.cs
-
80Editor/ImageSequencer/Serialization/Processors/RemoveBackgroundProcessor.cs
-
144Editor/ImageSequencer/Serialization/Processors/ResizeProcessor.cs
-
140Editor/ImageSequencer/Serialization/Processors/RetimeProcessor.cs
-
77Editor/ImageSequencer/Serialization/Processors/RotateProcessor.cs
-
4Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png
-
103Editor/ImageSequencer/EditorResources/ImageSequencer-Icon.png.meta
-
7Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png
-
103Editor/ImageSequencer/EditorResources/d_ImageSequencer-Icon.png.meta
-
9Editor/Examples.meta
|
|||
Copyright © 2018 Unity Technologies ApS |
|||
com.unity.vfx-toolbox copyright © 2020 Unity Technologies ApS |
|||
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). |
|||
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). |
|||
|
|||
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. |
|
|||
using UnityEngine; |
|||
using System; |
|||
|
|||
namespace UnityEditor.VFXToolbox.ImageSequencer |
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
/// <summary>
|
|||
/// Attribute for Class derived from ProcessorBase.
|
|||
/// Determines the ImageSequencer menu name and category for the processor when adding new processors to the asset.
|
|||
/// </summary>
|
|||
/// <summary>
|
|||
/// Menu Category where the Processor will be stored into
|
|||
/// </summary>
|
|||
|
|||
/// <summary>
|
|||
/// Processor name used to display in the menu
|
|||
/// </summary>
|
|||
public readonly Type processorType; |
|||
public ProcessorAttribute(string category, string name, Type processorType) |
|||
/// <summary>
|
|||
/// Defines a Processor Entry in the ImageSequencer add Processor Menu.
|
|||
/// </summary>
|
|||
/// <param name="category">Menu Category where the Processor will be stored into</param>
|
|||
/// <param name="name">Processor name used to display in the menu</param>
|
|||
public ProcessorAttribute(string category, string name) |
|||
this.processorType = processorType; |
|||
} |
|||
} |
|||
} |
|
|||
# Changelog |
|||
All notable changes to this package are documented in this file. |
|||
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) |
|||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). |
|||
|
|||
## [1.0.0-preview] - 2020-01-07 |
|||
|
|||
* Initial Version |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ad57f86e7dfbfa4a95870ba31a6686a |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 3dfcf7e3db1c87140891088eaecec382 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
* [VFX Toolbox](Index.md) |
|||
* [Image Sequencer](ImageSequencer.md) |
|||
* [DCC Tools](DCCTools.md) |
|||
|
|
|||
# VFX Toolbox |
|||
|
|||
VFX Toolbox is a set of unity tools, and DCC scripts that enables Visual Effect Artists to improve their workflow and interaction with Unity. |
|||
|
|||
<u>Unity Tools:</u> |
|||
|
|||
* [Image Sequencer](ImageSequencer.md) : A Workflow tool that enables making optimized flipbook and texture sheets from sequences of images. |
|||
|
|||
<u>DCC Tools :</u> |
|||
|
|||
* [Unity VFX Tools for Houdini](DCCTools.md) : A set of Houdini nodes that enable exporting data to Unity (for use with Visual Effect Graph). |
|||
|
|
|||
# DCC Tools |
|||
|
|||
VFX Toolbox comes bundled with a set of DCC Tools that help export data for unity. |
|||
|
|||
## Houdini DCC Tools for Visual Effect Graph |
|||
|
|||
Houdini DCC Tools are located inside the `DCC-Tools~/Houdini` folder of the VFX Toolbox Package. This folder is not visible in editor but it can be browsed by clicking the Packages/VFX Toolbox Folder in the Project View, then selecting Show in Explorer from the Context Menu. |
|||
|
|||
The Houdini Folder contains the following files: |
|||
|
|||
* **Examples** (Folder) : Contains a set of houdini example files (.hipnc) |
|||
* **Unity_VFX_Tools.hda** : The digital Asset file containing the Unity VFX Toolbox Digital assets for Houdini. |
|||
|
|||
<u>Compatibility:</u> Houdini 16.5 and newer. |
|||
|
940
Documentation~/images/ImageSequencer.png
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
649
Documentation~/images/ImageSequencerWindow.png
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
|
|||
|
|||
# Image Sequencer |
|||
|
|||
![Screenshot of Image Sequencer](images/ImageSequencer.png) |
|||
|
|||
## Introduction |
|||
The VFX Image Sequencer is a tool which provide Visual Effect Artists with the means to process sequences of images, and generate texture assets such as flipbooks texture sheets. |
|||
|
|||
![Image Sequencer Workflow](Images/ImageSequencerWorkflow.png) |
|||
|
|||
To edit sequences of images, the tool relies on **Image Sequence assets**. These template assets contain references to project imported textures, a set of processing settings, and an export configuration. Every asset can be considered as a “Project File” in order to generate one output texture file or a sequence of these. |
|||
|
|||
## Quick Start Guide |
|||
|
|||
Image Sequence Assets can be created from the Project Window Create Menu under the Category VFX Toolbox. Each asset contains configuration to read a sequence of images and output one or a sequence of textures. |
|||
|
|||
These assets can be considered as a “Project Template” for producing flipbook texture sheets, so you can retain the settings and iterate on them for future updates. |
|||
|
|||
### Creating Image Sequences |
|||
|
|||
Image Sequences are assets that can be created using the main Menu : **Assets/Create/Visual Effects/Image Sequence**. Every Image Sequence asset contains a "Project Template" to create a Texture Sheet. |
|||
|
|||
### Opening Image Sequences |
|||
|
|||
The Image sequencer window is the main editor of Image Sequence assets. You can open this window : |
|||
|
|||
* By clicking the **Window/Visual Effects/Image Sequencer** menu item |
|||
* By clicking the **Edit Sequence** button |
|||
* By double-clicking an Image Sequence asset in the Project Window. |
|||
|
|||
While the Image Sequencer window is open, selecting another image sequence in the project window will load it in the Image Sequencer window. |
|||
|
|||
### Editing Image Sequences |
|||
|
|||
Once created, the Image Sequence can be edited in the Image Sequencer using the following actions: |
|||
|
|||
1. Adding Input Frames |
|||
2. Add and Edit Processors to alter the source image sequence. |
|||
3. Export the Output of the Processors as one or many Images |
|||
|
|||
In order to switch between these actions, you can use the 3 tabs located at the top of the left inspector pane: |
|||
|
|||
![](images/ImageSequencer-Tabs.png) |
|||
|
|||
If no images are not imported, the Processors and Export tabs are disabled and cannot be clicked. |
|||
|
|||
#### Adding Input Frames |
|||
|
|||
![](images/ImageSequencer-InputFrames.png) |
|||
|
|||
You can import images in the sequence by clicking the **Input Frames** tab Button to enter Input Frames editing mode then by dragging them directly from the Project Window to the Image Sequencer window, or by dragging a folder that contains images from the Project Window to the Image Sequencer Window |
|||
|
|||
> For more information, see the Input Frames Workflow section of this document. |
|||
> |
|||
|
|||
#### Adding and Editing Processors |
|||
|
|||
![](images/ImageSequencer-Processors.png) |
|||
|
|||
After adding Input Frames, by clicking the Processors Tab button, you can enter the Processors editing Mode. In this view you can add processors in the reorderable list, edit them and iterate to generate texture sheets. |
|||
|
|||
In this mode you will be able to perform operations on textures such as Assembling and Disassembling Texture Sheets, Adjusting Timing of a Sequence, Make an image Sequence looping, Crop images to save empty space, Fix borders, adjust Colors, and many more actions. |
|||
|
|||
> For more information, see the Processors Workflow section of this document. |
|||
> |
|||
|
|||
#### Exporting Images |
|||
|
|||
![](images/ImageSequencer-Export.png) |
|||
|
|||
After Adding Processors, when you have a result that suits you, you can click the third Tab Button "Export" to enter Export Mode. In this mode you can configure the texture generation options and also the texture import options. |
|||
|
|||
In this view you will be able to set up your output textures either as color, masks or normal maps and ensure they are exported correctly. |
|||
|
|||
> For more information, see the Export Workflow section of this document. |
|||
> |
|||
|
|||
## Image Sequence Inspector |
|||
|
|||
![ImageSequence Inspector](images/ImageSequenceInspector.png) |
|||
|
|||
Upon selecting an Image Sequence Asset, the inspector displays an overview of this asset, with its input frames, processors overview, and optionally the Exported file (if any). This is only a read-only overview of the processing information of this sequence. |
|||
|
|||
- To edit this sequence, click the **Edit Sequence** button, this will open the Image Sequencer Editor Window. |
|||
- You can preview input sequence of images by clicking the preview button. |
|||
|
|||
## Image Sequencer Window |
|||
|
|||
![ImageSequencer Window](Images/ImageSequencerWindow.png) |
|||
|
|||
The Image Sequence editor window comes with two zones: |
|||
|
|||
* The **Toolbar** at the top provides controls to access the currently edited Image sequence, and display controls for the Canvas. |
|||
* The **Inspector** (left pane) displays contextual controls for adding input frames, configure processing, and export to other textures. |
|||
* The **Canvas** (right pane) offers preview of the processed sequence at different stages (input sequence, processors, exported texture). It contains play controls at the bottom, view options in the top toolbar, and handles panning and zooming using mouse. |
|||
|
|||
### Inspector |
|||
|
|||
The inspector lies in the left part of the window and is the part of the window where you will make changes to the Image Sequence. At the top of the inspector lies a 3-Button tab in order to switch between the editing modes : Input Frames, Processors and Export. |
|||
|
|||
![](images/ImageSequencer-Tabs.png) |
|||
|
|||
### Canvas |
|||
|
|||
The canvas is in the right side of the window and is used to preview the changes made in the Image Sequencer Inspector. It displays contextually either the Input Frames, the output of a processor, or the exported texture. |
|||
|
|||
Navigation in the canvas is made by using the mouse or trackpad. |
|||
|
|||
Some keyboard shortcuts enable access to same features accessible with the mouse: |
|||
|
|||
* Alt+Left Mouse Drag : Pan the viewport |
|||
|
|||
* Middle Mouse Drag : Pan the viewport |
|||
|
|||
* Alt+Right Mouse Drag : Zoom the viewport |
|||
|
|||
* Mouse Wheel : Zoom the viewport |
|||
|
|||
Some keyboard shortcuts enable focusing on the image: |
|||
|
|||
* F focuses the image, fitting the zoom to fill the viewport |
|||
* Shift + F focuses the image, with a 100% zoom |
|||
|
|||
(Note that these controls are available in the options popup menu) |
|||
|
|||
### Sequence Playback Controls |
|||
|
|||
Playback controls are visible when your sequence contains 2 or more images. They are available as a bottom panel. |
|||
![img](https://lh6.googleusercontent.com/VHDcK1K4X-3kyH9cGkwlJFPdK-fS3mxZVco5IsmEmwsSKp4HGWsJzpsgeduzOHTacxFTGzvASWeKeXlWZxGl5S_pGTxpC7E1rFnxAoTJQ4yuNEPX8VjjiuW4hH7MNakvwC1mKUR9) |
|||
|
|||
The sequence playback panel contains a timeline track showing the current progress of the timeline. |
|||
|
|||
Every frame is displayed in the track as a colored cell: |
|||
|
|||
* Bright blue cell displays the currently selected (displayed) frame in the canvas. |
|||
|
|||
* Blue darker cells tells that these frames have already been rendered. |
|||
|
|||
* Gray cells mean frames have not already been rendered |
|||
|
|||
> (You can enable automatic rendering by activating the AutoCook Option in the options menu) |
|||
|
|||
Here is an overview of the Toolbar located under the sequence trackbar |
|||
|
|||
* **Playback Buttons** |
|||
* First Frame |
|||
* Previous Frame |
|||
* Play/Pause |
|||
* Next Frame |
|||
* Last Frame |
|||
* **Image & Sequence information** |
|||
* Frame N on Total Count |
|||
* TCR time (based on frame rate) |
|||
* **Speed and Framerate Control** |
|||
* Number of frames per second |
|||
* Popup to set a given play rate |
|||
|
|||
### Viewport Toolbar |
|||
|
|||
The viewport toolbar contains controls for toggle display of various options in the viewport. |
|||
![Toolbar](images/ImageSequencer-Toolbar.png) |
|||
|
|||
The toolbar contains the following elements: |
|||
|
|||
* **Options Popup Menu**: Opens the Option popup menu to configure the Canvas display options. |
|||
* **RGBA toggles** : Use these buttons to filter the Red, Green, Blue or Alpha channel display. |
|||
* **MipMap Selector** : Using this slider, you can preview the different mip-maps of the currently displayed image. |
|||
* **Background brightness**: Using this slider, you can adjust the background checkerboard brightness. |
|||
|
|||
#### Options popup menu |
|||
![img](images/ImageSequencer-OptionsPopup.png) |
|||
|
|||
The Options popup menu is accessed via the toolbar button on the right.It contains toggles & buttons for visibility, and commands for the view. |
|||
|
|||
* Viewport Options: |
|||
* Grid Outline (Toggle) : Toggles Outline around Sub Image Cells and around texture. |
|||
* Frame Processor Overlays (Toggle) : Toggle Processor Overlay in the canvas to display visually its changes. |
|||
* Texture Filtering (Toggle) : Toggles Texture Filtering in the Canvas |
|||
|
|||
### Update Button |
|||
|
|||
![](images/ImageSequencer-UpdateButton.png) |
|||
|
|||
## Workflow |
|||
|
|||
This section details how to use the Image Sequencer and create output Texture Sheets. The workflow for a new Image Sequence is as follows: |
|||
|
|||
1. Import and Configure Source Images |
|||
2. Create Image Sequence Asset and Open It |
|||
3. Adding input frames into the Image Sequence |
|||
4. Add Processors and Build your Texture Sheets |
|||
5. Export the output sequence as one or many textures. |
|||
|
|||
After the first iteration on an Image sequence Asset, you can open this sequence later, make adjustments, then update your output image in one click using the **Update Button** in the canvas |
|||
|
|||
### Importing and Configuring source images |
|||
|
|||
Your source images will be imported inside the project for full efficiency, but they do not have to be used in your project. These textures will behave as intermediate sources for generating assets, so It could be a good Idea to use asset labels to filter them in order to ensure they are isolated and unused. |
|||
|
|||
#### Manual settings |
|||
|
|||
Texture configuration for the source images answers the “lossless quality” requirement : at this stage of workflow, you do not want to degrade quality, so importing your textures at maximum quality is required here. |
|||
In order to stay at lossless quality, make sure you have the following: |
|||
|
|||
- Keep your textures uncompressed : |
|||
- Use Compression : None |
|||
- Ensure your texture will not be resized if too large: |
|||
- Advanced/Non-Power of Two : None |
|||
- Max Size : 8192 |
|||
- Ensure your alpha channel will not be modified by the importer: |
|||
- Disable the Alpha Is Transparency (if available) |
|||
- Ensure the sRGB flag matches your image contents: |
|||
- sRGB on for any 8-bit per component Color Image |
|||
- sRGB off for: |
|||
- Any HDR Image (exr) |
|||
- Flow Maps, Normal Maps, Bump Maps and/or masks |
|||
|
|||
#### Asset Postprocessor |
|||
|
|||
It is advised to use an [AssetPostprocessor](https://docs.unity3d.com/ScriptReference/AssetPostprocessor.html) to ensure all your sources are configured correctly. Here is an example of one that would add a VFX and source label and Configure all textures within the Asset/Resources/ folder, with the same settings except disable sRGB for exr and textures with the **_nrm** nomenclature in their filename, or **_lin** nomenclature for forcing non sRGB. |
|||
You can find this default example that you can modify to fit your needs at the following path of the project: |
|||
|
|||
```C# |
|||
using System.IO; |
|||
|
|||
namespace UnityEditor.VFXToolbox |
|||
{ |
|||
public class ImageSequencerSourcePostprocessor : AssetPostprocessor |
|||
{ |
|||
// Internal flags for usage |
|||
public enum Usage |
|||
{ |
|||
Color, |
|||
LinearData |
|||
} |
|||
|
|||
public const string m_RootFolder = "Assets/VFXResources"; |
|||
public const string m_NormalNomenclaturePostFix = "_nrm"; |
|||
public const string m_LinearNomenclatureSuffix = "_lin"; |
|||
public const string m_OpticalFlowNomenclatureSuffix = "_of"; |
|||
public readonly string[] m_Labels = new string[] { "Weapon", "Audio" }; |
|||
|
|||
void OnPreprocessTexture() |
|||
{ |
|||
if (assetPath.StartsWith(m_RootFolder)) // for all assets in VFX resources folder |
|||
{ |
|||
string filename = Path.GetFileName(assetPath); |
|||
string extension = Path.GetExtension(assetPath); |
|||
|
|||
// Default usage is color |
|||
Usage usage = Usage.Color; |
|||
|
|||
// if containing normal suffix, switch to linear |
|||
if (filename.ToLower().Contains(m_NormalNomenclaturePostFix.ToLower())) |
|||
usage = Usage.LinearData; |
|||
|
|||
// if containing linear suffix, switch to linear |
|||
if (filename.ToLower().Contains(m_LinearNomenclatureSuffix.ToLower())) |
|||
usage = Usage.LinearData; |
|||
|
|||
// if containing opticalflow suffix, switch to linear |
|||
if (filename.ToLower().Contains(m_OpticalFlowNomenclatureSuffix.ToLower())) |
|||
usage = Usage.LinearData; |
|||
|
|||
// if HDR, switch to linear |
|||
if(extension.ToLower() == "EXR".ToLower()) |
|||
usage = Usage.LinearData; |
|||
|
|||
TextureImporter importer = (TextureImporter)assetImporter; |
|||
|
|||
// Even if we have normalmaps, we don't want to encode them in swizzled NM yet. |
|||
importer.textureType = TextureImporterType.Default; |
|||
|
|||
switch(usage) |
|||
{ |
|||
default: // Color, but should not happen |
|||
case Usage.Color: |
|||
importer.sRGBTexture = true; |
|||
break; |
|||
case Usage.LinearData: |
|||
importer.sRGBTexture = false; |
|||
break; |
|||
} |
|||
|
|||
importer.alphaSource = TextureImporterAlphaSource.FromInput; |
|||
importer.alphaIsTransparency = false; |
|||
importer.maxTextureSize = 8192; |
|||
importer.mipmapEnabled = true; |
|||
importer.mipmapFilter = TextureImporterMipFilter.KaiserFilter; |
|||
importer.npotScale = TextureImporterNPOTScale.None; |
|||
importer.textureShape = TextureImporterShape.Texture2D; |
|||
importer.textureCompression = TextureImporterCompression.Uncompressed; |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
Here are some guidelines for handling various situations when your source image changes: |
|||
|
|||
- Source file changes: In order to update these sources (i.e. you make changes inside your 3D VFX package and render your frames again), just overwrite the files. Source sequence length increases: If your sequence length ever changes, you will have to manually adjust your sequence length. A safe way to do this is to remove all the frames in the input sequence by clearing the list, then drag and drop the folder again. |
|||
- Source sequence length decreases: this corner case is not yet automatically addressed so you will have to manually trim the exceeding frames by deleting them in the project view. (or use a post-render script in your 3D Package to remove the files and the meta files) |
|||
|
|||
### Creating an Image Sequence |
|||
|
|||
You can create image sequences using the Create Asset Menu: |
|||
|
|||
- From the main Menu : **Assets/Create/Visual Effects/Image Sequence** |
|||
- From the main Project Window + Menu : **Visual Effects/Image Sequence** |
|||
|
|||
![Image Sequence Assets](images/ImageSequenceAssets.png) |
|||
|
|||
After Clicking the item in the menu, enter the name of your Image Sequence asset in the Project View, then press Return key to validate. |
|||
|
|||
Alternatively, When the Image Sequencer window is opened with no asset selected (eg: when opened from the menu), the Image Sequencer Window displays a message that informs that no Asset is selected, and provides the option to Create an image sequence from a button. When clicked, you can save a file for your new asset. |
|||
|
|||
![](Images/ImageSequencerNoAsset.png) |
|||
|
|||
#### Editing an Image Sequence asset |
|||
|
|||
After Creating the Image Sequence, you can either: |
|||
|
|||
* Double click its icon in the Project Window |
|||
* Click its Edit Sequence button at the top of the Unity Inspector |
|||
* Just select this asset while having the Image Sequencer window open. |
|||
|
|||
### Input Frames |
|||
|
|||
The first operation you need to do when working on an Image Sequence asset is to add Input Images to this sequence. These Images need to be already imported into your project and it is advised that they have particular settings in order to keep them as maximum quality. (See Importing and Configuring Source images) |
|||
|
|||
![](images/ImageSequencer-InputFrames.png) |
|||
|
|||
You can import images in the sequence by clicking the Input Frames tab Button to enter Input Frames editing mode then : |
|||
|
|||
- By dragging them directly from the Project Window to the Image Sequencer window |
|||
- By dragging a folder that contains images from the Project Window to the Image Sequencer Window |
|||
- By selecting textures in the Project Window and clicking the plus button at the bottom of the reorderable list. |
|||
|
|||
After importing, Images can be added, reordered, deleted, and using the popup menu, you can also perform actions such as sorting alphabetically. The Actions Popup Button offers you the following menu options: |
|||
|
|||
* Clear : Removes all the elements in the Input Frames List |
|||
* Sort All : Sorts all elements in the input frames list alphabetically |
|||
* Reverse Order : Reverses the order of all elements in the Input Frames list |
|||
|
|||
### Processors |
|||
|
|||
After adding Input Frames to the Image Sequence, you can go into Processor Editing mode. To do so, click the Processors Tab Button at the top of the Left pane. |
|||
|
|||
#### Processor Mode Inspector |
|||
|
|||
When entering Processor mode, the left pane inspector changes and presents the following information: |
|||
|
|||
![](images/ImageSequencer-ClearProcessors.png) |
|||
|
|||
**Clear (Button) :** Clears the current list of frame processors (if not inheriting from another asset.) |
|||
|
|||
![](images/ImageSequencer-FrameProcessorList.png) |
|||
|
|||
**Frame Processors (Reorderable List) :** The list of currently used processors. |
|||
|
|||
* If a frame processor is selected, below the list of processor list will appear the processor inspector. |
|||
* You can reorder processors by dragging them upwards or downwards in the list. |
|||
* You can enable or disable processors by clicking the toggle at the left of the Processor Title |
|||
|
|||
#### Adding Processors |
|||
|
|||
In order to add processors to the list, click the **+** button located at the bottom of the Processors List. Clicking the button opens the Processor Add Menu. |
|||
|
|||
![Add Processor Menu](images/ImageSequencer-AddProcessorMenu.png) |
|||
|
|||
You can use the Input Search Field at the top to filter and refine processors, or navigate the categories by clicking the items, and finally add the processor you want by clicking it in the list. |
|||
|
|||
#### Removing Processors |
|||
|
|||
You can remove processor using the following : |
|||
|
|||
* Select a processor in the list then click the "-" button located at the bottom of the Processor List. |
|||
* Click the Clear button to remove all processors at once. |
|||
|
|||
#### Editing Processors |
|||
|
|||
Upon Selecting a Processor in the list, it displays its properties below the Processors list. You can then edit these properties to modify its behavior. |
|||
|
|||
#### Adjusting Processor Visibility |
|||
|
|||
![](images/ImageSequencer-ProcessorPreview.png) |
|||
|
|||
Upon Selecting a processor in the list, it becomes previewed automatically in the viewport. You can check its visibility by looking at the **eye icon** located in the right part of the processor item in the Processors Reorderable List. |
|||
|
|||
![](images/ImageSequencer-ProcessorPreviewLock.png) |
|||
|
|||
You can **lock** or **unlock** the preview of a certain processor by toggling its lock icon, located in the far right part of the processor item in the Processors Reorderable List. |
|||
|
|||
When in **locked preview state**, you can still select and edit other processors to preview the changes on the currently locked processor view. |
|||
|
|||
#### Inheriting Processors and Settings from other Image Sequences |
|||
|
|||
#### ![ImageSequencer-InheritProcessors](images/ImageSequencer-InheritProcessors.png) |
|||
|
|||
**Inherit Processors From (Image Sequence Asset) :** When using an Image Sequence asset in this field, the whole processor stack becomes overridden by the same processors and their configuration stored into the other asset. When inheriting, you cannot edit these values, except in the Asset that defined them. |
|||
|
|||
### Exporting the Image Sequence |
|||
|
|||
After working on an image sequence, and adding processors, you will end up in a state where you want to generate a texture out of the result of this image sequence. |
|||
|
|||
## Built-In Processors |
|||
|
|||
This section details the built-in processors bundled with Image Sequencer and their behavior. |
|||
|
|||
### Main Category |
|||
|
|||
#### Custom Material |
|||
|
|||
Custom Material Processor enables using custom materials to process Frames. Custom Materials enable performing operations on a frame's pixels using a shader. |
|||
|
|||
When selecting a Custom Material processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-CustomMaterial.png) |
|||
|
|||
The **material** field references a material in the project, for use with this processor. When a material is selected, it displays its inspector below the material field. |
|||
|
|||
You can adjust material properties directly in this inspector. |
|||
|
|||
> Note: Materials you edit in the inspector are edited directly in the asset so they can share their settings across various Image Sequences. |
|||
|
|||
To write shaders compatible with Image Sequencer, see the [Custom Material Shader](#writing-shaders-for-custom-material-processor) documentation. |
|||
|
|||
|
|||
|
|||
### Color |
|||
|
|||
Color processors work on image color for common tasks. |
|||
|
|||
#### Alpha From RGB |
|||
|
|||
Alpha from RGB Processor generates an alpha channel value based on luminance values stored in the RGB channels of the input frames. |
|||
|
|||
When selecting an Alpha from RGB processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-AlphaFromRGB.png) |
|||
|
|||
**Color Filter** : A color to apply as a tint before converting the RGB values to Grayscale. |
|||
|
|||
#### Color Correction |
|||
|
|||
The Color Correction processor applies Brightness, Contrast and Saturation control over the input Frames, as well as Alpha Remapping. |
|||
|
|||
When selecting an Alpha from RGB processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-ColorCorrection.png) |
|||
|
|||
* **Brightness** : Controls image Brightnness |
|||
* **Contrast** : Control Image Contrast |
|||
* **Saturation** : Control Image Color Saturation |
|||
* **Alpha** Curve : Remaps the Alpha Output Range based on the input Alpha values |
|||
|
|||
#### Pre-multiply Alpha |
|||
|
|||
The Pre-Multiply Alpha Processor applies a simple Alpha mask on RGB values to ensure RGB values are pre-multiplied by the alpha channel. This process is often required to use your textures in pre-multiplied alpha blend mode. |
|||
|
|||
When selecting an Pre-Multiply Alpha processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-PremultiplyAlpha.png) |
|||
|
|||
* **Remove Alpha** (Toggle) : Replaces the alpha channel by a solid opacity |
|||
* **Alpha Value** (Float) : The solid opacity value to apply if Remove Alpha is enabled. |
|||
|
|||
#### Remap Color |
|||
|
|||
The Remap Color Processor remaps the output color based on a single grayscale value. This process is quite similar to Photoshop's Gradient Map filter. |
|||
|
|||
When selecting an Remap Color processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-RemapColor.png) |
|||
|
|||
* **Color Source** (Enum) : Controls how the input grayscale gradient is handled |
|||
* SRGB Luminance : RGB luminance as sRGB values (for sRGB input images) |
|||
* Linear Luminance : Linear Luminance (for HDR input images) |
|||
* Linear Red : Linear Red input channel value |
|||
* Linear Green : Linear Green input channel value |
|||
* Linear Blue : Linear Blue input channel value |
|||
* Alpha : Linear Alpha input channel value |
|||
* **Remap Gradient** (Gradient) : Gradient sampled using the input grayscale value to output values. |
|||
|
|||
#### Remove Background |
|||
|
|||
The Remove Background Processor works by trying to remove the RGB values of the background color and restoring the original color of the alpha blended element. |
|||
|
|||
When selecting a Remove Background processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-RemoveBackground.png) |
|||
|
|||
* **Background Color** (Color) : the assumed Color of the background, used for reverse lerp. |
|||
* **Grab** (Button) : Tries to fetch the first pixel's color to use it as the background color. |
|||
|
|||
### Common |
|||
|
|||
Common-category processors work on various tasks such as optimizing the size of every frame and performing adjustments. |
|||
|
|||
#### Crop |
|||
|
|||
The Crop Processor applies cropping to the four edges of a frame in order to reduce unused transparent space and maximize overdraw efficiency. It provides an automatic computation of the closest bounds. |
|||
|
|||
When selecting a Crop processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Crop.png) |
|||
|
|||
* **Top** (Slider) : The amount of pixels to crop at the top edge of the image. |
|||
* **Bottom** (Slider) : The amount of pixels to crop at the bottom edge of the image. |
|||
* **Left** (Slider) : The amount of pixels to crop at the left edge of the image. |
|||
* **Right** (Slider) : The amount of pixels to crop at the right edge of the image. |
|||
* **Automatic Crop Values** : A tool that computes automatically the Top, Bottom, Left and Right values. |
|||
* **Alpha Threshold** (Slider) : The alpha threshold taken into account for the closest bound computation |
|||
* **Find** (Button) : Clicking this button will perform the check on all Crop processor's input frames. |
|||
|
|||
#### Fix Borders |
|||
|
|||
The Fix Border Processor applies gradient fading to all edges of the image, ensuring it fades towards transparent and/or a given color. |
|||
|
|||
When selecting a Fix Border processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-FixBorders.png) |
|||
|
|||
* **Left** (Slider) : The percentage in width that will fade in from the left edge. |
|||
* **Right** (Slider) : The percentage in width that will fade in from the right edge. |
|||
* **Top** (Slider) : The percentage in width that will fade in from the top edge. |
|||
* **Bottom** (Slider) : The percentage in width that will fade in from the bottom edge. |
|||
* **Fade to Color** : The blended target color that will apply to RGB channels (use alpha = 0 to not affect RGB at all) |
|||
* **Fade to Alpha** : The target alpha that will be blended to at the edges of the image (default 0 to fade out) |
|||
* **Exponent** : An exponent applied to the gradient to make it harder or softer |
|||
|
|||
#### Resize |
|||
|
|||
The Resize processor simply resizes images from the processor's input sequence. |
|||
|
|||
When selecting a Resize processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Resize.png) |
|||
|
|||
* Width : the desired output width. |
|||
* Height : the desired output height. |
|||
|
|||
> Use the buttons to access common power of two values. |
|||
|
|||
#### Rotate |
|||
|
|||
The Rotate processor provides 90 degree step rotation to apply on the processor's input images. |
|||
|
|||
When selecting a Rotate processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Rotate.png) |
|||
|
|||
* **Rotation Mode** (enum) : Provides rotation options for 90, 180 or 270 degrees clockwise. |
|||
|
|||
### Sequence |
|||
|
|||
Sequence processors applies processing that modifies the sequence timing's and frame count. |
|||
|
|||
#### Decimate |
|||
|
|||
The Decimate processor discards input images to keep only one image out of N. |
|||
|
|||
When selecting a Decimate processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Decimate.png) |
|||
|
|||
* **Decimate By** (int) : The decimation factor (keeping one image out of N). |
|||
|
|||
#### Fade |
|||
|
|||
The Fade processor fades the image alpha and color to a given color matte, based on a time curve. |
|||
|
|||
When selecting a Fade processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Fade.png) |
|||
|
|||
* **Fade to Color** (Color) : The color to fade To |
|||
* **Fade Curve** (Curve) : The fading curve based on the overall progress of the sequence (percentage as X axis), with Y values blending from the Fade color (0.0) to the Actual color (1.0) |
|||
|
|||
#### Loop Sequence |
|||
|
|||
The Loop Sequence Processor outputs a sequence that is ensured to loop, based on a longer input sequence, and a sync frame. |
|||
|
|||
Images from the input sequence are blended from a sequence prior to the sync frame and after the sync frame. Then blended using a curve in order to ensure the last and first frame (sync frame and sync frame+1) are contiguous. |
|||
|
|||
When selecting a Loop Sequence processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Looping.png) |
|||
|
|||
* **Input Sync Frame** : the desired frame index to use as sync point. |
|||
* **Output Sequence Length** : the desired output sequence length, cannot be greater than half of the input sequence length. |
|||
* **Mix Curve** : The blend curve to apply between the sequence prior to the sync point. |
|||
|
|||
#### Retime |
|||
|
|||
The retime processor applies temporal remapping to the input sequence in order to apply acceleration and/or deceleration to the motion of images. |
|||
|
|||
When selecting a Retime processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-Retime.png) |
|||
|
|||
* **Sequence Length** : the desired output sequence length |
|||
* **Use Retiming Curve** : whether to remap the time using a curve instead of a linear retiming. |
|||
* **Retime Curve** : the time remap curve to apply to the input sequence. Based on the output sequence percentage (X axis), and given the input sequence frames (Y axis) |
|||
|
|||
### Texture Sheet |
|||
|
|||
Texture Sheet processors applies processing to image sequences to turn them into flipbooks or to turn flipbooks into sequences of images. |
|||
|
|||
#### Assemble Flipbook |
|||
|
|||
The Assemble Flipbook assembles a sequence of images into a flipbook sprite sheet of given rows and columns. |
|||
|
|||
When selecting a Assemble Flipbook processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-AssembleFlipbook.png) |
|||
|
|||
* **Assemble Mode** (enum) : How the input sequence is processed |
|||
* Full Sprite Sheet : Assembles the sprite sheet as rows and columns |
|||
* Vertical |
|||
* **Columns** (U) : how many columns to pack into |
|||
* **Rows** (V) : how many rows to pack into |
|||
* **Find Best Ratios** : Clicking this button will show a drop down with all Row and Column possibilities, sorted by closeness to a power-of-two-compatible ratio (ability to resize to sizes that are power of while maintaining a correct Texel aspect ratio) |
|||
|
|||
#### Break Flipbook |
|||
|
|||
The Break Flipbook operator splits an input image into a sequence of length based on the given number of rows and columns. |
|||
|
|||
When selecting a Break Flipbook processor, the following inspector is displayed: |
|||
|
|||
![](images/ImageSequencer-BreakFlipbook.png) |
|||
|
|||
* Columns : the desired amount of columns to split into |
|||
* Rows : the desired amount of rows to split into. |
|||
|
|||
### Shaders for Custom Material Processor |
|||
|
|||
Shaders for Custom Material processor provide a simple way to write a filter for Image Sequencer without relying to writing a full Custom Processor. By using a custom material processor you will be able to run a shader for every input frame and output an output frame of same dimensions and same rows and columns. |
|||
|
|||
#### Writing a shader for Custom Materials |
|||
|
|||
In order to create shaders for Custom material processor you need to create a simple unlit shader from the Project Create Menu : **Shaders > Unlit Shader**. You can alter the default code to match the following code: |
|||
|
|||
```c |
|||
Shader "ImageSequencer/SimpleCustomMaterial" |
|||
{ |
|||
Properties |
|||
{ |
|||
} |
|||
SubShader |
|||
{ |
|||
Tags { "RenderType"="Opaque" } |
|||
LOD 100 |
|||
|
|||
Pass |
|||
{ |
|||
CGPROGRAM |
|||
#pragma vertex vert |
|||
#pragma fragment frag |
|||
|
|||
#include "UnityCG.cginc" |
|||
|
|||
struct appdata |
|||
{ |
|||
float4 vertex : POSITION; |
|||
float2 uv : TEXCOORD0; |
|||
}; |
|||
|
|||
struct v2f |
|||
{ |
|||
float2 uv : TEXCOORD0; |
|||
float4 vertex : SV_POSITION; |
|||
}; |
|||
|
|||
sampler2D _InputFrame; |
|||
float4 _FrameData; |
|||
float4 _FlipbookData; |
|||
|
|||
v2f vert (appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
fixed4 frag (v2f i) : SV_Target |
|||
{ |
|||
// Process the Color |
|||
fixed4 col = tex2D(_InputFrame, i.uv); |
|||
return col; |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Specific Uniforms |
|||
|
|||
In order to access the input frame texture, you need to declare `sampler2D _InputFrame;` as uniform variable in the body of the shader. This uniform **must not be declared** into the `properties` section of the shader. |
|||
|
|||
If you want to access per-frame data, you can declare the following uniform variables in order to get values from Image Sequencer. |
|||
|
|||
``` |
|||
// ImageSequencer CustomMaterial uniforms |
|||
// ====================================== |
|||
// |
|||
// sampler2D _InputFrame; // Input Frame (from previous sequence) |
|||
// |
|||
// float4 _FrameData; // Frame Data |
|||
// // x, y : width (x) and height (y) in pixels |
|||
// // z, w : sequence index (z) and length (w) |
|||
// |
|||
// float4 _FlipbookData; // Flipbook Data |
|||
// // x, y : number of columns (x) and rows (y) |
|||
// // z, w : (unused) |
|||
``` |
|||
|
|||
### Custom Processors |
|||
|
|||
Custom Processors can be created in your project, to extend the features of Image Sequencer. Custom processors differ from Custom Material Processors as they can access more of the ImageSequencer features: |
|||
|
|||
* Output a sequence length that's different from the input sequence length |
|||
* Output a sequence of images of different pixel dimensions from the input sequence pixel dimensions. |
|||
* Change the **numU** and **numV** texture sheet cells. |
|||
* Perform multiple shader calls per execution |
|||
* Display a custom inspector and custom canvas overlay |
|||
|
|||
#### Creating a new Custom Processor |
|||
|
|||
To create a new custom processor so you need the following: |
|||
|
|||
* A C# class that extends `UnityEditor.Experimental.VFX.Toolbox.ImageSequencer.ProcessorBase` |
|||
|
|||
* This class is for use with **UnityEditor** assembly only and must be stored into an `Editor` folder. |
|||
* The C# class must implement the `[Processor("Category","Name")]` class attribute to be visible in the add menu. |
|||
|
|||
* A Shader file that will be referenced and used by the C# class. |
|||
|
|||
|
|||
|
|||
Here is a sample C# code that you can use as a starting base. |
|||
|
|||
```c# |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEditor; |
|||
using UnityEditor.Experimental.VFX.Toolbox.ImageSequencer; |
|||
using UnityEngine; |
|||
|
|||
[Processor("Category","Sample Processor")] |
|||
public class SampleProcessor : ProcessorBase |
|||
{ |
|||
/// <summary> |
|||
/// The Shader Path (in the project) of the Shader File |
|||
/// </summary> |
|||
public override string shaderPath => "Assets/Path/To/Shader.shader"; |
|||
|
|||
/// <summary> |
|||
/// The Processor Name (as it will appear in the list) |
|||
/// </summary> |
|||
public override string processorName => "Sample Processor"; |
|||
|
|||
|
|||
/// <summary> |
|||
/// Default() configures a default instance of the processor |
|||
/// of the processor (e.g: when it will be added from the menu) |
|||
/// </summary> |
|||
public override void Default() |
|||
{ |
|||
} |
|||
|
|||
/// <summary> |
|||
/// OnInspectorGUI() displays the properties in the left pane editor. |
|||
/// use the serializedObject to fetch serializedProperties |
|||
/// </summary> |
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
return changed; |
|||
} |
|||
|
|||
/// <summary> |
|||
/// Process(int frame) method is called when the frame needs to be processed. |
|||
/// ProcessFrame(int, texture) needs to be called in order to run the shader on the full frame. |
|||
/// You can still perform Graphics.Blit() if you need to perform multiple blits. |
|||
/// </summary> |
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
Here is the corresponding Shader code: |
|||
|
|||
```c |
|||
Shader "Hidden/VFXToolbox/ImageSequencer/SampleProcessor" |
|||
{ |
|||
Properties |
|||
{ |
|||
_MainTex("Texture", 2D) = "white" {} |
|||
} |
|||
SubShader |
|||
{ |
|||
// No culling or depth |
|||
Cull Off ZWrite Off ZTest Always |
|||
|
|||
Pass |
|||
{ |
|||
CGPROGRAM |
|||
#pragma vertex vert |
|||
#pragma fragment frag |
|||
|
|||
#include "UnityCG.cginc" |
|||
|
|||
struct appdata |
|||
{ |
|||
float4 vertex : POSITION; |
|||
float2 uv : TEXCOORD0; |
|||
}; |
|||
|
|||
struct v2f |
|||
{ |
|||
float2 uv : TEXCOORD0; |
|||
float4 vertex : SV_POSITION; |
|||
}; |
|||
|
|||
sampler2D _MainTex; |
|||
|
|||
v2f vert(appdata v) |
|||
{ |
|||
v2f o; |
|||
o.vertex = UnityObjectToClipPos(v.vertex); |
|||
o.uv = v.uv; |
|||
return o; |
|||
} |
|||
|
|||
fixed4 frag(v2f i) : SV_Target |
|||
{ |
|||
return tex2D(_MainTex, i.uv); |
|||
} |
|||
ENDCG |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|
|||
using UnityEngine; |
|||
using System; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
internal partial class ProcessingNodeStack |
|||
{ |
|||
public void AddSettingsObjectToAsset(ImageSequence asset, ScriptableObject settings) |
|||
{ |
|||
AssetDatabase.AddObjectToAsset(settings,asset); |
|||
settings.hideFlags = HideFlags.HideInHierarchy; |
|||
} |
|||
|
|||
public void AddProcessorInfoObjectToAsset(ImageSequence asset, ProcessorInfo info) |
|||
{ |
|||
AssetDatabase.AddObjectToAsset(info,asset); |
|||
info.hideFlags = HideFlags.HideInHierarchy; |
|||
} |
|||
|
|||
public void RemoveAllInputFrames(ImageSequence asset) |
|||
{ |
|||
asset.inputFrameGUIDs.Clear(); |
|||
m_InputSequence.frames.Clear(); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void SortAllInputFrames(ImageSequence asset) |
|||
{ |
|||
asset.inputFrameGUIDs.Sort((guidA,guidB) => { |
|||
return string.Compare(AssetDatabase.GUIDToAssetPath(guidA), AssetDatabase.GUIDToAssetPath(guidB)); |
|||
}); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void ReverseAllInputFrames(ImageSequence asset) |
|||
{ |
|||
asset.inputFrameGUIDs.Reverse(); |
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void LoadFramesFromAsset(ImageSequence asset) |
|||
{ |
|||
inputSequence.frames.Clear(); |
|||
if (asset.inputFrameGUIDs != null && asset.inputFrameGUIDs.Count > 0) |
|||
{ |
|||
int count = asset.inputFrameGUIDs.Count; |
|||
int i = 1; |
|||
foreach (string guid in asset.inputFrameGUIDs) |
|||
{ |
|||
VFXToolboxGUIUtility.DisplayProgressBar("Image Sequencer", "Loading Textures (" + i + "/" + count + ")", (float)i/count, 0.1f); |
|||
string path = AssetDatabase.GUIDToAssetPath(guid); |
|||
Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(path); |
|||
if (t != null) |
|||
{ |
|||
inputSequence.frames.Add(new ProcessingFrame(t)); |
|||
} |
|||
else |
|||
{ |
|||
inputSequence.frames.Add(ProcessingFrame.Missing); |
|||
} |
|||
i++; |
|||
} |
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
} |
|||
} |
|||
|
|||
public void SyncFramesToAsset(ImageSequence asset) |
|||
{ |
|||
asset.inputFrameGUIDs.Clear(); |
|||
foreach(ProcessingFrame f in inputSequence.frames) |
|||
{ |
|||
asset.inputFrameGUIDs.Add(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(f.texture))); |
|||
} |
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void AddProcessor(ProcessingNode node, ImageSequence asset) |
|||
{ |
|||
AddProcessorInfoObjectToAsset(asset, node.ProcessorInfo); |
|||
asset.processorInfos.Add(node.ProcessorInfo); |
|||
|
|||
ProcessorBase settings = node.GetSettingsAbstract(); |
|||
if (settings != null) |
|||
{ |
|||
AddSettingsObjectToAsset(asset, settings); |
|||
node.ProcessorInfo.Settings = settings; |
|||
} |
|||
m_ProcessingNodes.Add(node); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void RemoveAllProcessors(ImageSequence asset) |
|||
{ |
|||
asset.processorInfos.Clear(); |
|||
m_ProcessingNodes.Clear(); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void RemoveProcessor(int index, ImageSequence asset) |
|||
{ |
|||
asset.processorInfos.RemoveAt(index); |
|||
m_ProcessingNodes.RemoveAt(index); |
|||
|
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
|
|||
public void ReorderProcessors(ImageSequence asset) |
|||
{ |
|||
if(m_ProcessingNodes.Count > 0) |
|||
{ |
|||
List<ProcessingNode> old = new List<ProcessingNode>(); |
|||
foreach(ProcessingNode n in m_ProcessingNodes) |
|||
{ |
|||
old.Add(n); |
|||
} |
|||
|
|||
m_ProcessingNodes.Clear(); |
|||
foreach(ProcessorInfo info in asset.processorInfos) |
|||
{ |
|||
foreach(ProcessingNode p in old) |
|||
{ |
|||
if(p.ProcessorInfo.Equals(info)) |
|||
{ |
|||
m_ProcessingNodes.Add(p); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
EditorUtility.SetDirty(asset); |
|||
} |
|||
} |
|||
|
|||
public void LoadProcessorsFromAsset(ImageSequence asset) |
|||
{ |
|||
m_ProcessingNodes.Clear(); |
|||
|
|||
var infos = asset.processorInfos; |
|||
|
|||
UpdateProcessorsFromAssembly(); |
|||
|
|||
// Creating Runtime
|
|||
foreach(ProcessorInfo procInfo in infos) |
|||
{ |
|||
var processor = (ProcessingNode)Activator.CreateInstance(typeof(ProcessingNode), this, procInfo); |
|||
m_ProcessingNodes.Add(processor); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
internal partial class ProcessingNodeStack |
|||
{ |
|||
public ProcessingFrameSequence inputSequence |
|||
{ |
|||
get |
|||
{ |
|||
return m_InputSequence; |
|||
} |
|||
} |
|||
|
|||
public ProcessingFrameSequence outputSequence |
|||
{ |
|||
get |
|||
{ |
|||
if (m_ProcessingNodes.Count > 0) |
|||
return m_ProcessingNodes[m_ProcessingNodes.Count - 1].OutputSequence; |
|||
else |
|||
return m_InputSequence; |
|||
} |
|||
} |
|||
|
|||
public ImageSequencer imageSequencer |
|||
{ |
|||
get { return m_ImageSequencer; } |
|||
} |
|||
|
|||
public List<ProcessingNode> nodes |
|||
{ |
|||
get |
|||
{ |
|||
return m_ProcessingNodes; |
|||
} |
|||
} |
|||
|
|||
private List<ProcessingNode> m_ProcessingNodes; |
|||
private ProcessingFrameSequence m_InputSequence; |
|||
private ImageSequencer m_ImageSequencer; |
|||
|
|||
public ProcessingNodeStack(ProcessingFrameSequence inputSequence, ImageSequencer imageSequencer) |
|||
{ |
|||
m_InputSequence = inputSequence; |
|||
m_ProcessingNodes = new List<ProcessingNode>(); |
|||
m_ImageSequencer = imageSequencer; |
|||
|
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
foreach(ProcessingNode p in m_ProcessingNodes) |
|||
{ |
|||
p.Dispose(); |
|||
} |
|||
m_ProcessingNodes.Clear(); |
|||
} |
|||
|
|||
public ProcessingFrameSequence GetOutputSequence() |
|||
{ |
|||
if(m_ProcessingNodes.Count > 0) |
|||
{ |
|||
return m_ProcessingNodes[m_ProcessingNodes.Count - 1].OutputSequence; |
|||
} |
|||
else |
|||
{ |
|||
return inputSequence; |
|||
} |
|||
} |
|||
|
|||
public ProcessingFrameSequence GetInputSequence(ProcessingNode processor) |
|||
{ |
|||
int index = m_ProcessingNodes.IndexOf(processor); |
|||
|
|||
if (index > 0) |
|||
{ |
|||
return m_ProcessingNodes[index - 1].OutputSequence; |
|||
} |
|||
else |
|||
return m_InputSequence; |
|||
} |
|||
|
|||
public ProcessingNode GetNextProcessor(ProcessingNode node) |
|||
{ |
|||
int index = m_ProcessingNodes.IndexOf(node); |
|||
if(index < m_ProcessingNodes.Count-1) |
|||
{ |
|||
return m_ProcessingNodes[index + 1]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public void Invalidate(ProcessingNode node) |
|||
{ |
|||
int index = m_ProcessingNodes.IndexOf(node); |
|||
if(index != -1) |
|||
m_ProcessingNodes[index].Invalidate(); |
|||
} |
|||
|
|||
public void InvalidateAll() |
|||
{ |
|||
if (m_ProcessingNodes.Count > 0) |
|||
m_ProcessingNodes[0].Invalidate(); |
|||
} |
|||
|
|||
|
|||
public Dictionary<Type, ProcessorAttribute> settingsDefinitions { get; private set; } |
|||
|
|||
public void UpdateProcessorsFromAssembly() |
|||
{ |
|||
settingsDefinitions = new Dictionary<Type, ProcessorAttribute>(); |
|||
|
|||
var processorType = typeof(ProcessorBase); |
|||
var attrType = typeof(ProcessorAttribute); |
|||
|
|||
Type[] allProcessorTypes = new Type[0]; |
|||
|
|||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies() ) |
|||
{ |
|||
try |
|||
{ |
|||
allProcessorTypes = allProcessorTypes.Concat(assembly.GetTypes().Where(t => |
|||
t.IsClass |
|||
&& !t.IsAbstract |
|||
&& t.IsSubclassOf(processorType) |
|||
&& t.IsDefined(attrType, false) |
|||
)).ToArray(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
UnityEngine.Debug.LogException(e); |
|||
} |
|||
} |
|||
|
|||
foreach (var type in allProcessorTypes) |
|||
{ |
|||
var attr = (ProcessorAttribute)type.GetCustomAttributes(attrType, false)[0]; |
|||
settingsDefinitions.Add(type, attr); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
/// <summary>
|
|||
/// Base Class for Custom Processors. Derive from this class to add a new Processor.
|
|||
/// In order to populate processors in the menu, you need to implement the [ProcessorAttribute] to the class.
|
|||
/// </summary>
|
|||
public abstract class ProcessorBase : ScriptableObject |
|||
{ |
|||
/// <summary>
|
|||
/// Asset Path of the Shader used for this processor (eg: "Assets/Shaders/MyShader.shader")
|
|||
/// </summary>
|
|||
public abstract string shaderPath { get; } |
|||
|
|||
/// <summary>
|
|||
/// Name of the Processor
|
|||
/// </summary>
|
|||
public abstract string processorName { get; } |
|||
|
|||
/// <summary>
|
|||
/// Display Label text of the processor (will be displayed in the processor list and asset inspector)
|
|||
/// </summary>
|
|||
public virtual string label => processorName; |
|||
|
|||
/// <summary>
|
|||
/// Number of U (Columns) defined by this processor. Implement to override default (passthrough input sequence's numU)
|
|||
/// </summary>
|
|||
public virtual int numU => inputSequenceNumU; |
|||
|
|||
/// <summary>
|
|||
/// Number of V (Rows) defined by this processor. Implement to override default (passthrough input sequence's numV)
|
|||
/// </summary>
|
|||
public virtual int numV => inputSequenceNumV; |
|||
|
|||
/// <summary>
|
|||
/// Number of frames defined by this processor. Implement to override default (passthrough input's sequence length)
|
|||
/// </summary>
|
|||
public virtual int sequenceLength => processingNode.InputSequence.length; |
|||
|
|||
/// <summary>
|
|||
/// Determines the actual processing of the frame. Will be called when this frame is requested by the Image Sequencer.
|
|||
/// </summary>
|
|||
/// <param name="frame">the requested frame index</param>
|
|||
/// <returns></returns>
|
|||
public abstract bool Process(int frame); |
|||
|
|||
/// <summary>
|
|||
/// Updates the output size of the processing node (resets internal render targets), implement to override the default (passthrough the input sequence frame width and height)
|
|||
/// </summary>
|
|||
public virtual void UpdateOutputSize() |
|||
{ |
|||
processingNode.SetOutputSize(processingNode.InputSequence.width, processingNode.InputSequence.height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Displays the Processor inspector in the left pane of the Image Sequencer
|
|||
/// </summary>
|
|||
/// <param name="changed">Whether the inspector has already caught changes.</param>
|
|||
/// <param name="serializedObject">The processor's serializedObject</param>
|
|||
/// <returns>whether there has changes to apply</returns>
|
|||
public abstract bool OnInspectorGUI(bool changed, SerializedObject serializedObject); |
|||
|
|||
/// <summary>
|
|||
/// Displays the Processor's Canvas Helpers as overlay.
|
|||
/// </summary>
|
|||
/// <param name="canvas">The Image Sequencer Canvas currently drawn</param>
|
|||
/// <returns>whether the canvas needs to redraw</returns>
|
|||
public virtual bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the default values of the processor. Will be called to configure the default state when a new processor is added to the Image Sequence.
|
|||
/// </summary>
|
|||
public abstract void Default(); |
|||
|
|||
#region PROCESSINGNODE ACCESS
|
|||
/// <summary>
|
|||
/// The Input Sequence Frame Count
|
|||
/// </summary>
|
|||
public int inputSequenceLength => processingNode.InputSequence.length; |
|||
|
|||
/// <summary>
|
|||
/// The Input Sequence Frame Width (in pixels)
|
|||
/// </summary>
|
|||
public int inputSequenceWidth => processingNode.InputSequence.width; |
|||
|
|||
/// <summary>
|
|||
/// The Input Sequence Frame Height (in pixels)
|
|||
/// </summary>
|
|||
public int inputSequenceHeight => processingNode.InputSequence.height; |
|||
|
|||
/// <summary>
|
|||
/// The Input Sequence Flipbook U Count (Columns)
|
|||
/// </summary>
|
|||
public int inputSequenceNumU => processingNode.InputSequence.numU; |
|||
|
|||
/// <summary>
|
|||
/// The Input Sequence Flipbook V Count (Rows)
|
|||
/// </summary>
|
|||
public int inputSequenceNumV => processingNode.InputSequence.numV; |
|||
|
|||
/// <summary>
|
|||
/// Whether the Input frame Sequence is the Asset's Input Frame List (use to determine whether it needs gamma correction or not)
|
|||
/// </summary>
|
|||
public bool isInputFrameSequence => processingNode.InputSequence.processingNode == null; |
|||
|
|||
/// <summary>
|
|||
/// Whether the current processor is being previewed in the Image Sequencer Viewport, or not (for example, when the view is locked to another processor's result)
|
|||
/// </summary>
|
|||
public bool isCurrentlyPreviewed => processingNode.isCurrentlyPreviewed; |
|||
|
|||
/// <summary>
|
|||
/// The current Image Sequencer Viewport's preview sequence length. Please not that this is not necessarily this Processor's preview, use isCurrentlyPreviewed to check.
|
|||
/// </summary>
|
|||
public int previewSequenceLength => processingNode.previewSequenceLength; |
|||
|
|||
/// <summary>
|
|||
/// The current Image Sequencer Viewport's preview image index. Please not that this is not necessarily this Processor's preview, use isCurrentlyPreviewed to check.
|
|||
/// </summary>
|
|||
public int previewCurrentFrame => processingNode.previewCurrentFrame; |
|||
|
|||
/// <summary>
|
|||
/// The material internally used for this processor. It is created from the shader defined using this.shaderPath.
|
|||
/// </summary>
|
|||
public Material material => processingNode.material; |
|||
|
|||
/// <summary>
|
|||
/// Requests the Texture (and its Processing) of the Input Sequence Image at given index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the Frame to be returned.</param>
|
|||
/// <returns>The texture object corresponding to the frame.</returns>
|
|||
public Texture RequestInputTexture(int index) |
|||
{ |
|||
return processingNode.InputSequence.RequestFrame(index).texture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Requests the Texture of the Output Sequence Image at given index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the Frame to be returned.</param>
|
|||
/// <returns>The texture object corresponding to the frame.</returns>
|
|||
public Texture RequestOutputTexture(int index) |
|||
{ |
|||
return processingNode.OutputSequence.frames[index].texture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the Output Size to the Processing Node
|
|||
/// </summary>
|
|||
/// <param name="width"></param>
|
|||
/// <param name="height"></param>
|
|||
public void SetOutputSize(int width, int height) |
|||
{ |
|||
processingNode.SetOutputSize(width, height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process the Frame at Given Index, using given material and texture as MainTexture.
|
|||
/// </summary>
|
|||
/// <param name="outputIndex">Frame Index to process</param>
|
|||
/// <param name="mainTexture">Texture object to pass as MainTexture</param>
|
|||
/// <param name="material">Material to use for the procesing</param>
|
|||
public void ProcessFrame(int outputIndex, Texture mainTexture, Material material) |
|||
{ |
|||
processingNode.ExecuteShaderAndDump(outputIndex, mainTexture, material); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process the Frame at Given Index, using default processor material and texture as MainTexture.
|
|||
/// </summary>
|
|||
/// <param name="outputIndex">Frame Index to process</param>
|
|||
/// <param name="mainTexture">Texture object to pass as MainTexture</param>
|
|||
public void ProcessFrame(int outputIndex, Texture mainTexture = null) |
|||
{ |
|||
processingNode.ExecuteShaderAndDump(outputIndex, mainTexture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Invalidates the Processor (will require to rebake frames)
|
|||
/// </summary>
|
|||
public void Invalidate() |
|||
{ |
|||
processingNode.Invalidate(); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region INTERNAL
|
|||
private ProcessingNode processingNode; |
|||
|
|||
internal void AttachTo(ProcessingNode processor) |
|||
{ |
|||
this.processingNode = processor; |
|||
} |
|||
#endregion
|
|||
|
|||
} |
|||
} |
|||
|
|||
|
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Color","Alpha From RGB")] |
|||
internal class AlphaFromRGBProcessor : ProcessorBase |
|||
{ |
|||
public Color BWFilterTint; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/AlphaFromRGB.shader"; |
|||
|
|||
public override string processorName => "Alpha From RGB"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
BWFilterTint = Color.white; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
material.SetVector("_RGBTint", BWFilterTint); |
|||
|
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var tint = serializedObject.FindProperty("BWFilterTint"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
EditorGUILayout.PropertyField(tint, VFXToolboxGUIUtility.Get("Color Filter")); |
|||
EditorGUILayout.HelpBox("Color Filter serves as a tint before applying the black and white desaturation, just like in black and white photography. This way you can filter color weighting.", MessageType.Info); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Texture Sheet", "Assemble Flipbook")] |
|||
internal class AssembleProcessor : ProcessorBase |
|||
{ |
|||
public enum AssembleMode |
|||
{ |
|||
FullSpriteSheet = 0, |
|||
VerticalSequence = 1 |
|||
} |
|||
|
|||
public int FlipbookNumU; |
|||
public int FlipbookNumV; |
|||
public AssembleMode Mode; |
|||
|
|||
bool hasChanged = false; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/AssembleBlit.shader"; |
|||
|
|||
public override string processorName => "Assemble Flipbook"; |
|||
|
|||
public override string label |
|||
{ |
|||
get |
|||
{ |
|||
string numU = (Mode == AssembleMode.VerticalSequence) ? "*" : FlipbookNumU.ToString(); |
|||
string numV = FlipbookNumV.ToString(); |
|||
return $"{processorName} ({numU}x{numV})"; |
|||
} |
|||
} |
|||
|
|||
public override int numU |
|||
{ |
|||
get |
|||
{ |
|||
switch (Mode) |
|||
{ |
|||
default: |
|||
case AssembleMode.FullSpriteSheet: |
|||
return FlipbookNumU * inputSequenceNumU; |
|||
case AssembleMode.VerticalSequence: |
|||
return inputSequenceNumU; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override int numV => FlipbookNumV * inputSequenceNumV; |
|||
|
|||
public override int sequenceLength |
|||
{ |
|||
get |
|||
{ |
|||
switch (Mode) |
|||
{ |
|||
default: |
|||
case AssembleMode.FullSpriteSheet: |
|||
return 1; |
|||
|
|||
case AssembleMode.VerticalSequence: |
|||
return inputSequenceLength / FlipbookNumV; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void Default() |
|||
{ |
|||
FlipbookNumU = 5; |
|||
FlipbookNumV = 5; |
|||
Mode = AssembleMode.FullSpriteSheet; |
|||
} |
|||
|
|||
public override void UpdateOutputSize() |
|||
{ |
|||
switch (Mode) |
|||
{ |
|||
case AssembleMode.FullSpriteSheet: |
|||
|
|||
SetOutputSize(inputSequenceWidth * FlipbookNumU, inputSequenceHeight * FlipbookNumV); |
|||
break; |
|||
|
|||
case AssembleMode.VerticalSequence: |
|||
|
|||
SetOutputSize(inputSequenceWidth, inputSequenceHeight * FlipbookNumV); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
if (Event.current.type != EventType.Repaint) |
|||
return false; |
|||
|
|||
Vector2 topRight; |
|||
Vector2 bottomLeft; |
|||
|
|||
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2)); |
|||
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2)); |
|||
|
|||
// Texts
|
|||
GUI.color = canvas.styles.green; |
|||
for (int i = 0; i < FlipbookNumU; i++) |
|||
{ |
|||
float cw = (topRight.x - bottomLeft.x) / FlipbookNumU; |
|||
GUI.Label(new Rect(bottomLeft.x + i * cw, topRight.y - 16, cw, 16), (i + 1).ToString(), canvas.styles.miniLabelCenter); |
|||
} |
|||
|
|||
for (int i = 0; i < FlipbookNumV; i++) |
|||
{ |
|||
float ch = (bottomLeft.y - topRight.y) / FlipbookNumV; |
|||
VFXToolboxGUIUtility.GUIRotatedLabel(new Rect(bottomLeft.x - 8, topRight.y + i * ch, 16, ch), (i + 1).ToString(), -90.0f, canvas.styles.miniLabelCenter); |
|||
} |
|||
|
|||
GUI.color = Color.white; |
|||
return false; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
int length = inputSequenceLength; |
|||
|
|||
RenderTexture backup = RenderTexture.active; |
|||
|
|||
switch (Mode) |
|||
{ |
|||
case AssembleMode.FullSpriteSheet: |
|||
|
|||
// Blit Every Image inside output
|
|||
for (int i = 0; i < (FlipbookNumU * FlipbookNumV); i++) |
|||
{ |
|||
int u = i % FlipbookNumU; |
|||
int v = (FlipbookNumV - 1) - (int)Mathf.Floor((float)i / FlipbookNumU); |
|||
|
|||
Vector2 size = new Vector2(1.0f / FlipbookNumU, 1.0f / FlipbookNumV); |
|||
int idx = Mathf.Clamp(i, 0, length - 1); |
|||
|
|||
Texture currentTexture = RequestInputTexture(idx); |
|||
|
|||
Vector4 ClipCoordinates = new Vector4(u * size.x, v * size.y, size.x, size.y); |
|||
|
|||
material.SetTexture("_MainTex", currentTexture); |
|||
material.SetVector("_CC", ClipCoordinates); |
|||
|
|||
Graphics.Blit(currentTexture, (RenderTexture)RequestOutputTexture(0), material); |
|||
} |
|||
|
|||
RenderTexture.active = backup; |
|||
|
|||
break; |
|||
|
|||
case AssembleMode.VerticalSequence: |
|||
|
|||
// Blit Every N'th Image inside output
|
|||
int cycleLength = inputSequenceLength / FlipbookNumV; |
|||
|
|||
for (int i = 0; i < FlipbookNumV; i++) |
|||
{ |
|||
int u = 0; |
|||
int v = FlipbookNumV - 1 - i; |
|||
|
|||
Vector2 size = new Vector2(1.0f, 1.0f / FlipbookNumV); |
|||
int idx = Mathf.Clamp((i * cycleLength) + frame, 0, length - 1); |
|||
|
|||
Texture currentTexture = RequestInputTexture(idx); |
|||
|
|||
Vector4 ClipCoordinates = new Vector4(u * size.x, v * size.y, size.x, size.y); |
|||
|
|||
material.SetTexture("_MainTex", currentTexture); |
|||
material.SetVector("_CC", ClipCoordinates); |
|||
|
|||
Graphics.Blit(currentTexture, (RenderTexture)RequestOutputTexture(frame), material); |
|||
} |
|||
|
|||
RenderTexture.active = backup; |
|||
break; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var flipbookNumU = serializedObject.FindProperty("FlipbookNumU"); |
|||
var flipbookNumV = serializedObject.FindProperty("FlipbookNumV"); |
|||
var mode = serializedObject.FindProperty("Mode"); |
|||
|
|||
var assembleMode = (AssembleMode)mode.intValue; |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
assembleMode = (AssembleMode)EditorGUILayout.EnumPopup(VFXToolboxGUIUtility.Get("Assemble Mode"), assembleMode); |
|||
if (assembleMode != (AssembleMode)mode.intValue) |
|||
{ |
|||
mode.intValue = (int)assembleMode; |
|||
hasChanged = true; |
|||
} |
|||
|
|||
switch (assembleMode) |
|||
{ |
|||
case AssembleMode.FullSpriteSheet: |
|||
|
|||
int newU = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Columns (U) : "), flipbookNumU.intValue); |
|||
int newV = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue); |
|||
|
|||
if (inputSequenceLength > 0) |
|||
{ |
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Label(VFXToolboxGUIUtility.Get("Find Best Ratios"), GUILayout.Width(EditorGUIUtility.labelWidth)); |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Get"))) |
|||
{ |
|||
float frameRatio = (float)inputSequenceWidth / (float)inputSequenceHeight; |
|||
int length = inputSequenceLength; |
|||
List<int> ratios = new List<int>(); |
|||
SortedDictionary<int, float> coeffs = new SortedDictionary<int, float>(); |
|||
float rad = Mathf.Sqrt(length); |
|||
for (int i = (int)rad; i >= 1; i--) |
|||
{ |
|||
if (((float)length / (float)i) % 1.0f == 0.0f) |
|||
{ |
|||
float pageRatio = (float)i / (length / i); |
|||
float fullRatio = frameRatio * pageRatio; |
|||
float divergence = Mathf.Abs(Mathf.Log(fullRatio, 2.0f) % 1.0f); |
|||
|
|||
if (!ratios.Contains(i)) |
|||
{ |
|||
ratios.Add(i); |
|||
coeffs.Add(i, divergence); |
|||
} |
|||
|
|||
fullRatio = frameRatio / pageRatio; |
|||
divergence = Mathf.Abs(Mathf.Log(fullRatio, 2.0f) % 1.0f); |
|||
|
|||
if (!ratios.Contains(length / i)) |
|||
{ |
|||
ratios.Add(length / i); |
|||
coeffs.Add((length / i), divergence); |
|||
} |
|||
} |
|||
} |
|||
|
|||
GenericMenu menu = new GenericMenu(); |
|||
|
|||
var sortedValues = coeffs.OrderBy(kvp => kvp.Value); |
|||
|
|||
foreach (KeyValuePair<int, float> kvp in sortedValues) |
|||
{ |
|||
int value = kvp.Key; |
|||
|
|||
menu.AddItem(new GUIContent(value + " x " + (length / value) + ((kvp.Value == 0.0f) ? " (PERFECT)" : "")), false, |
|||
(o) => { |
|||
var seq_length = inputSequenceLength; |
|||
var seq_numU = (int)o; |
|||
var seq_numV = seq_length / seq_numU; |
|||
serializedObject.Update(); |
|||
var seq_flipbookNumU = serializedObject.FindProperty("FlipbookNumU"); |
|||
var seq_flipbookNumV = serializedObject.FindProperty("FlipbookNumV"); |
|||
|
|||
seq_flipbookNumU.intValue = seq_numU; |
|||
seq_flipbookNumV.intValue = seq_numV; |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
} |
|||
, value); |
|||
} |
|||
if (menu.GetItemCount() > 0) |
|||
menu.ShowAsContext(); |
|||
} |
|||
} |
|||
EditorGUILayout.HelpBox("Find Best Ratios will try to find matching possibilities for the current sequence, ordered by ratio pertinence, meaning that first results will have less stretch when resized to power-of-two textures.", MessageType.Info); |
|||
} |
|||
|
|||
|
|||
if (newU != flipbookNumU.intValue) |
|||
{ |
|||
newU = Mathf.Clamp(newU, 1, inputSequenceLength / newV); |
|||
flipbookNumU.intValue = newU; |
|||
} |
|||
|
|||
if (newV != flipbookNumV.intValue) |
|||
{ |
|||
newV = Mathf.Clamp(newV, 1, inputSequenceLength / newU); |
|||
flipbookNumV.intValue = newV; |
|||
} |
|||
break; |
|||
|
|||
case AssembleMode.VerticalSequence: |
|||
|
|||
int numRows = EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue); |
|||
|
|||
if (numRows != flipbookNumV.intValue) |
|||
{ |
|||
numRows = Mathf.Clamp(numRows, 1, inputSequenceLength); |
|||
flipbookNumV.intValue = numRows; |
|||
} |
|||
|
|||
break; |
|||
|
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
hasChanged = true; |
|||
} |
|||
|
|||
return hasChanged; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Texture Sheet", "Break Flipbook")] |
|||
internal class BreakFlipbookProcessor : ProcessorBase |
|||
{ |
|||
public int FlipbookNumU; |
|||
public int FlipbookNumV; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/GetSubUV.shader"; |
|||
|
|||
public override string processorName => "Break Flipbook"; |
|||
|
|||
public override string label => $"{processorName} ({FlipbookNumU}x{FlipbookNumV}): {FlipbookNumU * FlipbookNumV} frame(s)."; |
|||
|
|||
private bool m_BypassSecurityCheck = false; |
|||
|
|||
public override void Default() |
|||
{ |
|||
FlipbookNumU = 5; |
|||
FlipbookNumV = 5; |
|||
} |
|||
|
|||
public override void UpdateOutputSize() |
|||
{ |
|||
int width = (int)Mathf.Ceil((float)inputSequenceWidth / FlipbookNumU); |
|||
int height = (int)Mathf.Ceil((float)inputSequenceHeight / FlipbookNumV); |
|||
SetOutputSize(width, height); |
|||
} |
|||
|
|||
public override int sequenceLength => Mathf.Min(FlipbookNumU, inputSequenceWidth) * Mathf.Min(FlipbookNumV, inputSequenceHeight); |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture texture = RequestInputTexture(0); |
|||
material.SetTexture("_MainTex", texture); |
|||
Vector4 rect = new Vector4(); |
|||
|
|||
int u = Mathf.Min(FlipbookNumU, texture.width); |
|||
int v = Mathf.Min(FlipbookNumV, texture.height); |
|||
|
|||
int x = frame % FlipbookNumU; |
|||
int y = (int)Mathf.Floor((float)frame / u); |
|||
rect.x = (float)x; |
|||
rect.y = (float)(v - 1) - y; |
|||
rect.z = 1.0f / u; |
|||
rect.w = 1.0f / v; |
|||
|
|||
material.SetVector("_Rect", rect); |
|||
ProcessFrame(frame, texture); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool hasChanged, SerializedObject serializedObject) |
|||
{ |
|||
var flipbookNumU = serializedObject.FindProperty("FlipbookNumU"); |
|||
var flipbookNumV = serializedObject.FindProperty("FlipbookNumV"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
int newU = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Columns (U) : "), flipbookNumU.intValue), 1, inputSequenceWidth); |
|||
int newV = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Rows (V) : "), flipbookNumV.intValue), 1, inputSequenceHeight); |
|||
|
|||
if (newU != flipbookNumU.intValue || flipbookNumV.intValue != newV) |
|||
GUI.changed = true; |
|||
|
|||
if (m_BypassSecurityCheck) |
|||
EditorGUILayout.HelpBox("Warning: you are currently bypassing frame count limits. Proceed with caution when entering values, as it can take a long time to process and stall your editor.", MessageType.Warning); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Debug.Log("Updated"); |
|||
|
|||
if (newU * newV <= 4096) |
|||
{ |
|||
flipbookNumU.intValue = newU; |
|||
flipbookNumV.intValue = newV; |
|||
} |
|||
else |
|||
{ |
|||
if (!m_BypassSecurityCheck && EditorUtility.DisplayDialog("VFX Toolbox", "CAUTION : You are going to generate a sequence of " + newU * newV + " frames. This could take a long time to process, stall your editor, and consume a large amount of memory. Are you SURE you want to Continue?", "I know what I am doing, proceed", "Cancel")) |
|||
m_BypassSecurityCheck = true; |
|||
|
|||
if (m_BypassSecurityCheck) |
|||
{ |
|||
flipbookNumU.intValue = newU; |
|||
flipbookNumV.intValue = newV; |
|||
} |
|||
} |
|||
|
|||
Invalidate(); |
|||
hasChanged = true; |
|||
} |
|||
|
|||
return hasChanged; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|
|||
using UnityEngine; |
|||
using UnityEngine.VFXToolbox; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Color","Color Correction")] |
|||
internal class ColorCorrectionProcessor : ProcessorBase |
|||
{ |
|||
[FloatSlider(0.0f,2.0f)] |
|||
public float Brightness; |
|||
[FloatSlider(0.0f,2.0f)] |
|||
public float Contrast; |
|||
[FloatSlider(0.0f,2.0f)] |
|||
public float Saturation; |
|||
|
|||
public AnimationCurve AlphaCurve; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/ColorCorrection.shader"; |
|||
|
|||
public override string processorName => "Color Correction"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
Brightness = 1.0f; |
|||
Contrast = 1.0f; |
|||
Saturation = 1.0f; |
|||
DefaultCurve(); |
|||
} |
|||
|
|||
public void DefaultCurve() |
|||
{ |
|||
AlphaCurve = AnimationCurve.Linear(0, 0, 1, 1); |
|||
} |
|||
public override bool Process(int frame) |
|||
{ |
|||
if (m_CurveTexture == null) |
|||
{ |
|||
InitTexture(); |
|||
} |
|||
|
|||
CurveToTextureUtility.CurveToTexture(AlphaCurve, ref m_CurveTexture); |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
material.SetFloat("_Brightness", Brightness); |
|||
material.SetFloat("_Contrast", Contrast); |
|||
material.SetFloat("_Saturation", Saturation); |
|||
|
|||
material.SetTexture("_AlphaCurve", m_CurveTexture); |
|||
|
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
|
|||
private void InitTexture() |
|||
{ |
|||
m_CurveTexture = new Texture2D(256, 1, TextureFormat.RGBAHalf, false, true); |
|||
m_CurveTexture.wrapMode = TextureWrapMode.Clamp; |
|||
m_CurveTexture.filterMode = FilterMode.Bilinear; |
|||
CurveToTextureUtility.CurveToTexture(AlphaCurve, ref m_CurveTexture); |
|||
} |
|||
|
|||
Texture2D m_CurveTexture; |
|||
CurveDrawer m_CurveDrawer; |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
if (m_CurveDrawer == null) |
|||
{ |
|||
m_CurveDrawer = new CurveDrawer(null, 0.0f, 1.0f, 0.0f, 1.0f, 140, false); |
|||
m_CurveDrawer.AddCurve(serializedObject.FindProperty("AlphaCurve"), new Color(1.0f, 0.55f, 0.1f), "Alpha Curve"); |
|||
} |
|||
|
|||
if (AlphaCurve == null) |
|||
DefaultCurve(); |
|||
|
|||
var brightness = serializedObject.FindProperty("Brightness"); |
|||
var contrast = serializedObject.FindProperty("Contrast"); |
|||
var saturation = serializedObject.FindProperty("Saturation"); |
|||
var alphaCurve = serializedObject.FindProperty("AlphaCurve"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
EditorGUILayout.PropertyField(brightness, VFXToolboxGUIUtility.Get("Brightness")); |
|||
EditorGUILayout.PropertyField(contrast, VFXToolboxGUIUtility.Get("Contrast")); |
|||
EditorGUILayout.PropertyField(saturation, VFXToolboxGUIUtility.Get("Saturation")); |
|||
|
|||
bool curveChanged = false; |
|||
|
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
EditorGUILayout.LabelField(VFXToolboxGUIUtility.Get("Alpha Curve"), GUILayout.Width(EditorGUIUtility.labelWidth)); |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Reset"))) |
|||
{ |
|||
alphaCurve.animationCurveValue = AnimationCurve.Linear(0, 0, 1, 1); |
|||
m_CurveDrawer.ClearSelection(); |
|||
curveChanged = true; |
|||
} |
|||
} |
|||
if (!curveChanged) |
|||
curveChanged = m_CurveDrawer.OnGUILayout(); |
|||
|
|||
if (EditorGUI.EndChangeCheck() || curveChanged) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
|
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Common","Crop")] |
|||
internal class CropProcessor : ProcessorBase |
|||
{ |
|||
public uint Crop_Top; |
|||
public uint Crop_Bottom; |
|||
public uint Crop_Left; |
|||
public uint Crop_Right; |
|||
|
|||
public float AutoCropThreshold = 0.003f; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Crop.shader"; |
|||
|
|||
public override string processorName => "Crop"; |
|||
|
|||
public override void UpdateOutputSize() |
|||
{ |
|||
int width = (inputSequenceWidth - (int)Crop_Left) - (int)Crop_Right; |
|||
int height = (inputSequenceHeight - (int)Crop_Top) - (int)Crop_Bottom; |
|||
SetOutputSize(width, height); |
|||
} |
|||
|
|||
public override void Default() |
|||
{ |
|||
Crop_Top = 0; |
|||
Crop_Bottom = 0; |
|||
Crop_Left = 0; |
|||
Crop_Right = 0; |
|||
AutoCropThreshold = 0.003f; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Texture texture = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", texture); |
|||
material.SetVector("_CropFactors", new Vector4( |
|||
(float)Crop_Left / texture.width, |
|||
(float)Crop_Right / texture.width, |
|||
(float)Crop_Top / texture.height, |
|||
(float)Crop_Bottom / texture.height |
|||
)); |
|||
|
|||
ProcessFrame(frame, texture); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
int sourceWidth = inputSequenceWidth; |
|||
int sourceHeight = inputSequenceHeight; |
|||
|
|||
var crop_top = serializedObject.FindProperty("Crop_Top"); |
|||
var crop_bottom = serializedObject.FindProperty("Crop_Bottom"); |
|||
var crop_left = serializedObject.FindProperty("Crop_Left"); |
|||
var crop_right = serializedObject.FindProperty("Crop_Right"); |
|||
var threshold = serializedObject.FindProperty("AutoCropThreshold"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
EditorGUILayout.IntSlider(crop_top, 0, (sourceHeight / 2) - 1, VFXToolboxGUIUtility.Get("Top")); |
|||
EditorGUILayout.IntSlider(crop_bottom, 0, (sourceHeight / 2) - 1, VFXToolboxGUIUtility.Get("Bottom")); |
|||
EditorGUILayout.IntSlider(crop_left, 0, (sourceWidth / 2) - 1, VFXToolboxGUIUtility.Get("Left")); |
|||
EditorGUILayout.IntSlider(crop_right, 0, (sourceWidth / 2) - 1, VFXToolboxGUIUtility.Get("Right")); |
|||
|
|||
GUILayout.Space(20); |
|||
GUILayout.Label(VFXToolboxGUIUtility.Get("Automatic Crop Values"), EditorStyles.boldLabel); |
|||
EditorGUI.indentLevel += 2; |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
EditorGUILayout.Slider(threshold, 0.0f, 1.0f, VFXToolboxGUIUtility.Get("Alpha Threshold")); |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Find"))) |
|||
{ |
|||
FindProperValues(threshold.floatValue, ref crop_top, ref crop_bottom, ref crop_left, ref crop_right); |
|||
GUI.changed = true; |
|||
} |
|||
} |
|||
EditorGUI.indentLevel -= 2; |
|||
|
|||
GUILayout.Space(20); |
|||
|
|||
Rect preview_rect; |
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.FlexibleSpace(); |
|||
preview_rect = GUILayoutUtility.GetRect(200, 200); |
|||
GUILayout.FlexibleSpace(); |
|||
} |
|||
|
|||
EditorGUI.DrawRect(preview_rect, new Color(0, 0, 0, 0.1f)); |
|||
|
|||
GUI.BeginClip(preview_rect); |
|||
|
|||
GUI.Label(new Rect(0, 0, 200, 16), "Preview"); |
|||
|
|||
int top = 40; |
|||
int left = 40; |
|||
int right = 160; |
|||
int bottom = 160; |
|||
|
|||
Handles.color = Color.green; |
|||
Handles.DrawLine(new Vector3(left, top), new Vector3(left, bottom)); |
|||
Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, bottom)); |
|||
Handles.DrawLine(new Vector3(right, bottom), new Vector3(right, top)); |
|||
Handles.DrawLine(new Vector3(right, top), new Vector3(left, top)); |
|||
|
|||
top = (int)(40 + 120 * (float)crop_top.intValue / sourceHeight); |
|||
bottom = (int)(160 - 120 * (float)crop_bottom.intValue / sourceHeight); |
|||
left = (int)(40 + 120 * (float)crop_left.intValue / sourceWidth); |
|||
right = (int)(160 - 120 * (float)crop_right.intValue / sourceWidth); |
|||
|
|||
Handles.color = Color.red; |
|||
|
|||
Handles.DrawLine(new Vector3(left, top), new Vector3(left, bottom)); |
|||
Handles.DrawLine(new Vector3(left, bottom), new Vector3(right, bottom)); |
|||
Handles.DrawLine(new Vector3(right, bottom), new Vector3(right, top)); |
|||
Handles.DrawLine(new Vector3(right, top), new Vector3(left, top)); |
|||
|
|||
GUI.EndClip(); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
|
|||
private void FindProperValues(float threshold, ref SerializedProperty top, ref SerializedProperty bottom, ref SerializedProperty left, ref SerializedProperty right) |
|||
{ |
|||
int width = inputSequenceWidth; |
|||
int height = inputSequenceHeight; |
|||
|
|||
int minX = width; |
|||
int maxX = 0; |
|||
int minY = height; |
|||
int maxY = 0; |
|||
|
|||
Color[] colors; |
|||
RenderTexture tempRT = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); |
|||
|
|||
for (int i = 0; i < inputSequenceLength; i++) |
|||
{ |
|||
Texture t = RequestInputTexture(i); |
|||
|
|||
VFXToolboxGUIUtility.DisplayProgressBar("Crop processor", "Evaluating closest bound (Frame #" + i + " on " + inputSequenceLength + "...)", (float)i / inputSequenceLength); |
|||
if (!isInputFrameSequence) |
|||
{ |
|||
colors = VFXToolboxUtility.ReadBack(t as RenderTexture); |
|||
} |
|||
else |
|||
{ |
|||
Graphics.Blit(t, tempRT); |
|||
colors = VFXToolboxUtility.ReadBack(tempRT); |
|||
} |
|||
|
|||
// Check frame
|
|||
for (int j = 0; j < colors.Length; j++) |
|||
{ |
|||
int x = j % width; |
|||
int y = j / width; |
|||
if (colors[j].a >= threshold) |
|||
{ |
|||
minX = Mathf.Min(minX, x); |
|||
maxX = Mathf.Max(maxX, x); |
|||
minY = Mathf.Min(minY, y); |
|||
maxY = Mathf.Max(maxY, y); |
|||
} |
|||
} |
|||
} |
|||
VFXToolboxGUIUtility.ClearProgressBar(); |
|||
|
|||
bottom.intValue = minY; |
|||
top.intValue = height - maxY - 1; |
|||
left.intValue = minX; |
|||
right.intValue = width - maxX - 1; |
|||
|
|||
RenderTexture.ReleaseTemporary(tempRT); |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
if (Event.current.type != EventType.Repaint) |
|||
return false; |
|||
|
|||
Vector2 center = canvas.CanvasToScreen(Vector2.zero); |
|||
|
|||
Vector2 topRight; |
|||
Vector2 bottomLeft; |
|||
|
|||
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2 - Crop_Right, canvas.currentFrame.texture.height / 2 + Crop_Top)); |
|||
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2 + Crop_Left, -canvas.currentFrame.texture.height / 2 - Crop_Bottom)); |
|||
|
|||
Handles.DrawSolidRectangleWithOutline(new Rect(topRight, bottomLeft - topRight), Color.clear, canvas.styles.green); |
|||
|
|||
Vector2 topRightCrop; |
|||
Vector2 bottomLeftCrop; |
|||
|
|||
topRightCrop = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2)); |
|||
bottomLeftCrop = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2)); |
|||
|
|||
Handles.DrawSolidRectangleWithOutline(new Rect(topRightCrop, bottomLeftCrop - topRightCrop), Color.clear, canvas.styles.red); |
|||
|
|||
// Arrows
|
|||
Handles.color = canvas.styles.white; |
|||
Handles.DrawLine(new Vector3(center.x, topRight.y), new Vector3(center.x, topRightCrop.y)); |
|||
Handles.DrawLine(new Vector3(center.x, bottomLeft.y), new Vector3(center.x, bottomLeftCrop.y)); |
|||
Handles.DrawLine(new Vector3(topRight.x, center.y), new Vector3(topRightCrop.x, center.y)); |
|||
Handles.DrawLine(new Vector3(bottomLeft.x, center.y), new Vector3(bottomLeftCrop.x, center.y)); |
|||
Handles.color = Color.white; |
|||
|
|||
// Texts
|
|||
GUI.Label(new Rect(center.x, topRightCrop.y - 16, 64, 16), Crop_Top.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(center.x, bottomLeftCrop.y, 64, 16), Crop_Bottom.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(topRightCrop.x, center.y, 64, 16), Crop_Right.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(bottomLeftCrop.x - 64, center.y, 64, 16), Crop_Left.ToString(), canvas.styles.miniLabelRight); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
|
|
|||
using UnityEngine; |
|||
using UnityEngine.Serialization; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("","Custom Material")] |
|||
class CustomMaterialProcessor : ProcessorBase |
|||
{ |
|||
[FormerlySerializedAs("material")] |
|||
public Material customMaterial; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Null.shader"; |
|||
|
|||
public override string processorName => "Custom Material"; |
|||
|
|||
public override string label => $"{processorName} ({((customMaterial == null) ? "Not Set" : customMaterial.name)})"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
customMaterial = null; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
|
|||
if (customMaterial != null) |
|||
{ |
|||
customMaterial.SetTexture("_InputFrame", inputFrame); |
|||
customMaterial.SetVector("_FrameData", new Vector4(inputSequenceWidth, inputSequenceHeight, frame, sequenceLength)); |
|||
customMaterial.SetVector("_FlipbookData", new Vector4(inputSequenceNumU, inputSequenceNumV, 0, 0)); |
|||
ProcessFrame(frame, inputFrame, customMaterial); |
|||
} |
|||
else |
|||
{ |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
ProcessFrame(frame, inputFrame); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
private Editor m_MaterialEditor; |
|||
private Shader m_CachedShader; |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
EditorGUI.BeginChangeCheck(); |
|||
Material mat = (Material)EditorGUILayout.ObjectField(VFXToolboxGUIUtility.Get("Material"), customMaterial, typeof(Material), false); |
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Undo.RecordObject(this, "Custom Material Change"); |
|||
customMaterial = mat; |
|||
EditorUtility.SetDirty(this); |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
if (customMaterial != null) |
|||
{ |
|||
Editor.CreateCachedEditor(customMaterial, typeof(MaterialEditor), ref m_MaterialEditor); |
|||
EditorGUI.BeginChangeCheck(); |
|||
m_MaterialEditor.DrawHeader(); |
|||
EditorGUIUtility.labelWidth = 120; |
|||
m_MaterialEditor.OnInspectorGUI(); |
|||
|
|||
if (m_CachedShader != customMaterial.shader) |
|||
{ |
|||
// Hack : we cache shader in order to track changes as DrawHeader does not consider shader change as a EditorGUI.changed
|
|||
m_CachedShader = customMaterial.shader; |
|||
GUI.changed = true; |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
} |
|||
return changed; |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Sequence","Decimate")] |
|||
internal class DecimateProcessor : ProcessorBase |
|||
{ |
|||
public ushort DecimateBy; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Null.shader"; |
|||
|
|||
public override string label => $"{processorName} (1 of {DecimateBy})"; |
|||
|
|||
public override string processorName => "Decimate"; |
|||
|
|||
public override int sequenceLength => Mathf.Max(1, (int)Mathf.Floor((float)inputSequenceLength / DecimateBy)); |
|||
|
|||
public override void Default() |
|||
{ |
|||
DecimateBy = 3; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var decimateBy = serializedObject.FindProperty("DecimateBy"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
int newDecimate = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Decimate by"), (int)DecimateBy), 2, inputSequenceLength); |
|||
|
|||
if (newDecimate != decimateBy.intValue) |
|||
{ |
|||
decimateBy.intValue = newDecimate; |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
int targetFrame = frame * DecimateBy; |
|||
Texture texture = RequestInputTexture(targetFrame); |
|||
material.SetTexture("_MainTex", texture); |
|||
ProcessFrame(frame, texture); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Sequence","Fade")] |
|||
internal class FadeProcessor : ProcessorBase |
|||
{ |
|||
public AnimationCurve FadeCurve; |
|||
public Color FadeToColor; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Fade.shader"; |
|||
|
|||
public override string processorName => "Fade"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
FadeCurve = new AnimationCurve(); |
|||
FadeCurve.AddKey(new Keyframe(0.85f, 1f)); |
|||
FadeCurve.AddKey(new Keyframe(1f, 0f)); |
|||
FadeToColor = new Color(0.25f,0.25f,0.25f,0.0f); |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
material.SetColor("_FadeToColor", FadeToColor); |
|||
material.SetFloat("_Ratio", FadeCurve.Evaluate(((float)frame) / sequenceLength)); |
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
|
|||
CurveDrawer m_CurveDrawer; |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
if (m_CurveDrawer == null) |
|||
{ |
|||
m_CurveDrawer = new CurveDrawer("Fade Curve", 0.0f, 1.0f, 0.0f, 1.0f, 140, false); |
|||
m_CurveDrawer.AddCurve(serializedObject.FindProperty("FadeCurve"), new Color(0.75f, 0.5f, 1.0f), "Fade Curve"); |
|||
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI; |
|||
} |
|||
var fadeToColor = serializedObject.FindProperty("FadeToColor"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
Color c = EditorGUILayout.ColorField(VFXToolboxGUIUtility.Get("Fade To Color"), fadeToColor.colorValue); |
|||
|
|||
if (c != fadeToColor.colorValue) |
|||
{ |
|||
fadeToColor.colorValue = c; |
|||
} |
|||
|
|||
if (m_CurveDrawer.OnGUILayout()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
|
|||
} |
|||
|
|||
void OnCurveFieldGUI(Rect renderArea, Rect curveArea) |
|||
{ |
|||
float seqRatio = -1.0f; |
|||
if (isCurrentlyPreviewed) |
|||
{ |
|||
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f; |
|||
} |
|||
|
|||
// If previewing current sequence : draw trackbar
|
|||
if (seqRatio >= 0.0f) |
|||
{ |
|||
Handles.color = Color.white; |
|||
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax)); |
|||
} |
|||
} |
|||
|
|||
bool CurveEquals(AnimationCurve target) |
|||
{ |
|||
for (int i = 0; i < target.keys.Length; i++) |
|||
{ |
|||
if (target[i].time != FadeCurve[i].time || |
|||
target[i].value != FadeCurve[i].value || |
|||
target[i].inTangent != FadeCurve[i].inTangent || |
|||
target[i].outTangent != FadeCurve[i].outTangent) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
using UnityEngine.VFXToolbox; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Common","Fix Borders")] |
|||
class FixBordersProcessor : ProcessorBase |
|||
{ |
|||
public Vector4 FixFactors; |
|||
public Color FadeToColor; |
|||
public float FadeToAlpha; |
|||
[FloatSlider(0.5f,4.0f)] |
|||
public float Exponent; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/FixBorders.shader"; |
|||
|
|||
public override string processorName => "Fix Borders"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
FixFactors = Vector4.zero; |
|||
FadeToColor = new Color(0.0f, 0.0f, 0.0f, 0.0f); |
|||
FadeToAlpha = 0.0f; |
|||
Exponent = 1.5f; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
material.SetVector("_FixFactors", FixFactors); |
|||
material.SetColor("_FadeToColor", FadeToColor); |
|||
material.SetFloat("_FadeToAlpha", FadeToAlpha); |
|||
material.SetFloat("_Exponent", Exponent); |
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var fixFactors = serializedObject.FindProperty("FixFactors"); |
|||
var fadeToColor = serializedObject.FindProperty("FadeToColor"); |
|||
var fadeToAlpha = serializedObject.FindProperty("FadeToAlpha"); |
|||
var exponent = serializedObject.FindProperty("Exponent"); |
|||
Vector4 value = fixFactors.vector4Value; |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
float left = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Left"), value.x, 0.0f, 1.0f); |
|||
float right = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Right"), value.y, 0.0f, 1.0f); |
|||
float top = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Top"), value.z, 0.0f, 1.0f); |
|||
float bottom = EditorGUILayout.Slider(VFXToolboxGUIUtility.Get("Bottom"), value.w, 0.0f, 1.0f); |
|||
|
|||
if ( |
|||
left != value.x |
|||
|| right != value.y |
|||
|| top != value.z |
|||
|| bottom != value.w |
|||
) |
|||
{ |
|||
fixFactors.vector4Value = new Vector4(left, right, top, bottom); |
|||
} |
|||
|
|||
|
|||
Color c = EditorGUILayout.ColorField(new GUIContent("Fade to Color"), fadeToColor.colorValue, true, true, true); |
|||
if (c != fadeToColor.colorValue) |
|||
{ |
|||
fadeToColor.colorValue = c; |
|||
} |
|||
|
|||
float a = EditorGUILayout.Slider("Fade to Alpha", fadeToAlpha.floatValue, 0.0f, 1.0f); |
|||
if (a != fadeToAlpha.floatValue) |
|||
{ |
|||
fadeToAlpha.floatValue = a; |
|||
} |
|||
|
|||
EditorGUILayout.PropertyField(exponent, VFXToolboxGUIUtility.Get("Exponent")); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
if (Event.current.type != EventType.Repaint) |
|||
return false; |
|||
|
|||
Vector2 center = canvas.CanvasToScreen(Vector2.zero); |
|||
|
|||
int width = canvas.currentFrame.texture.width; |
|||
int height = canvas.currentFrame.texture.height; |
|||
|
|||
// (left, right, top, bottom)
|
|||
float left = FixFactors.x * width; |
|||
float right = FixFactors.y * width; |
|||
float top = FixFactors.z * height; |
|||
float bottom = FixFactors.w * height; |
|||
|
|||
Vector2 topRight = canvas.CanvasToScreen(new Vector2(-width / 2, height / 2)); |
|||
Vector2 bottomLeft = canvas.CanvasToScreen(new Vector2(width / 2, -height / 2)); |
|||
Vector2 topRightCrop = canvas.CanvasToScreen(new Vector2(-width / 2 + right, height / 2 - top)); |
|||
Vector2 bottomLeftCrop = canvas.CanvasToScreen(new Vector2(width / 2 - left, -height / 2 + bottom)); |
|||
|
|||
// Arrows
|
|||
Handles.color = canvas.styles.green; |
|||
Handles.DrawLine(new Vector3(center.x, topRight.y), new Vector3(center.x, topRightCrop.y)); |
|||
Handles.DrawLine(new Vector3(center.x, bottomLeft.y), new Vector3(center.x, bottomLeftCrop.y)); |
|||
Handles.DrawLine(new Vector3(topRight.x, center.y), new Vector3(topRightCrop.x, center.y)); |
|||
Handles.DrawLine(new Vector3(bottomLeft.x, center.y), new Vector3(bottomLeftCrop.x, center.y)); |
|||
|
|||
// Limits
|
|||
Handles.color = canvas.styles.fadewhite; |
|||
Handles.DrawLine(new Vector3(topRight.x, topRightCrop.y), new Vector3(bottomLeft.x, topRightCrop.y)); |
|||
Handles.DrawLine(new Vector3(topRight.x, bottomLeftCrop.y), new Vector3(bottomLeft.x, bottomLeftCrop.y)); |
|||
Handles.DrawLine(new Vector3(topRightCrop.x, topRight.y), new Vector3(topRightCrop.x, bottomLeft.y)); |
|||
Handles.DrawLine(new Vector3(bottomLeftCrop.x, topRight.y), new Vector3(bottomLeftCrop.x, bottomLeft.y)); |
|||
|
|||
// Texts
|
|||
int labelwidth = 36; |
|||
GUI.color = canvas.styles.green; |
|||
GUI.Label(new Rect(center.x - labelwidth / 2, topRight.y - 20, labelwidth, 16), FixFactors.z.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(center.x - labelwidth / 2, bottomLeft.y + 4, labelwidth, 16), FixFactors.w.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(topRight.x + 4, center.y - 8, labelwidth, 16), FixFactors.y.ToString(), canvas.styles.miniLabel); |
|||
GUI.Label(new Rect(bottomLeft.x - labelwidth - 4, center.y - 8, labelwidth, 16), FixFactors.x.ToString(), canvas.styles.miniLabelRight); |
|||
|
|||
Handles.color = Color.white; |
|||
GUI.color = Color.white; |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Sequence","Loop Sequence")] |
|||
class LoopingProcessor : ProcessorBase |
|||
{ |
|||
public AnimationCurve curve; |
|||
public int syncFrame; |
|||
public int outputSequenceLength; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Blend.shader"; |
|||
|
|||
public override string processorName => "Looping"; |
|||
|
|||
public override string label => $"{processorName} ({outputSequenceLength} frame(s), Sync : {syncFrame + 1})"; |
|||
|
|||
public override int sequenceLength |
|||
{ |
|||
get |
|||
{ |
|||
if (inputSequenceLength > 0) |
|||
return outputSequenceLength; |
|||
else |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
public override void Default() |
|||
{ |
|||
curve = new AnimationCurve(); |
|||
curve.AddKey(0.25f, 0.0f); |
|||
curve.AddKey(0.75f, 1.0f); |
|||
syncFrame = 25; |
|||
outputSequenceLength = 25; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
int inputlength = inputSequenceLength; |
|||
int outputlength = sequenceLength; |
|||
|
|||
float t = (float)frame / outputlength; |
|||
|
|||
float blendFactor = Mathf.Clamp(curve.Evaluate(t), 0.0f, 1.0f); |
|||
|
|||
int Prev = Mathf.Clamp((int)Mathf.Ceil(syncFrame + frame), 0, inputlength - 1); |
|||
int Next = Mathf.Clamp((int)Mathf.Floor(syncFrame - (outputlength - frame)), 0, inputlength - 1); |
|||
|
|||
Texture prevtex = RequestInputTexture(Prev); |
|||
Texture nexttex = RequestInputTexture(Next); |
|||
|
|||
material.SetTexture("_MainTex", prevtex); |
|||
material.SetTexture("_AltTex", nexttex); |
|||
material.SetFloat("_BlendFactor", blendFactor); |
|||
|
|||
ProcessFrame(frame, prevtex); |
|||
return true; |
|||
} |
|||
|
|||
CurveDrawer m_CurveDrawer; |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var outputSequenceLength = serializedObject.FindProperty("outputSequenceLength"); |
|||
var syncFrame = serializedObject.FindProperty("syncFrame"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
int sync = syncFrame.intValue; |
|||
int newSync = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Input Sync Frame|The frame from input sequence that will be used at start and end of the output sequence."), sync, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue); |
|||
|
|||
if (newSync != sync) |
|||
{ |
|||
newSync = Mathf.Clamp(newSync, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue); |
|||
syncFrame.intValue = newSync; |
|||
} |
|||
|
|||
int length = outputSequenceLength.intValue; |
|||
int newlength = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Output Sequence Length|How many frames will be in the output sequence?"), length, 2, (inputSequenceLength / 2) + 1); |
|||
|
|||
if (newlength != length) |
|||
{ |
|||
newlength = Mathf.Min(newlength, Mathf.Max(1, (inputSequenceLength / 2))); |
|||
outputSequenceLength.intValue = newlength; |
|||
syncFrame.intValue = Mathf.Clamp(syncFrame.intValue, 0 + outputSequenceLength.intValue, inputSequenceLength - outputSequenceLength.intValue); |
|||
} |
|||
|
|||
float seqRatio = -1.0f; |
|||
if (isCurrentlyPreviewed) |
|||
{ |
|||
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f; |
|||
} |
|||
|
|||
// Draw Preview
|
|||
GUILayout.Label(VFXToolboxGUIUtility.Get("Mix Curve")); |
|||
Rect preview_rect; |
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
preview_rect = GUILayoutUtility.GetRect(200, 80); |
|||
} |
|||
|
|||
EditorGUI.DrawRect(preview_rect, new Color(0.0f, 0.0f, 0.0f, 0.25f)); |
|||
|
|||
Rect gradient_rect = new RectOffset(40, 16, 0, 16).Remove(preview_rect); |
|||
float width = gradient_rect.width; |
|||
float height = gradient_rect.height; |
|||
Color topTrackColor = new Color(1.0f, 0.8f, 0.25f); |
|||
Color bottomTrackColor = new Color(0.25f, 0.8f, 1.0f); |
|||
|
|||
using (new GUI.ClipScope(preview_rect)) |
|||
{ |
|||
GUI.color = topTrackColor; |
|||
Handles.color = topTrackColor; |
|||
GUI.Label(new Rect(0, 0, 32, 16), "In:", VFXToolboxStyles.miniLabel); |
|||
Handles.DrawLine(new Vector3(72, 8), new Vector3(width + 40 - 32, 8)); |
|||
GUI.color = bottomTrackColor; |
|||
Handles.color = bottomTrackColor; |
|||
GUI.Label(new Rect(0, height - 16, 32, 16), "In:", VFXToolboxStyles.miniLabel); |
|||
Handles.DrawLine(new Vector3(72, height - 8), new Vector3(width + 40 - 32, height - 8)); |
|||
GUI.color = Color.white; |
|||
Handles.color = Color.white; |
|||
GUI.Label(new Rect(0, height, 32, 16), "Out:", VFXToolboxStyles.miniLabel); |
|||
GUI.Label(new Rect(40, height, 32, 16), "1", VFXToolboxStyles.miniLabel); |
|||
GUI.Label(new Rect(width + 40 - 32, height, 32, 16), length.ToString(), VFXToolboxStyles.miniLabelRight); |
|||
Handles.DrawLine(new Vector3(72, height + 8), new Vector3(width + 40 - 32, height + 8)); |
|||
} |
|||
|
|||
AnimationCurve curve = serializedObject.FindProperty("curve").animationCurveValue; |
|||
using (new GUI.ClipScope(gradient_rect)) |
|||
{ |
|||
int seqLen = this.outputSequenceLength; |
|||
int syncF = syncFrame.intValue; |
|||
|
|||
float w = Mathf.Ceil((float)width / seqLen); |
|||
|
|||
for (int i = 0; i < seqLen; i++) |
|||
{ |
|||
float t = (float)i / seqLen; |
|||
Color blended = Color.Lerp(bottomTrackColor, topTrackColor, curve.Evaluate(t)); |
|||
EditorGUI.DrawRect(new Rect(i * w, 18, w, height - 36), blended); |
|||
} |
|||
|
|||
GUI.color = topTrackColor; |
|||
GUI.Label(new Rect(0, 0, 32, 16), (syncF - seqLen + 1).ToString(), VFXToolboxStyles.miniLabel); |
|||
GUI.Label(new Rect(width - 32, 0, 32, 16), (syncF).ToString(), VFXToolboxStyles.miniLabelRight); |
|||
GUI.color = bottomTrackColor; |
|||
GUI.Label(new Rect(0, height - 16, 32, 16), (syncF + 1).ToString(), VFXToolboxStyles.miniLabel); |
|||
GUI.Label(new Rect(width - 32, height - 16, 32, 16), (syncF + seqLen).ToString(), VFXToolboxStyles.miniLabelRight); |
|||
GUI.color = Color.white; |
|||
|
|||
} |
|||
|
|||
// If previewing current sequence : draw trackbar
|
|||
if (seqRatio >= 0.0f) |
|||
{ |
|||
Handles.color = Color.white; |
|||
Handles.DrawLine(new Vector3(gradient_rect.xMin + seqRatio * gradient_rect.width, preview_rect.yMin), new Vector3(gradient_rect.xMin + seqRatio * gradient_rect.width, preview_rect.yMax)); |
|||
} |
|||
|
|||
// Curve Drawer
|
|||
if (m_CurveDrawer == null) |
|||
{ |
|||
m_CurveDrawer = new CurveDrawer(null, 0.0f, 1.0f, 0.0f, 1.0f, 140, false); |
|||
m_CurveDrawer.AddCurve(serializedObject.FindProperty("curve"), new Color(0.5f, 0.75f, 1.0f), "Looping Curve"); |
|||
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI; |
|||
} |
|||
|
|||
if (m_CurveDrawer.OnGUILayout()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
|
|||
if (curve.keys.Length < 2 || curve.keys[0].value > 0.0f || curve.keys[curve.keys.Length - 1].value < 1.0f) |
|||
EditorGUILayout.HelpBox("Warning : Mix Curve must have first key's value equal 0 and last key's value equal 1 to achieve looping", MessageType.Warning); |
|||
|
|||
return changed; |
|||
|
|||
} |
|||
|
|||
bool CurveEquals(AnimationCurve target) |
|||
{ |
|||
for (int i = 0; i < target.keys.Length; i++) |
|||
{ |
|||
if (target[i].time != curve[i].time || |
|||
target[i].value != curve[i].value || |
|||
target[i].inTangent != curve[i].inTangent || |
|||
target[i].outTangent != curve[i].outTangent) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void OnCurveFieldGUI(Rect renderArea, Rect curveArea) |
|||
{ |
|||
float seqRatio = -1.0f; |
|||
if (isCurrentlyPreviewed) |
|||
{ |
|||
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f; |
|||
} |
|||
|
|||
// If previewing current sequence : draw trackbar
|
|||
if (seqRatio >= 0.0f) |
|||
{ |
|||
Handles.color = Color.white; |
|||
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax)); |
|||
} |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
int inLength = inputSequenceLength; |
|||
int outLength = sequenceLength; |
|||
int syncFrame = this.syncFrame; |
|||
|
|||
int outCurIDX = canvas.currentFrameIndex; |
|||
float outCurT = (float)outCurIDX / outLength; |
|||
int inSeqAIDX = (syncFrame - outLength) + outCurIDX; |
|||
int inSeqBIDX = syncFrame + outCurIDX; |
|||
|
|||
AnimationCurve mixCurve = curve; |
|||
float mix = mixCurve.Evaluate(outCurT); |
|||
|
|||
Color topTrackColor = canvas.styles.yellow; |
|||
Color bottomTrackColor = canvas.styles.cyan; |
|||
|
|||
Vector2 top = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width, canvas.currentFrame.texture.height) / 2); |
|||
|
|||
Rect rect = new Rect((int)top.x + 24, (int)top.y + 8, 260, 280); |
|||
EditorGUI.DrawRect(new RectOffset(8, 8, 8, 8).Add(rect), canvas.styles.backgroundPanelColor); |
|||
GUILayout.BeginArea(rect); |
|||
GUI.color = topTrackColor; |
|||
GUILayout.Label("Mix Chunk A (Input Range : " + (syncFrame - outLength + 1).ToString() + "-" + syncFrame.ToString() + ")", canvas.styles.label); |
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
Rect imgARect = GUILayoutUtility.GetRect(100, 100); |
|||
imgARect.width = 100; |
|||
imgARect.height = 100; |
|||
|
|||
GUI.color = Color.white; |
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear); |
|||
#endif
|
|||
GUI.DrawTexture(imgARect, RequestInputTexture(inSeqAIDX)); |
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = false; |
|||
#endif
|
|||
GUI.color = canvas.styles.white; |
|||
Handles.DrawSolidRectangleWithOutline(imgARect, Color.clear, topTrackColor); |
|||
|
|||
|
|||
using (new GUILayout.VerticalScope()) |
|||
{ |
|||
GUI.color = topTrackColor; |
|||
GUILayout.Label("Frame #" + (inSeqAIDX + 1).ToString(), canvas.styles.miniLabel); |
|||
GUILayout.Label("Mixed at : " + (int)(mix * 100) + "%", canvas.styles.miniLabel); |
|||
} |
|||
} |
|||
|
|||
GUILayout.Space(16); |
|||
GUI.color = bottomTrackColor; |
|||
GUILayout.Label("Mix Chunk B (Input Range : " + (syncFrame + 1).ToString() + "-" + (syncFrame + outLength).ToString() + ")", canvas.styles.label); |
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
Rect imgBRect = GUILayoutUtility.GetRect(100, 100); |
|||
imgBRect.width = 100; |
|||
imgBRect.height = 100; |
|||
|
|||
GUI.color = Color.white; |
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = (QualitySettings.activeColorSpace == ColorSpace.Linear); |
|||
#endif
|
|||
GUI.DrawTexture(imgBRect, RequestInputTexture(inSeqBIDX)); |
|||
#if !UNITY_2018_2_OR_NEWER
|
|||
GL.sRGBWrite = false; |
|||
#endif
|
|||
|
|||
GUI.color = canvas.styles.white; |
|||
Handles.DrawSolidRectangleWithOutline(imgBRect, Color.clear, bottomTrackColor); |
|||
|
|||
using (new GUILayout.VerticalScope()) |
|||
{ |
|||
GUI.color = bottomTrackColor; |
|||
GUILayout.Label("Frame #" + (inSeqBIDX + 1).ToString(), canvas.styles.miniLabel); |
|||
GUILayout.Label("Mixed at : " + (int)((1.0f - mix) * 100) + "%", canvas.styles.miniLabel); |
|||
} |
|||
} |
|||
GUI.color = Color.white; |
|||
GUILayout.EndArea(); |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Color","Premultiply Alpha")] |
|||
class PremultiplyAlphaProcessor : ProcessorBase |
|||
{ |
|||
public bool RemoveAlpha; |
|||
public float AlphaValue; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/PremultiplyAlpha.shader"; |
|||
|
|||
public override string processorName => "Premultiply Alpha"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
RemoveAlpha = false; |
|||
AlphaValue = 1.0f; |
|||
} |
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
material.SetInt("_RemoveAlpha", RemoveAlpha ? 1 : 0); |
|||
material.SetFloat("_AlphaValue", AlphaValue); |
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var removeAlpha = serializedObject.FindProperty("RemoveAlpha"); |
|||
var alphaValue = serializedObject.FindProperty("AlphaValue"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
EditorGUILayout.PropertyField(removeAlpha, VFXToolboxGUIUtility.Get("Remove Alpha")); |
|||
EditorGUI.BeginDisabledGroup(!removeAlpha.boolValue); |
|||
EditorGUILayout.PropertyField(alphaValue, VFXToolboxGUIUtility.Get("Alpha Value")); |
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Color","Remap Color")] |
|||
class RemapColorProcessor: ProcessorBase |
|||
{ |
|||
public enum RemapColorSource |
|||
{ |
|||
sRGBLuminance = 0, |
|||
LinearRGBLuminance = 1, |
|||
Alpha = 2, |
|||
LinearR = 3, |
|||
LinearG = 4, |
|||
LinearB = 5 |
|||
} |
|||
|
|||
public Gradient Gradient; |
|||
public RemapColorSource ColorSource; |
|||
|
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/RemapColor.shader"; |
|||
|
|||
public override string processorName => "Remap Color"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
ColorSource = RemapColorSource.sRGBLuminance; |
|||
DefaultGradient(); |
|||
} |
|||
|
|||
public void DefaultGradient() |
|||
{ |
|||
Gradient = new Gradient(); |
|||
GradientColorKey[] colors = new GradientColorKey[2] { new GradientColorKey(Color.black, 0),new GradientColorKey(Color.white, 1) }; |
|||
GradientAlphaKey[] alpha = new GradientAlphaKey[2] { new GradientAlphaKey(0,0), new GradientAlphaKey(1,1) }; |
|||
Gradient.SetKeys(colors, alpha); |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
if (m_GradientTexture == null) |
|||
{ |
|||
InitTexture(); |
|||
} |
|||
|
|||
CurveToTextureUtility.GradientToTexture(Gradient, ref m_GradientTexture); |
|||
Texture inputFrame = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", inputFrame); |
|||
|
|||
material.SetFloat("_Mode", (int)ColorSource); |
|||
|
|||
material.SetTexture("_Gradient", m_GradientTexture); |
|||
|
|||
ProcessFrame(frame, inputFrame); |
|||
return true; |
|||
} |
|||
|
|||
Texture2D m_GradientTexture; |
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var colorSource = serializedObject.FindProperty("ColorSource"); |
|||
var gradient = serializedObject.FindProperty("Gradient"); |
|||
EditorGUI.BeginChangeCheck(); |
|||
EditorGUILayout.PropertyField(colorSource, VFXToolboxGUIUtility.Get("Color Source")); |
|||
EditorGUILayout.PropertyField(gradient, VFXToolboxGUIUtility.Get("Remap Gradient")); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
private void InitTexture() |
|||
{ |
|||
m_GradientTexture = new Texture2D(256, 1, TextureFormat.RGBA32, false, false); |
|||
m_GradientTexture.wrapMode = TextureWrapMode.Clamp; |
|||
m_GradientTexture.filterMode = FilterMode.Bilinear; |
|||
CurveToTextureUtility.GradientToTexture(Gradient, ref m_GradientTexture); |
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Color","Remove Background")] |
|||
class RemoveBackgroundProcessor : ProcessorBase |
|||
{ |
|||
public Color BackgroundColor; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Unblend.shader"; |
|||
|
|||
public override string processorName => "Remove Background"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
BackgroundColor = new Color(0.25f,0.25f,0.25f,0.0f); |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture tex = RequestInputTexture(frame); |
|||
SetOutputSize(tex.width, tex.height); |
|||
material.SetTexture("_MainTex", tex); |
|||
material.SetColor("_BackgroundColor", BackgroundColor); |
|||
ProcessFrame(frame, tex); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var bgColor = serializedObject.FindProperty("BackgroundColor"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
EditorGUILayout.PropertyField(bgColor, VFXToolboxGUIUtility.Get("Background Color")); |
|||
if (GUILayout.Button(VFXToolboxGUIUtility.Get("Grab"), GUILayout.Width(40))) |
|||
{ |
|||
if (inputSequenceLength > 0) |
|||
{ |
|||
var texture = RequestInputTexture(0); |
|||
|
|||
Color background; |
|||
|
|||
if (texture is RenderTexture) |
|||
{ |
|||
background = VFXToolboxUtility.ReadBack((RenderTexture)texture)[0]; |
|||
} |
|||
else |
|||
{ |
|||
Texture2D inputFrame = (Texture2D)texture; |
|||
RenderTexture rt = RenderTexture.GetTemporary(inputFrame.width, inputFrame.height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); |
|||
Graphics.Blit(inputFrame, rt); |
|||
background = VFXToolboxUtility.ReadBack(rt)[0]; |
|||
RenderTexture.ReleaseTemporary(rt); |
|||
} |
|||
|
|||
if (QualitySettings.activeColorSpace == ColorSpace.Linear) |
|||
background = background.gamma; |
|||
|
|||
bgColor.colorValue = background; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
GUILayout.Space(20); |
|||
EditorGUILayout.HelpBox("Please select a color corresponding to the solid background of the flipbook to try to reconstruct the pixel's color. \n\nThis filter will only work if your flipbook was rendered on a solid color background. Try the Grab button to fetch the upper left pixel of the first frame, or use the color picker.", MessageType.Info); |
|||
|
|||
return changed; |
|||
|
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Common","Resize")] |
|||
internal class ResizeProcessor : ProcessorBase |
|||
{ |
|||
public ushort Width; |
|||
public ushort Height; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Resize.shader"; |
|||
|
|||
public override string processorName => "Resize"; |
|||
|
|||
public override string label => $"{processorName} ({Width}x{Height})"; |
|||
|
|||
public override void Default() |
|||
{ |
|||
Width = 256; |
|||
Height = 256; |
|||
} |
|||
|
|||
public override void UpdateOutputSize() |
|||
{ |
|||
SetOutputSize(Width, Height); |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
Texture texture = RequestInputTexture(frame); |
|||
Vector4 kernelAndSize = new Vector4((float)texture.width / (float)Width, (float)texture.height / (float)Height, (float)Width, (float)Height); |
|||
material.SetTexture("_MainTex", texture); |
|||
material.SetVector("_KernelAndSize", kernelAndSize); |
|||
ProcessFrame(frame, texture); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var width = serializedObject.FindProperty("Width"); |
|||
var height = serializedObject.FindProperty("Height"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
int w = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Width"), width.intValue), 1, 8192); |
|||
|
|||
if (GUILayout.Button("", EditorStyles.popup, GUILayout.Width(16))) |
|||
{ |
|||
GenericMenu menu = new GenericMenu(); |
|||
for (int s = 8192; s >= 16; s /= 2) |
|||
{ |
|||
menu.AddItem(VFXToolboxGUIUtility.Get(s.ToString()), false, |
|||
(o) => { |
|||
serializedObject.Update(); |
|||
var out_width = serializedObject.FindProperty("Width"); |
|||
out_width.intValue = (int)o; |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Invalidate(); |
|||
UpdateOutputSize(); |
|||
}, s); |
|||
} |
|||
menu.ShowAsContext(); |
|||
} |
|||
|
|||
if (w != width.intValue) |
|||
{ |
|||
width.intValue = w; |
|||
} |
|||
} |
|||
|
|||
using (new GUILayout.HorizontalScope()) |
|||
{ |
|||
int h = Mathf.Clamp(EditorGUILayout.IntField(VFXToolboxGUIUtility.Get("Height"), height.intValue), 1, 8192); |
|||
|
|||
if (GUILayout.Button("", EditorStyles.popup, GUILayout.Width(16))) |
|||
{ |
|||
GenericMenu menu = new GenericMenu(); |
|||
for (int s = 8192; s >= 16; s /= 2) |
|||
{ |
|||
menu.AddItem(VFXToolboxGUIUtility.Get(s.ToString()), false, (o) => { |
|||
serializedObject.Update(); |
|||
var out_height = serializedObject.FindProperty("Height"); |
|||
out_height.intValue = (int)o; |
|||
serializedObject.ApplyModifiedProperties(); |
|||
Invalidate(); |
|||
UpdateOutputSize(); |
|||
}, s); |
|||
} |
|||
menu.ShowAsContext(); |
|||
} |
|||
if (h != height.intValue) |
|||
{ |
|||
height.intValue = h; |
|||
} |
|||
} |
|||
|
|||
if (Mathf.Log(height.intValue, 2) % 1.0f != 0 || Mathf.Log(width.intValue, 2) % 1.0f != 0) |
|||
{ |
|||
EditorGUILayout.HelpBox("Warning: your resize resolution is not a power of two.", MessageType.Warning); |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
if (Event.current.type != EventType.Repaint) |
|||
return false; |
|||
|
|||
Vector2 center = canvas.CanvasToScreen(Vector2.zero); |
|||
|
|||
Vector2 topRight; |
|||
Vector2 bottomLeft; |
|||
|
|||
topRight = canvas.CanvasToScreen(new Vector2(-canvas.currentFrame.texture.width / 2, canvas.currentFrame.texture.height / 2)); |
|||
bottomLeft = canvas.CanvasToScreen(new Vector2(canvas.currentFrame.texture.width / 2, -canvas.currentFrame.texture.height / 2)); |
|||
|
|||
// Arrows
|
|||
Handles.color = canvas.styles.green; |
|||
Handles.DrawLine(new Vector3(topRight.x, topRight.y - 16), new Vector3(bottomLeft.x, topRight.y - 16)); |
|||
Handles.DrawLine(new Vector3(bottomLeft.x - 16, topRight.y), new Vector3(bottomLeft.x - 16, bottomLeft.y)); |
|||
Handles.color = Color.white; |
|||
|
|||
// Texts
|
|||
GUI.color = Color.green; |
|||
GUI.Label(new Rect(center.x - 32, topRight.y - 32, 64, 16), Width.ToString(), canvas.styles.miniLabelCenter); |
|||
VFXToolboxGUIUtility.GUIRotatedLabel(new Rect(bottomLeft.x - 48, center.y - 8, 64, 16), Height.ToString(), -90.0f, canvas.styles.miniLabelCenter); |
|||
GUI.color = Color.white; |
|||
return false; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Sequence","Retime")] |
|||
class RetimeProcessor : ProcessorBase |
|||
{ |
|||
public AnimationCurve curve; |
|||
public int outputSequenceLength; |
|||
public bool useCurve; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Blend.shader"; |
|||
|
|||
public override string processorName => "Retime"; |
|||
|
|||
public override string label => $"{processorName} ({outputSequenceLength} frames)"; |
|||
|
|||
public override int sequenceLength |
|||
{ |
|||
get |
|||
{ |
|||
if (inputSequenceLength > 0) |
|||
return outputSequenceLength; |
|||
else |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
public override void Default() |
|||
{ |
|||
curve = new AnimationCurve(); |
|||
curve.AddKey(new Keyframe(0, 0)); |
|||
curve.AddKey(new Keyframe(1, 24)); |
|||
outputSequenceLength = 25; |
|||
useCurve = true; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
int inputlength = inputSequenceLength; |
|||
int outputlength = sequenceLength; |
|||
float t = (float)frame / outputlength; |
|||
|
|||
float Frame; |
|||
|
|||
if (useCurve) |
|||
Frame = curve.Evaluate(t); |
|||
else |
|||
Frame = t * inputlength; |
|||
|
|||
float blendFactor = Frame % 1.0f; |
|||
int Prev = Mathf.Clamp((int)Mathf.Floor(Frame), 0, inputlength - 1); |
|||
int Next = Mathf.Clamp((int)Mathf.Ceil(Frame), 0, inputlength - 1); |
|||
|
|||
Texture prevtex = RequestInputTexture(Prev); |
|||
Texture nexttex = RequestInputTexture(Next); |
|||
|
|||
material.SetTexture("_MainTex", prevtex); |
|||
material.SetTexture("_AltTex", nexttex); |
|||
material.SetFloat("_BlendFactor", blendFactor); |
|||
|
|||
ProcessFrame(frame, prevtex); |
|||
return true; |
|||
} |
|||
|
|||
CurveDrawer m_CurveDrawer; |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
if (m_CurveDrawer == null) |
|||
{ |
|||
m_CurveDrawer = new CurveDrawer("Retime Curve", 0.0f, 1.0f, 0.0f, inputSequenceLength, 140, false); |
|||
m_CurveDrawer.AddCurve(serializedObject.FindProperty("curve"), new Color(0.5f, 0.75f, 1.0f), "Retime Curve"); |
|||
m_CurveDrawer.OnPostGUI = OnCurveFieldGUI; |
|||
} |
|||
|
|||
var useCurve = serializedObject.FindProperty("useCurve"); |
|||
var outputSequenceLength = serializedObject.FindProperty("outputSequenceLength"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
int length = outputSequenceLength.intValue; |
|||
int newlength = EditorGUILayout.IntSlider(VFXToolboxGUIUtility.Get("Sequence Length"), length, 1, inputSequenceLength); |
|||
if (newlength != length) |
|||
{ |
|||
outputSequenceLength.intValue = newlength; |
|||
} |
|||
|
|||
EditorGUILayout.PropertyField(useCurve, VFXToolboxGUIUtility.Get("Use Retiming Curve")); |
|||
|
|||
if (useCurve.boolValue) |
|||
{ |
|||
m_CurveDrawer.SetBounds(new Rect(0, 0, 1, inputSequenceLength - 1)); |
|||
|
|||
if (m_CurveDrawer.OnGUILayout()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
} |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
void OnCurveFieldGUI(Rect renderArea, Rect curveArea) |
|||
{ |
|||
float seqRatio = -1.0f; |
|||
if (isCurrentlyPreviewed) |
|||
{ |
|||
seqRatio = (previewSequenceLength > 1) ? (float)previewCurrentFrame / (previewSequenceLength - 1) : 0.0f; |
|||
} |
|||
|
|||
// If previewing current sequence : draw trackbar
|
|||
if (seqRatio >= 0.0f) |
|||
{ |
|||
Handles.color = Color.white; |
|||
Handles.DrawLine(new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMin), new Vector3(curveArea.xMin + seqRatio * curveArea.width, renderArea.yMax)); |
|||
} |
|||
} |
|||
|
|||
bool CurveEquals(AnimationCurve target) |
|||
{ |
|||
for (int i = 0; i < target.keys.Length; i++) |
|||
{ |
|||
if (target[i].time != curve[i].time || |
|||
target[i].value != curve[i].value || |
|||
target[i].inTangent != curve[i].inTangent || |
|||
target[i].outTangent != curve[i].outTangent) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace UnityEditor.Experimental.VFX.Toolbox.ImageSequencer |
|||
{ |
|||
[Processor("Common","Rotate")] |
|||
internal class RotateProcessor : ProcessorBase |
|||
{ |
|||
public enum RotateMode |
|||
{ |
|||
None = 0, |
|||
Rotate90 = 1, |
|||
Rotate180 = 2, |
|||
Rotate270 = 3 |
|||
} |
|||
|
|||
public RotateMode FrameRotateMode; |
|||
|
|||
public override string shaderPath => "Packages/com.unity.vfx-toolbox/Editor/ImageSequencer/Shaders/Rotate.shader"; |
|||
|
|||
public override string processorName => "Rotate"; |
|||
|
|||
public override string label => $"{processorName} ({FrameRotateMode})"; |
|||
|
|||
public override int numU => (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180) ? base.numU : base.numV; |
|||
|
|||
public override int numV => (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180) ? base.numV : base.numU; |
|||
|
|||
public override void UpdateOutputSize() |
|||
{ |
|||
if (FrameRotateMode == RotateMode.None || FrameRotateMode == RotateMode.Rotate180) |
|||
SetOutputSize(inputSequenceWidth, inputSequenceHeight); |
|||
else |
|||
SetOutputSize(inputSequenceHeight, inputSequenceWidth); |
|||
} |
|||
|
|||
public override void Default() |
|||
{ |
|||
FrameRotateMode = 0; |
|||
} |
|||
|
|||
public override bool Process(int frame) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Texture texture = RequestInputTexture(frame); |
|||
material.SetTexture("_MainTex", texture); |
|||
material.SetInt("_Mode", (int)FrameRotateMode); |
|||
ProcessFrame(frame, texture); |
|||
return true; |
|||
} |
|||
|
|||
public override bool OnInspectorGUI(bool changed, SerializedObject serializedObject) |
|||
{ |
|||
var rotatemode = serializedObject.FindProperty("FrameRotateMode"); |
|||
|
|||
EditorGUI.BeginChangeCheck(); |
|||
|
|||
EditorGUILayout.PropertyField(rotatemode, VFXToolboxGUIUtility.Get("Rotation Mode")); |
|||
|
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
UpdateOutputSize(); |
|||
Invalidate(); |
|||
changed = true; |
|||
} |
|||
|
|||
return changed; |
|||
} |
|||
|
|||
public override bool OnCanvasGUI(ImageSequencerCanvas canvas) |
|||
{ |
|||
Vector2 pos = canvas.CanvasToScreen(Vector2.zero + (new Vector2(canvas.currentFrame.texture.width, canvas.currentFrame.texture.height) / 2)); |
|||
Rect r = new Rect(pos.x, pos.y - 16, 150, 16); |
|||
GUI.Label(r, VFXToolboxGUIUtility.Get($"Rotation : {ObjectNames.NicifyVariableName(FrameRotateMode.ToString())}")); |
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 39609299c2088084c8e7f2837973b90b |
|||
TextureImporter: |
|||
internalIDToNameTable: [] |
|||
externalObjects: {} |
|||
serializedVersion: 10 |
|||
mipmaps: |
|||
mipMapMode: 0 |
|||
enableMipMap: 0 |
|||
sRGBTexture: 1 |
|||
linearTexture: 0 |
|||
fadeOut: 0 |
|||
borderMipMap: 0 |
|||
mipMapsPreserveCoverage: 0 |
|||
alphaTestReferenceValue: 0.5 |
|||
mipMapFadeDistanceStart: 1 |
|||
mipMapFadeDistanceEnd: 3 |
|||
bumpmap: |
|||
convertToNormalMap: 0 |
|||
externalNormalMap: 0 |
|||
heightScale: 0.25 |
|||
normalMapFilter: 0 |
|||
isReadable: 0 |
|||
streamingMipmaps: 0 |
|||
streamingMipmapsPriority: 0 |
|||
grayScaleToAlpha: 0 |
|||
generateCubemap: 6 |
|||
cubemapConvolution: 0 |
|||
seamlessCubemap: 0 |
|||
textureFormat: 1 |
|||
maxTextureSize: 2048 |
|||
textureSettings: |
|||
serializedVersion: 2 |
|||
filterMode: -1 |
|||
aniso: 1 |
|||
mipBias: -100 |
|||
wrapU: 1 |
|||
wrapV: 1 |
|||
wrapW: -1 |
|||
nPOTScale: 0 |
|||
lightmap: 0 |
|||
compressionQuality: 50 |
|||
spriteMode: 0 |
|||
spriteExtrude: 1 |
|||
spriteMeshType: 1 |
|||
alignment: 0 |
|||
spritePivot: {x: 0.5, y: 0.5} |
|||
spritePixelsToUnits: 100 |
|||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
|||
spriteGenerateFallbackPhysicsShape: 1 |
|||
alphaUsage: 1 |
|||
alphaIsTransparency: 1 |
|||
spriteTessellationDetail: -1 |
|||
textureType: 2 |
|||
textureShape: 1 |
|||
singleChannelComponent: 0 |
|||
maxTextureSizeSet: 0 |
|||
compressionQualitySet: 0 |
|||
textureFormatSet: 0 |
|||
platformSettings: |
|||
- serializedVersion: 3 |
|||
buildTarget: DefaultTexturePlatform |
|||
maxTextureSize: 2048 |
|||
resizeAlgorithm: 0 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
androidETC2FallbackOverride: 0 |
|||
forceMaximumCompressionQuality_BC6H_BC7: 0 |
|||
- serializedVersion: 3 |
|||
buildTarget: Standalone |
|||
maxTextureSize: 2048 |
|||
resizeAlgorithm: 0 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
androidETC2FallbackOverride: 0 |
|||
forceMaximumCompressionQuality_BC6H_BC7: 0 |
|||
spriteSheet: |
|||
serializedVersion: 2 |
|||
sprites: [] |
|||
outline: [] |
|||
physicsShape: [] |
|||
bones: [] |
|||
spriteID: |
|||
internalID: 0 |
|||
vertices: [] |
|||
indices: |
|||
edges: [] |
|||
weights: [] |
|||
secondaryTextures: [] |
|||
spritePackingTag: |
|||
pSDRemoveMatte: 0 |
|||
pSDShowRemoveMatteOption: 0 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: a4b87311d85ae7d4a8800739c0a7b012 |
|||
TextureImporter: |
|||
internalIDToNameTable: [] |
|||
externalObjects: {} |
|||
serializedVersion: 10 |
|||
mipmaps: |
|||
mipMapMode: 0 |
|||
enableMipMap: 0 |
|||
sRGBTexture: 1 |
|||
linearTexture: 0 |
|||
fadeOut: 0 |
|||
borderMipMap: 0 |
|||
mipMapsPreserveCoverage: 0 |
|||
alphaTestReferenceValue: 0.5 |
|||
mipMapFadeDistanceStart: 1 |
|||
mipMapFadeDistanceEnd: 3 |
|||
bumpmap: |
|||
convertToNormalMap: 0 |
|||
externalNormalMap: 0 |
|||
heightScale: 0.25 |
|||
normalMapFilter: 0 |
|||
isReadable: 0 |
|||
streamingMipmaps: 0 |
|||
streamingMipmapsPriority: 0 |
|||
grayScaleToAlpha: 0 |
|||
generateCubemap: 6 |
|||
cubemapConvolution: 0 |
|||
seamlessCubemap: 0 |
|||
textureFormat: 1 |
|||
maxTextureSize: 2048 |
|||
textureSettings: |
|||
serializedVersion: 2 |
|||
filterMode: -1 |
|||
aniso: 1 |
|||
mipBias: -100 |
|||
wrapU: 1 |
|||
wrapV: 1 |
|||
wrapW: -1 |
|||
nPOTScale: 0 |
|||
lightmap: 0 |
|||
compressionQuality: 50 |
|||
spriteMode: 0 |
|||
spriteExtrude: 1 |
|||
spriteMeshType: 1 |
|||
alignment: 0 |
|||
spritePivot: {x: 0.5, y: 0.5} |
|||
spritePixelsToUnits: 100 |
|||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
|||
spriteGenerateFallbackPhysicsShape: 1 |
|||
alphaUsage: 1 |
|||
alphaIsTransparency: 1 |
|||
spriteTessellationDetail: -1 |
|||
textureType: 2 |
|||
textureShape: 1 |
|||
singleChannelComponent: 0 |
|||
maxTextureSizeSet: 0 |
|||
compressionQualitySet: 0 |
|||
textureFormatSet: 0 |
|||
platformSettings: |
|||
- serializedVersion: 3 |
|||
buildTarget: DefaultTexturePlatform |
|||
maxTextureSize: 2048 |
|||
resizeAlgorithm: 0 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
androidETC2FallbackOverride: 0 |
|||
forceMaximumCompressionQuality_BC6H_BC7: 0 |
|||
- serializedVersion: 3 |
|||
buildTarget: Standalone |
|||
maxTextureSize: 2048 |
|||
resizeAlgorithm: 0 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
androidETC2FallbackOverride: 0 |
|||
forceMaximumCompressionQuality_BC6H_BC7: 0 |
|||
spriteSheet: |
|||
serializedVersion: 2 |
|||
sprites: [] |
|||
outline: [] |
|||
physicsShape: [] |
|||
bones: [] |
|||
spriteID: |
|||
internalID: 0 |
|||
vertices: [] |
|||
indices: |
|||
edges: [] |
|||
weights: [] |
|||
secondaryTextures: [] |
|||
spritePackingTag: |
|||
pSDRemoveMatte: 0 |
|||
pSDShowRemoveMatteOption: 0 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: cb01cd6b53c5afb4e932d16ea337934c |
|||
folderAsset: yes |
|||
timeCreated: 1475064740 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue