├── Documentation~
├── index.md
├── TableOfContents.md
└── images
│ ├── inspector.png
│ ├── newasset.png
│ ├── newassetadd.png
│ ├── bitbuckettags.png
│ ├── createnewasset.png
│ ├── newassetapply.png
│ ├── errorfromformat.png
│ ├── packagemanegerdone.png
│ ├── createnewassetwaiting.png
│ └── packagemanegeraddgit.png
├── CHANGELOG.md.meta
├── LICENSE.md.meta
├── README.md.meta
├── package.json.meta
├── Editor.meta
├── Shaders.meta
├── Tests.meta
├── Tests
├── Editor.meta
└── Editor
│ ├── Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef.meta
│ ├── Texture2DArrayImporterTests.cs.meta
│ ├── Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef
│ └── Texture2DArrayImporterTests.cs
├── Editor
├── Unity.Texture2DArrayImportPipeline.Editor.asmdef.meta
├── Texture2DArrayImporter.cs.meta
├── Texture2DArrayImporterInspector.cs.meta
├── Unity.Texture2DArrayImportPipeline.Editor.asmdef
├── Texture2DArrayImporterInspector.cs
└── Texture2DArrayImporter.cs
├── Shaders
├── Unlit-Texture2DArray.shader.meta
└── Unlit-Texture2DArray.shader
├── .gitignore
├── LICENSE.md
├── package.json
├── CHANGELOG.md
└── README.md
/Documentation~/index.md:
--------------------------------------------------------------------------------
1 | Please see [README](../README.md)
--------------------------------------------------------------------------------
/Documentation~/TableOfContents.md:
--------------------------------------------------------------------------------
1 | Please see [README](../README.md)
--------------------------------------------------------------------------------
/Documentation~/images/inspector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/inspector.png
--------------------------------------------------------------------------------
/Documentation~/images/newasset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/newasset.png
--------------------------------------------------------------------------------
/Documentation~/images/newassetadd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/newassetadd.png
--------------------------------------------------------------------------------
/Documentation~/images/bitbuckettags.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/bitbuckettags.png
--------------------------------------------------------------------------------
/Documentation~/images/createnewasset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/createnewasset.png
--------------------------------------------------------------------------------
/Documentation~/images/newassetapply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/newassetapply.png
--------------------------------------------------------------------------------
/Documentation~/images/errorfromformat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/errorfromformat.png
--------------------------------------------------------------------------------
/Documentation~/images/packagemanegerdone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/packagemanegerdone.png
--------------------------------------------------------------------------------
/Documentation~/images/createnewassetwaiting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/createnewassetwaiting.png
--------------------------------------------------------------------------------
/Documentation~/images/packagemanegeraddgit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/HEAD/Documentation~/images/packagemanegeraddgit.png
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 93cc9556bb730f240b63e102b07efc92
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 74df93c92e3d89b41b9c76afb1a5e381
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 648a69268f7a4fb4f8a19d8672d5bc22
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f78f2069318e8424a93849039bebcda0
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6928e63a501eb394389a3489818e682b
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Shaders.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 01c3fa9e7f68f614fbfa107ef229da88
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 42585e4c698f27442a9773593cf11c4d
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Tests/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2a62a3e05799d9b478331e26f6460588
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Unity.Texture2DArrayImportPipeline.Editor.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2e547bd6f8a19ba49b452f98af61e722
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Tests/Editor/Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d966f2f5354172242a4e09737e3de57b
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Shaders/Unlit-Texture2DArray.shader.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 59a891ab7e092fd4b9054da41a3fb770
3 | ShaderImporter:
4 | externalObjects: {}
5 | defaultTextures: []
6 | nonModifiableTextures: []
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Editor/Texture2DArrayImporter.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fff0fc64c42275f4191b5fbb23975176
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Texture2DArrayImporterInspector.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: db6a928588a90b64087edaaf4233fa74
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Tests/Editor/Texture2DArrayImporterTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9e608e070c770de4a9588130caf71147
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Aa]rtifacts/
2 | [Bb]uild/
3 | [Ll]ibrary/
4 | [Oo]bj/
5 | [Tt]emp/
6 | [Ll]og/
7 | [Ll]ogs/
8 | .vs
9 | .vscode
10 | .idea
11 | .DS_Store
12 | *.aspx
13 | *.browser
14 | *.csproj
15 | *.exe
16 | *.ini
17 | *.map
18 | *.mdb
19 | *.npmrc
20 | *.pyc
21 | *.resS
22 | *.sdf
23 | *.sln
24 | *.sublime-project
25 | *.sublime-workspace
26 | *.suo
27 | *.userprefs
28 | .npmrc
29 | *.leu
30 |
--------------------------------------------------------------------------------
/Editor/Unity.Texture2DArrayImportPipeline.Editor.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Unity.Texture2DArrayImportPipeline.Editor",
3 | "references": [],
4 | "includePlatforms": [
5 | "Editor"
6 | ],
7 | "excludePlatforms": [],
8 | "allowUnsafeCode": false,
9 | "overrideReferences": false,
10 | "precompiledReferences": [],
11 | "autoReferenced": true,
12 | "defineConstraints": [],
13 | "versionDefines": [],
14 | "noEngineReferences": false
15 | }
--------------------------------------------------------------------------------
/Tests/Editor/Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Unity.Texture2DArrayImportPipeline.Editor.Tests",
3 | "references": [
4 | "GUID:2e547bd6f8a19ba49b452f98af61e722"
5 | ],
6 | "optionalUnityReferences": [
7 | "TestAssemblies"
8 | ],
9 | "includePlatforms": [
10 | "Editor"
11 | ],
12 | "excludePlatforms": [],
13 | "allowUnsafeCode": false,
14 | "overrideReferences": false,
15 | "precompiledReferences": [],
16 | "autoReferenced": true,
17 | "defineConstraints": [
18 | "UNITY_INCLUDE_TESTS"
19 | ],
20 | "versionDefines": [],
21 | "noEngineReferences": false
22 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2024 Peter Schraut (http://www.console-dev.de)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.oddworm.texture2darrayimportpipeline",
3 | "version": "1.5.0",
4 | "displayName": "Texture2DArray Import Pipeline",
5 | "description": "A texture array is a collection of same size/format/flags 2D textures that look like a single object to the GPU.\n\nUnity has no built-in import pipeline for texture arrays in Unity 2020.1 and earlier versions.\n\nThe Texture2DArray Import Pipeline package you're currently looking at tries to fill the gap of that missing import pipeline in Unity and provides an implementation that makes it easy create and modify texture arrays. Hopefully just as good as you would expect from Unity in the first place. :)",
6 | "documentationUrl": "https://github.com/pschraut/UnityTexture2DArrayImportPipeline",
7 | "unity": "2019.3",
8 | "dependencies": {
9 | "com.unity.test-framework": "1.1.3"
10 | },
11 | "keywords": [
12 | "texture2darray",
13 | "importer",
14 | "editor"
15 | ],
16 | "author": {
17 | "name": "Peter Schraut",
18 | "url": "http://console-dev.de"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Shaders/Unlit-Texture2DArray.shader:
--------------------------------------------------------------------------------
1 | // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
2 |
3 | // Unlit shader. Simplest possible textured shader.
4 | // - no lighting
5 | // - no lightmap support
6 | // - no per-material color
7 |
8 | // Modified to work with a 2DArray rather than Texture
9 |
10 | Shader "Unlit/Texture2D Array" {
11 | Properties {
12 | _MainTex ("Base (RGB)", 2DArray) = "" {}
13 | _Slice ("Slice", Int) = 0
14 | }
15 |
16 | SubShader {
17 | Tags { "RenderType"="Opaque" }
18 | LOD 100
19 |
20 | Pass {
21 | CGPROGRAM
22 | #pragma vertex vert
23 | #pragma fragment frag
24 | #pragma target 3.5
25 | #pragma multi_compile_fog
26 | #pragma multi_compile_instancing
27 | #include "UnityCG.cginc"
28 |
29 | struct appdata_t {
30 | float4 vertex : POSITION;
31 | float2 texcoord : TEXCOORD0;
32 | UNITY_VERTEX_INPUT_INSTANCE_ID
33 | };
34 |
35 | struct v2f {
36 | float4 vertex : SV_POSITION;
37 | float3 texcoord : TEXCOORD0;
38 | UNITY_FOG_COORDS(1)
39 | UNITY_VERTEX_OUTPUT_STEREO
40 | };
41 |
42 | UNITY_DECLARE_TEX2DARRAY(_MainTex);
43 | float4 _MainTex_ST;
44 | UNITY_INSTANCING_BUFFER_START(Props)
45 | UNITY_DEFINE_INSTANCED_PROP(int, _Slice)
46 | UNITY_INSTANCING_BUFFER_END(Props)
47 |
48 | v2f vert (appdata_t v)
49 | {
50 | v2f o;
51 | UNITY_SETUP_INSTANCE_ID(v);
52 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
53 | o.vertex = UnityObjectToClipPos(v.vertex);
54 | o.texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
55 | o.texcoord.z = UNITY_ACCESS_INSTANCED_PROP(Props, _Slice);
56 | UNITY_TRANSFER_FOG(o,o.vertex);
57 | return o;
58 | }
59 |
60 | fixed4 frag (v2f i) : SV_Target
61 | {
62 | fixed4 col = UNITY_SAMPLE_TEX2DARRAY(_MainTex, i.texcoord);
63 | UNITY_APPLY_FOG(i.fogCoord, col);
64 | UNITY_OPAQUE_ALPHA(col.a);
65 | return col;
66 | }
67 | ENDCG
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this package are documented in this file.
3 |
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6 |
7 | ## [1.5.0] - 2022-04-15
8 | ### Fixed
9 | - Fixed that when ```new Texture2DArray``` causes an exception in the importer, that the Texture2DArray asset is left in a broken state. Now it will create a magenta Texture2DArray instead and log an error to the console.
10 | - Perform source texture dependency registration as very first step during the import, to fix that Unity triggers a reimport of the Texture2DArray asset when changing a dependency, on an earlier failed Texture2DArray import.
11 |
12 | ## [1.4.0] - 2022-03-11
13 | After installing this update, it will trigger a reimport of all Texture2DArray assets in the project and Texture2DArray's will no longer be readable via scripts by default.
14 | ### Added
15 | - Added ability to toggle whether the Texture2DArray is readable from scripts at the expense of consuming more memory when turned on. The default is off. Thanks to Guarneri1743 for the contribution, see [PR#7](https://github.com/pschraut/UnityTexture2DArrayImportPipeline/pull/7).
16 |
17 | ### Changed
18 | - Creating a new Texture2DArray asset will now no longer be readable from scripts by default. If you want to restore the previous behavior, you need to enable the ```Read/Write Enabled``` option.
19 |
20 |
21 | ## [1.3.0] - 2021-02-21
22 | After installing this update, it will trigger a reimport of all Texture2DArray assets in the project.
23 | ### Fixed
24 | - Fixed Texture2DArray not updating its texture format when changing the build target with [Asset Import Pipeline V2](https://blogs.unity3d.com/2019/10/31/the-new-asset-import-pipeline-solid-foundation-for-speeding-up-asset-imports/) being used. Thanks to Bastien of Unity Technologies for the help (actually providing the fix/workaround). This should solve [issue #3](https://github.com/pschraut/UnityTexture2DArrayImportPipeline/issues/3).
25 |
26 |
27 | ## [1.2.0] - 2020-11-02
28 | ### Fixed
29 | - Fixed compile error in Unity 2020.2 (ScriptedImporter was moved to a different namespace)
30 | - Don't display the Texture2DArray imported object twice in the Inspector
31 |
32 |
33 | ## [1.1.0] - 2020-08-29
34 | ### Changed
35 | - Removed "partial" keyword from the Texture2DArrayImporter class.
36 |
37 | ### Fixed
38 | - Added missing reference Unity TestRunner assemblies to Texture2D Import Pipeline test assembly.
39 | - Various documentation fixes.
40 |
41 | ## [1.0.0] - 2019-09-02
42 | - First release
43 |
--------------------------------------------------------------------------------
/Tests/Editor/Texture2DArrayImporterTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Texture2D Array Importer for Unity. Copyright (c) 2019 Peter Schraut (www.console-dev.de). See LICENSE.md
3 | // https://github.com/pschraut/UnityTexture2DArrayImportPipeline
4 | //
5 | using NUnit.Framework;
6 | using UnityEditor;
7 | using UnityEngine;
8 |
9 | namespace Oddworm.EditorFramework.Tests
10 | {
11 | class Texture2DArrayImporterTests
12 | {
13 | ///
14 | /// Creates a new Texture2DArray asset and returns the asset path.
15 | ///
16 | string BeginAssetTest()
17 | {
18 | var path = AssetDatabase.GenerateUniqueAssetPath("Assets/" + string.Format("Test_Texture2DArray.{0}", Texture2DArrayImporter.kFileExtension));
19 | System.IO.File.WriteAllText(path, "");
20 | AssetDatabase.Refresh();
21 | return path;
22 | }
23 |
24 | ///
25 | /// Deletes the asset specified by path.
26 | ///
27 | /// The path returned by BeginAssetTest().
28 | void EndAssetTest(string path)
29 | {
30 | AssetDatabase.DeleteAsset(path);
31 | }
32 |
33 | [Test]
34 | public void DefaultSettings()
35 | {
36 | var path = BeginAssetTest();
37 | try
38 | {
39 | var importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
40 |
41 | Assert.AreEqual(1, importer.anisoLevel);
42 | Assert.AreEqual(FilterMode.Bilinear, importer.filterMode);
43 | Assert.AreEqual(TextureWrapMode.Repeat, importer.wrapMode);
44 | Assert.AreEqual(0, importer.textures.Length);
45 | }
46 | finally
47 | {
48 | EndAssetTest(path);
49 | }
50 | }
51 |
52 | [Test]
53 | public void ScriptingAPI_SetProperties()
54 | {
55 | var path = BeginAssetTest();
56 | try
57 | {
58 | var anisoLevel = 10;
59 | var filterMode = FilterMode.Trilinear;
60 | var wrapMode = TextureWrapMode.Mirror;
61 |
62 | var importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
63 | importer.anisoLevel = anisoLevel;
64 | importer.filterMode = filterMode;
65 | importer.wrapMode = wrapMode;
66 | EditorUtility.SetDirty(importer);
67 | importer.SaveAndReimport();
68 |
69 | // Reload importer
70 | importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
71 |
72 | Assert.AreEqual(anisoLevel, importer.anisoLevel);
73 | Assert.AreEqual(filterMode, importer.filterMode);
74 | Assert.AreEqual(wrapMode, importer.wrapMode);
75 | Assert.AreEqual(0, importer.textures.Length);
76 | }
77 | finally
78 | {
79 | EndAssetTest(path);
80 | }
81 | }
82 |
83 |
84 | [Test]
85 | public void ScriptingAPI_AddMemoryTexture()
86 | {
87 | var path = BeginAssetTest();
88 | try
89 | {
90 | System.Exception exception = null;
91 | var importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
92 | var texture = new Texture2D(64, 64, TextureFormat.RGB24, true);
93 |
94 | try
95 | {
96 | importer.textures = new Texture2D[] { texture };
97 | }
98 | catch (System.Exception e)
99 | {
100 | exception = e;
101 | }
102 | finally
103 | {
104 | Texture2D.DestroyImmediate(texture);
105 | }
106 |
107 | Assert.IsTrue(exception is System.NotSupportedException);
108 | }
109 | finally
110 | {
111 | EndAssetTest(path);
112 | }
113 | }
114 |
115 |
116 | [Test]
117 | public void ScriptingAPI_AddNullTexture()
118 | {
119 | var path = BeginAssetTest();
120 | try
121 | {
122 | System.Exception exception = null;
123 | var importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
124 |
125 | try
126 | {
127 | importer.textures = new Texture2D[] { null };
128 | }
129 | catch (System.Exception e)
130 | {
131 | exception = e;
132 | }
133 | Assert.IsTrue(exception is System.NotSupportedException);
134 | }
135 | finally
136 | {
137 | EndAssetTest(path);
138 | }
139 | }
140 |
141 | [Test]
142 | public void ScriptingAPI_SetNullArray()
143 | {
144 | var path = BeginAssetTest();
145 | try
146 | {
147 | System.Exception exception = null;
148 | var importer = (Texture2DArrayImporter)AssetImporter.GetAtPath(path);
149 |
150 | try
151 | {
152 | importer.textures = null;
153 | }
154 | catch (System.Exception e)
155 | {
156 | exception = e;
157 | }
158 | Assert.IsTrue(exception is System.NotSupportedException);
159 | }
160 | finally
161 | {
162 | EndAssetTest(path);
163 | }
164 | }
165 |
166 | [Test]
167 | public void LoadTexture2DArray()
168 | {
169 | var path = BeginAssetTest();
170 | try
171 | {
172 | var asset = AssetDatabase.LoadMainAssetAtPath(path) as Texture2DArray;
173 | Assert.IsNotNull(asset);
174 | }
175 | finally
176 | {
177 | EndAssetTest(path);
178 | }
179 | }
180 |
181 | // A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
182 | // `yield return null;` to skip a frame.
183 | //[UnityTest]
184 | //public IEnumerator Texture2DArrayImporterTestWithEnumeratorPasses()
185 | //{
186 | // // Use the Assert class to test conditions.
187 | // // Use yield to skip a frame.
188 | // yield return null;
189 | //}
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Texture2DArray Import Pipeline for Unity
2 |
3 | Unity has no built-in import pipeline for texture arrays in Unity 2020.1 and earlier versions. This is the relevant quote from their documentation:
4 | > Currently in Unity texture arrays do not have an import pipeline for them, and must be created from code, either at runtime or in editor scripts. ([link](https://docs.unity3d.com/ScriptReference/Texture2DArray.html))
5 |
6 | The Texture2DArray Import Pipeline package you're currently looking at tries to fill the gap of that missing import pipeline and provides an implementation to create and modify texture arrays.
7 |
8 | This Texture2DArray Import Pipeline is an editor extension, it does not contain code that is required at runtime.
9 |
10 | 
11 |
12 |
13 |
14 | # Installation
15 |
16 | Open in Unity Window > Package Manager, choose "Add package from git URL" and insert one of the Package URL's you can find below.
17 |
18 | ## Package URL's
19 |
20 | I recommend to right-click the URL below and choose "Copy Link" rather than selecting the text and copying it, because sometimes it copies a space at the end and the Unity Package Manager can't handle it and spits out an error when you try to add the package.
21 |
22 | Please see the ```CHANGELOG.md``` file to see what's changed in each version.
23 |
24 | | Version | Link |
25 | |----------|---------------|
26 | | 1.5.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.5.0 |
27 | | 1.4.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.4.0 |
28 | | 1.3.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.3.0 |
29 | | 1.2.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.2.0 |
30 | | 1.1.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.1.0 |
31 | | 1.0.1 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.0.1 |
32 | | 1.0.0 | https://github.com/pschraut/UnityTexture2DArrayImportPipeline.git#1.0.0 |
33 |
34 | # Credits
35 |
36 | If you find this package useful, please mention my name in your credits screen.
37 | Something like "Texture2DArray Import Pipeline by Peter Schraut" or "Thanks to Peter Schraut" would be very much appreciated.
38 |
39 |
40 | # Unity Bugs
41 | | Bug | Fixed in | Description |
42 | |-----|:--------:|-------------|
43 | | [1393786](https://issuetracker.unity3d.com/product/unity/issues/guid/1393786)|n/a|Using ```Texture Quality``` other than ```Full``` under the Unity Quality Settings (or QualitySettings.masterTextureLimit) causes the Texture2DArray to be empty when creating a new or reimporting an existing one. Basically the Texture2DArray Import Pipeline doesn't work when this setting is not "Full". This is the bug-report at Unity Technologies that represents [issue #4](https://github.com/pschraut/UnityTexture2DArrayImportPipeline/issues/4) in this repository.|
44 | | [1317998](https://forum.unity.com/threads/case-1317998-texture2darray-texture3d-compressed-texture-format-is-not-supported-on-this-platform.1066052/)|2021.2.0a14|Creating a Texture2DArray with any compressed texture format causes the editor to output "TextureFormat is not supported on this platform. Decompressing texture.", yet Unity is able to create the object with this particular texture format. At least the Inspector displays it's using that format.|
45 |
46 | # How it works
47 |
48 | ## Overview
49 |
50 | Texture2DArray Import Pipeline uses Unity's [ScriptedImporter](https://docs.unity3d.com/Manual/ScriptedImporters.html). ScriptedImporter allows to implement custom asset importers in C# for file formats not natively supported by Unity.
51 |
52 | Texture2DArray is natively supported by Unity, but has no import pipeline yet. What I created is a ScriptedImporter that handles files ending with .texture2darray as a Texture2DArray object.
53 |
54 | Combined with a custom [ScriptedImporterEditor](https://docs.unity3d.com/ScriptReference/Experimental.AssetImporters.ScriptedImporterEditor.html), I'm pretty happy with the result, as the integration feels quite smooth and you hopefully can't tell that it's not part of built-in Unity.
55 |
56 |
57 | ## Texture format and size
58 |
59 | The texture size and format of a Texture2DArray is taken from the first input texture, the texture used for slice 0. Thus the Texture2DArray asset will have the same size and format as the texture specified in slice 0. All input textures have to use the same size and format.
60 |
61 | Input textures are copied into the Texture2DArray with no modifications, thus they all need to match. If they don't match, you'll see an error why the Texture2DArray could not be created and the Texture2DArray is changed to solid magenta to indicate there is an issue that must be fixed.
62 |
63 | If you want to use different texture formats/sizes for different target platforms, this is fully supported. You just need to specify the format and size in the input textures, not in the Texture2DArray asset, as shown in the video below.
64 |
65 | [](http://www.youtube.com/watch?v=tghQHb6QWnU "")
66 |
67 |
68 | ## Dependency handling
69 |
70 | A Texture2DArray asset is built from several input textures. The contents of those input textures is copied into the Texture2DArray asset.
71 |
72 | The import pipeline registers these input textures as dependencies. This means if a texture that is used by a Texture2DArray asset changes, the Texture2DArray asset gets reimported automatically as well.
73 |
74 | It's a really neat feature, as you don't need to hit a rebuild button to update the Texture2DArray every time you change one of its input textures.
75 |
76 |
77 | ## Platform handling
78 |
79 | If you change the target platform in Unity, for example from PC Standalone to Android, Unity starts to reimport assets for that particular target platform you chose. This will also cause the Texture2DArray asset to get reimported and it will use the texture format and size of its input textures.
80 |
81 |
82 |
83 | # Create Texture2DArray asset
84 |
85 | In order to create a Texture2DArray asset, open the project window context menu via right-mouse click and choose "Create > Texture2DArray" from the menu. This will create a new Texture2DArray asset in the current directory.
86 |
87 | 
88 |
89 | 
90 |
91 | Once you created the asset, it's selected in the Inspector.
92 |
93 | 
94 |
95 | A new Texture2DArray has no input textures yet. It only contains a "dummy slice" that is solid magenta to indicate you need to add textures. The Texture2DArray at this point is already fully functional though.
96 |
97 | In order to add textures you can click the plus (+) button or just drag&drop your textures onto the list header. In the following figure, I clicked (+) three times to add three slices. As no slice has an input texture assigned yet, an error icon is shown next it.
98 |
99 | 
100 |
101 | Then add a texture to each slice and press the "Apply" button. At this point you should have a valid Texture2DArray asset as in the image below.
102 |
103 | 
104 |
105 |
106 |
107 | ## Import error
108 |
109 | If the Texture2DArray is still solid magenta after you pressed "Apply", it's very likely there was an issue with the setup. Please take a look at the Unity Console window for any import error. Open the Console from the main menu "Window > General > Console".
110 |
111 | A common reason why importing a Texture2DArray fails, is because the input textures have different sizes or formats. Every texture added to the Texture2DArray must have the same size and format, as explained earlier, otherwise it won't work.
112 |
113 | The error in the Console window would look like in the following image.
114 |
115 | 
116 |
117 | The error message should provide all the information required to fix the problem.
118 |
119 |
120 |
121 | # AssetPostprocessor Example
122 |
123 | If you want to automate a certain Texture2DArray setup every time you create a new Texture2DArray asset, you can do this using Unity's AssetPostprocessor API and hook into the OnPreprocessAsset callback to configure the texture array to your like.
124 |
125 | ```csharp
126 | using UnityEditor;
127 | using UnityEngine;
128 |
129 | class Example : AssetPostprocessor
130 | {
131 | void OnPreprocessAsset()
132 | {
133 | // If the asset has not imported yet...
134 | if (assetImporter.importSettingsMissing)
135 | {
136 | // Gets its importer and apply default settings
137 | var importer = assetImporter as Oddworm.EditorFramework.Texture2DArrayImporter;
138 | if (importer != null)
139 | {
140 | importer.filterMode = FilterMode.Point;
141 | importer.wrapMode = TextureWrapMode.Clamp;
142 | importer.anisoLevel = 0;
143 | }
144 | }
145 | }
146 | }
147 | ```
148 |
149 |
150 |
--------------------------------------------------------------------------------
/Editor/Texture2DArrayImporterInspector.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Texture2D Array Importer for Unity. Copyright (c) 2019-2024 Peter Schraut (www.console-dev.de). See LICENSE.md
3 | // https://github.com/pschraut/UnityTexture2DArrayImportPipeline
4 | //
5 | #pragma warning disable IDE1006, IDE0017, IDE0090
6 | using UnityEngine;
7 | using UnityEditor;
8 | using UnityEditorInternal;
9 |
10 | #if UNITY_2020_2_OR_NEWER
11 | using UnityEditor.AssetImporters;
12 | #else
13 | using UnityEditor.Experimental.AssetImporters;
14 | #endif
15 |
16 | namespace Oddworm.EditorFramework
17 | {
18 | [CanEditMultipleObjects]
19 | [CustomEditor(typeof(Texture2DArrayImporter), true)]
20 | class Texture2DArrayImporterInspector : ScriptedImporterEditor
21 | {
22 | class Styles
23 | {
24 | public readonly GUIStyle preButton = "RL FooterButton";
25 | public readonly Texture2D popupIcon = EditorGUIUtility.FindTexture("_Popup");
26 | public readonly Texture2D errorIcon = EditorGUIUtility.FindTexture("console.erroricon.sml");
27 | public readonly Texture2D warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
28 | public readonly GUIContent textureTypeLabel = new GUIContent("Texture Type");
29 | public readonly GUIContent textureTypeValue = new GUIContent("Texture Array");
30 | public readonly GUIContent textureShapeLabel = new GUIContent("Texture Shape");
31 | public readonly GUIContent textureShapeValue = new GUIContent("2D");
32 | public readonly GUIContent wrapModeLabel = new GUIContent("Wrap Mode", "Select how the Texture behaves when tiled.");
33 | public readonly GUIContent filterModeLabel = new GUIContent("Filter Mode", "Select how the Texture is filtered when it gets stretched by 3D transformations.");
34 | public readonly GUIContent anisoLevelLabel = new GUIContent("Aniso Level", "Increases Texture quality when viewing the Texture at a steep angle. Good for floor and ground Textures.");
35 | public readonly GUIContent readableLabel = new GUIContent("Read/Write Enabled", "Enable to be able to access the texture data from scripts.");
36 | public readonly GUIContent anisotropicFilteringDisable = new GUIContent("Anisotropic filtering is disabled for all textures in Quality Settings.");
37 | public readonly GUIContent anisotropicFilteringForceEnable = new GUIContent("Anisotropic filtering is enabled for all textures in Quality Settings.");
38 | public readonly GUIContent texturesHeaderLabel = new GUIContent("Textures", "Drag&drop one or multiple textures here to add them to the list.");
39 | public readonly GUIContent removeItemButton = new GUIContent("", EditorGUIUtility.FindTexture("Toolbar Minus"), "Remove from list.");
40 | }
41 |
42 | static Styles s_Styles;
43 | Styles styles
44 | {
45 | get
46 | {
47 | s_Styles = s_Styles ?? new Styles();
48 | return s_Styles;
49 | }
50 | }
51 |
52 | SerializedProperty m_WrapMode = null;
53 | SerializedProperty m_FilterMode = null;
54 | SerializedProperty m_AnisoLevel = null;
55 | SerializedProperty m_IsReadable = null;
56 | SerializedProperty m_Textures = null;
57 | ReorderableList m_TextureList = null;
58 |
59 | public override bool showImportedObject
60 | {
61 | get { return false; }
62 | }
63 |
64 | public override void OnEnable()
65 | {
66 | base.OnEnable();
67 |
68 | m_WrapMode = serializedObject.FindProperty("m_WrapMode");
69 | m_FilterMode = serializedObject.FindProperty("m_FilterMode");
70 | m_AnisoLevel = serializedObject.FindProperty("m_AnisoLevel");
71 | m_IsReadable = serializedObject.FindProperty("m_IsReadable");
72 | m_Textures = serializedObject.FindProperty("m_Textures");
73 |
74 | m_TextureList = new ReorderableList(serializedObject, m_Textures);
75 | m_TextureList.displayRemove = false;
76 | m_TextureList.drawElementCallback += OnDrawElement;
77 | m_TextureList.drawHeaderCallback += OnDrawHeader;
78 | }
79 |
80 | public override void OnInspectorGUI()
81 | {
82 | serializedObject.Update();
83 |
84 | // This is just some visual nonsense to make it look&feel
85 | // similar to Unity's Texture Inspector.
86 | using (new EditorGUI.DisabledGroupScope(true))
87 | {
88 | EditorGUILayout.LabelField(styles.textureTypeLabel, styles.textureTypeValue, EditorStyles.popup);
89 | EditorGUILayout.LabelField(styles.textureShapeLabel, styles.textureShapeValue, EditorStyles.popup);
90 | EditorGUILayout.Separator();
91 | }
92 |
93 | EditorGUILayout.PropertyField(m_WrapMode, styles.wrapModeLabel);
94 | EditorGUILayout.PropertyField(m_FilterMode, styles.filterModeLabel);
95 | EditorGUILayout.PropertyField(m_AnisoLevel, styles.anisoLevelLabel);
96 | EditorGUILayout.PropertyField(m_IsReadable, styles.readableLabel);
97 |
98 | // If Aniso is used, check quality settings and displays some info.
99 | // I've only added this, because Unity is doing it in the Texture Inspector as well.
100 | if (m_AnisoLevel.intValue > 1)
101 | {
102 | if (QualitySettings.anisotropicFiltering == AnisotropicFiltering.Disable)
103 | EditorGUILayout.HelpBox(styles.anisotropicFilteringDisable.text, MessageType.Info);
104 |
105 | if (QualitySettings.anisotropicFiltering == AnisotropicFiltering.ForceEnable)
106 | EditorGUILayout.HelpBox(styles.anisotropicFilteringForceEnable.text, MessageType.Info);
107 | }
108 |
109 | // Draw the reorderable texture list only if a single asset is selected.
110 | // This is to avoid issues drawing the list if it contains a different amount of textures.
111 | if (!serializedObject.isEditingMultipleObjects)
112 | {
113 | EditorGUILayout.Separator();
114 | m_TextureList.DoLayoutList();
115 | }
116 |
117 | serializedObject.ApplyModifiedProperties();
118 | ApplyRevertGUI();
119 | }
120 |
121 | void OnDrawHeader(Rect rect)
122 | {
123 | var label = rect; label.width -= 16;
124 | var popup = rect; popup.x += label.width; popup.width = 20;
125 |
126 | // Display textures list header
127 | EditorGUI.LabelField(label, styles.texturesHeaderLabel);
128 |
129 | // Show popup button to open a context menu
130 | using (new EditorGUI.DisabledGroupScope(m_Textures.hasMultipleDifferentValues))
131 | {
132 | if (GUI.Button(popup, styles.popupIcon, EditorStyles.label))
133 | ShowHeaderPopupMenu();
134 | }
135 |
136 | // Handle drag&drop on header label
137 | if (CanAcceptDragAndDrop(label))
138 | {
139 | DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
140 | if (Event.current.type == EventType.DragPerform)
141 | AcceptDragAndDrop();
142 | }
143 | }
144 |
145 | void ShowHeaderPopupMenu()
146 | {
147 | var menu = new GenericMenu();
148 |
149 | menu.AddItem(new GUIContent("Select Textures"), false, delegate ()
150 | {
151 | var importer = target as Texture2DArrayImporter;
152 | Selection.objects = importer.textures;
153 | });
154 |
155 | menu.ShowAsContext();
156 | }
157 |
158 |
159 | void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
160 | {
161 | if (m_Textures.arraySize <= index)
162 | return;
163 |
164 | rect.y += 1;
165 | rect.height -= 2;
166 |
167 | var r = rect;
168 |
169 | var importer = target as Texture2DArrayImporter;
170 | var textureProperty = m_Textures.GetArrayElementAtIndex(index);
171 |
172 | var errorMsg = importer.GetVerifyString(index);
173 | if (!string.IsNullOrEmpty(errorMsg))
174 | {
175 | r = rect;
176 | rect.width = 24;
177 | switch (importer.Verify(index))
178 | {
179 | case Texture2DArrayImporter.VerifyResult.Valid:
180 | case Texture2DArrayImporter.VerifyResult.MasterNull:
181 | break;
182 |
183 | default:
184 | EditorGUI.LabelField(rect, new GUIContent(styles.errorIcon, errorMsg));
185 | break;
186 | }
187 | rect = r;
188 | rect.width -= 24;
189 | rect.x += 24;
190 | }
191 | else
192 | {
193 |
194 | r = rect;
195 | rect.width = 24;
196 | EditorGUI.LabelField(rect, new GUIContent($"{index}", "Slice"), isFocused ? EditorStyles.whiteLabel : EditorStyles.label);
197 | rect = r;
198 | rect.width -= 24;
199 | rect.x += 24;
200 | }
201 |
202 | r = rect;
203 | rect.width -= 18;
204 | EditorGUI.BeginChangeCheck();
205 | EditorGUI.PropertyField(rect, textureProperty, GUIContent.none);
206 | if (EditorGUI.EndChangeCheck())
207 | {
208 | // We have to apply modification here, so that Texture2DArrayImporter.Verify has the just changed values
209 | serializedObject.ApplyModifiedProperties();
210 |
211 | // Make sure we assign assets that exist on disk only.
212 | // During my tests, when selecting built-in assets,
213 | // Unity reimports the texture array asset infinitely, which is probably an Unity bug.
214 | var result = importer.Verify(index);
215 | if (result == Texture2DArrayImporter.VerifyResult.NotAnAsset)
216 | {
217 | textureProperty.objectReferenceValue = null;
218 | var msg = importer.GetVerifyString(index);
219 | Debug.LogError(msg, importer);
220 | }
221 | }
222 |
223 | rect = r;
224 | rect.x += rect.width - 15;
225 | rect.y += 2;
226 | rect.width = 20;
227 | if (GUI.Button(rect, styles.removeItemButton, styles.preButton))
228 | textureProperty.DeleteCommand();
229 | }
230 |
231 | bool CanAcceptDragAndDrop(Rect rect)
232 | {
233 | if (!rect.Contains(Event.current.mousePosition))
234 | return false;
235 |
236 | foreach (var obj in DragAndDrop.objectReferences)
237 | {
238 | var tex2d = obj as Texture2D;
239 | if (tex2d != null)
240 | return true;
241 | }
242 |
243 | return false;
244 | }
245 |
246 | void AcceptDragAndDrop()
247 | {
248 | serializedObject.Update();
249 |
250 | // Add all textures from the drag&drop operation
251 | foreach (var obj in DragAndDrop.objectReferences)
252 | {
253 | var tex2d = obj as Texture2D;
254 | if (tex2d != null)
255 | {
256 | m_Textures.InsertArrayElementAtIndex(m_Textures.arraySize);
257 | var e = m_Textures.GetArrayElementAtIndex(m_Textures.arraySize - 1);
258 | e.objectReferenceValue = tex2d;
259 | }
260 | }
261 |
262 | serializedObject.ApplyModifiedProperties();
263 | DragAndDrop.AcceptDrag();
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/Editor/Texture2DArrayImporter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Texture2D Array Importer for Unity. Copyright (c) 2019-2024 Peter Schraut (www.console-dev.de). See LICENSE.md
3 | // https://github.com/pschraut/UnityTexture2DArrayImportPipeline
4 | //
5 | #pragma warning disable IDE1006, IDE0017, IDE0090
6 | using UnityEngine;
7 | using UnityEditor;
8 | using System.IO;
9 | using System.Collections.Generic;
10 |
11 | #if UNITY_2020_2_OR_NEWER
12 | using UnityEditor.AssetImporters;
13 | #else
14 | using UnityEditor.Experimental.AssetImporters;
15 | #endif
16 |
17 | namespace Oddworm.EditorFramework
18 | {
19 | [CanEditMultipleObjects]
20 | [HelpURL("https://docs.unity3d.com/Manual/SL-TextureArrays.html")]
21 | [ScriptedImporter(k_VersionNumber, Texture2DArrayImporter.kFileExtension)]
22 | public class Texture2DArrayImporter : ScriptedImporter
23 | {
24 | [Tooltip("Selects how the Texture behaves when tiled.")]
25 | [SerializeField]
26 | TextureWrapMode m_WrapMode = TextureWrapMode.Repeat;
27 |
28 | [Tooltip("Selects how the Texture is filtered when it gets stretched by 3D transformations.")]
29 | [SerializeField]
30 | FilterMode m_FilterMode = FilterMode.Bilinear;
31 |
32 | [Tooltip("Increases Texture quality when viewing the texture at a steep angle.\n0 = Disabled for all textures\n1 = Enabled for all textures in Quality Settings\n2..16 = Anisotropic filtering level")]
33 | [Range(0, 16)]
34 | [SerializeField]
35 | int m_AnisoLevel = 1;
36 |
37 | [SerializeField]
38 | bool m_IsReadable = false;
39 |
40 | [Tooltip("A list of textures that are added to the texture array.")]
41 | [SerializeField]
42 | List m_Textures = new List();
43 |
44 | public enum VerifyResult
45 | {
46 | Valid,
47 | Null,
48 | MasterNull,
49 | WidthMismatch,
50 | HeightMismatch,
51 | FormatMismatch,
52 | MipmapMismatch,
53 | SRGBTextureMismatch,
54 | NotAnAsset,
55 | MasterNotAnAsset,
56 | }
57 |
58 | ///
59 | /// Gets or sets the textures that are being used to create the texture array.
60 | ///
61 | public Texture2D[] textures
62 | {
63 | get { return m_Textures.ToArray(); }
64 | set
65 | {
66 | if (value == null)
67 | throw new System.NotSupportedException("'textures' must not be set to 'null'. If you want to clear the textures array, set it to a zero-sized array instead.");
68 |
69 | for (var n=0; n< value.Length; ++n)
70 | {
71 | if (value[n] == null)
72 | throw new System.NotSupportedException($"The texture at array index '{n}' must not be 'null'.");
73 |
74 | if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(value[n])))
75 | throw new System.NotSupportedException($"The texture '{value[n].name}' at array index '{n}' does not exist on disk. Only texture assets can be added.");
76 | }
77 |
78 | m_Textures = new List(value);
79 | }
80 | }
81 |
82 | ///
83 | /// Texture coordinate wrapping mode.
84 | ///
85 | public TextureWrapMode wrapMode
86 | {
87 | get { return m_WrapMode; }
88 | set { m_WrapMode = value; }
89 | }
90 |
91 | ///
92 | /// Filtering mode of the texture.
93 | ///
94 | public FilterMode filterMode
95 | {
96 | get { return m_FilterMode; }
97 | set { m_FilterMode = value; }
98 | }
99 |
100 | ///
101 | /// Anisotropic filtering level of the texture.
102 | ///
103 | public int anisoLevel
104 | {
105 | get { return m_AnisoLevel; }
106 | set { m_AnisoLevel = value; }
107 | }
108 |
109 | ///
110 | /// Set this to true if you want texture data to be readable from scripts.
111 | /// Set it to false to prevent scripts from reading texture data.
112 | ///
113 | public bool isReadable
114 | {
115 | get { return m_IsReadable; }
116 | set { m_IsReadable = value; }
117 | }
118 |
119 | ///
120 | /// The file extension used for Texture2DArray assets without leading dot.
121 | ///
122 | public const string kFileExtension = "texture2darray";
123 |
124 | #if UNITY_2020_1_OR_NEWER
125 | const int k_VersionNumber = 202011;
126 | #else
127 | const int k_VersionNumber = 201941;
128 | #endif
129 |
130 | public override void OnImportAsset(AssetImportContext ctx)
131 | {
132 | var width = 64;
133 | var height = 64;
134 | var mipmapEnabled = true;
135 | var textureFormat = TextureFormat.ARGB32;
136 | var srgbTexture = true;
137 |
138 | // Mark all input textures as dependency to the texture array.
139 | // This causes the texture array to get re-generated when any input texture changes or when the build target changed.
140 | for (var n = 0; n < m_Textures.Count; ++n)
141 | {
142 | var source = m_Textures[n];
143 | if (source != null)
144 | {
145 | var path = AssetDatabase.GetAssetPath(source);
146 | #if UNITY_2020_1_OR_NEWER
147 | ctx.DependsOnArtifact(path);
148 | #else
149 | ctx.DependsOnSourceAsset(path);
150 | #endif
151 | }
152 | }
153 |
154 | #if !UNITY_2020_1_OR_NEWER
155 | // This value is not really used in this importer,
156 | // but getting the build target here will add a dependency to the current active buildtarget.
157 | // Because DependsOnArtifact does not exist in 2019.4, adding this dependency on top of the DependsOnSourceAsset
158 | // will force a re-import when the target platform changes in case it would have impacted any texture this importer depends on.
159 | var buildTarget = ctx.selectedBuildTarget;
160 | #endif
161 |
162 | // Check if the input textures are valid to be used to build the texture array.
163 | var isValid = Verify(ctx, false);
164 | if (isValid)
165 | {
166 | // Use the texture assigned to the first slice as "master".
167 | // This means all other textures have to use same settings as the master texture.
168 | var sourceTexture = m_Textures[0];
169 | width = sourceTexture.width;
170 | height = sourceTexture.height;
171 | textureFormat = sourceTexture.format;
172 |
173 | var sourceTexturePath = AssetDatabase.GetAssetPath(sourceTexture);
174 | var textureImporter = (TextureImporter)AssetImporter.GetAtPath(sourceTexturePath);
175 | mipmapEnabled = textureImporter.mipmapEnabled;
176 | srgbTexture = textureImporter.sRGBTexture;
177 | }
178 |
179 | Texture2DArray texture2DArray = null;
180 | try
181 | {
182 | // Create the texture array.
183 | // When the texture array asset is being created, there are no input textures added yet,
184 | // thus we do Max(1, Count) to make sure to add at least 1 slice.
185 | texture2DArray = new Texture2DArray(width, height, Mathf.Max(1, m_Textures.Count), textureFormat, mipmapEnabled, !srgbTexture);
186 | }
187 | catch (System.Exception e)
188 | {
189 | Debug.LogException(e);
190 | ctx.LogImportError($"Import failed '{ctx.assetPath}'.", ctx.mainObject);
191 |
192 | isValid = false;
193 | textureFormat = TextureFormat.RGBA32;
194 | texture2DArray = new Texture2DArray(width, height, Mathf.Max(1, m_Textures.Count), textureFormat, mipmapEnabled, !srgbTexture);
195 | }
196 |
197 | texture2DArray.wrapMode = m_WrapMode;
198 | texture2DArray.filterMode = m_FilterMode;
199 | texture2DArray.anisoLevel = m_AnisoLevel;
200 |
201 | if (isValid)
202 | {
203 | // If everything is valid, copy source textures over to the texture array.
204 | for (var n = 0; n < m_Textures.Count; ++n)
205 | {
206 | var source = m_Textures[n];
207 | Graphics.CopyTexture(source, 0, texture2DArray, n);
208 | }
209 | }
210 | else
211 | {
212 | // If there is any error, copy a magenta colored texture into every slice.
213 | // I was thinking to only make the invalid slice magenta, but then it's way less obvious that
214 | // something isn't right with the texture array. Thus I mark the entire texture array as broken.
215 | var errorTexture = new Texture2D(width, height, textureFormat, mipmapEnabled);
216 | try
217 | {
218 | var errorPixels = errorTexture.GetPixels32();
219 | for (var n = 0; n < errorPixels.Length; ++n)
220 | errorPixels[n] = Color.magenta;
221 | errorTexture.SetPixels32(errorPixels);
222 | errorTexture.Apply();
223 |
224 | for (var n = 0; n < texture2DArray.depth; ++n)
225 | Graphics.CopyTexture(errorTexture, 0, texture2DArray, n);
226 | }
227 | finally
228 | {
229 | DestroyImmediate(errorTexture);
230 | }
231 | }
232 |
233 | texture2DArray.Apply(false, !m_IsReadable);
234 | ctx.AddObjectToAsset("Texture2DArray", texture2DArray);
235 | ctx.SetMainObject(texture2DArray);
236 |
237 | if (!isValid)
238 | {
239 | // Run the verify step again, but this time we have the main object asset.
240 | // Console logs should ping the asset, but they don't in 2019.3 beta, bug?
241 | Verify(ctx, true);
242 | }
243 | }
244 |
245 | ///
246 | /// Checks if the asset is set up properly and all its dependencies are ok.
247 | ///
248 | ///
249 | /// Returns true if the asset can be imported, false otherwise.
250 | ///
251 | bool Verify(AssetImportContext ctx, bool logToConsole)
252 | {
253 | if (!SystemInfo.supports2DArrayTextures)
254 | {
255 | if (logToConsole)
256 | ctx.LogImportError($"Import failed '{ctx.assetPath}'. Your system does not support texture arrays.", ctx.mainObject);
257 |
258 | return false;
259 | }
260 |
261 | if (m_Textures.Count > 0)
262 | {
263 | if (m_Textures[0] == null)
264 | {
265 | if (logToConsole)
266 | ctx.LogImportError($"Import failed '{ctx.assetPath}'. The first element in the 'Textures' list must not be 'None'.", ctx.mainObject);
267 |
268 | return false;
269 | }
270 | }
271 |
272 | var result = m_Textures.Count > 0;
273 | for (var n = 0; n < m_Textures.Count; ++n)
274 | {
275 | var valid = Verify(n);
276 | if (valid != VerifyResult.Valid)
277 | {
278 | result = false;
279 | if (logToConsole)
280 | {
281 | var error = GetVerifyString(n);
282 | if (!string.IsNullOrEmpty(error))
283 | {
284 | var msg = $"Import failed '{ctx.assetPath}'. {error}";
285 | ctx.LogImportError(msg, ctx.mainObject);
286 | }
287 | }
288 | }
289 | }
290 |
291 | return result;
292 | }
293 |
294 | ///
295 | /// Verifies the entry in the importer at the specified slice.
296 | ///
297 | /// The texture slice. Must be an index in the 'textures' array.
298 | /// Returns the verify result.
299 | public VerifyResult Verify(int slice)
300 | {
301 | Texture2D master = (m_Textures.Count > 0) ? m_Textures[0] : null;
302 | Texture2D texture = (slice >= 0 && m_Textures.Count > slice) ? m_Textures[slice] : null;
303 |
304 | if (texture == null)
305 | return VerifyResult.Null;
306 |
307 | var textureImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;
308 | if (textureImporter == null)
309 | return VerifyResult.NotAnAsset;
310 |
311 | if (master == null)
312 | return VerifyResult.MasterNull;
313 |
314 | var masterImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(master)) as TextureImporter;
315 | if (masterImporter == null)
316 | return VerifyResult.MasterNotAnAsset;
317 |
318 | if (texture.width != master.width)
319 | return VerifyResult.WidthMismatch;
320 |
321 | if (texture.height != master.height)
322 | return VerifyResult.HeightMismatch;
323 |
324 | if (texture.format != master.format)
325 | return VerifyResult.FormatMismatch;
326 |
327 | if (texture.mipmapCount != master.mipmapCount)
328 | return VerifyResult.MipmapMismatch;
329 |
330 | if (textureImporter.sRGBTexture != masterImporter.sRGBTexture)
331 | return VerifyResult.SRGBTextureMismatch;
332 |
333 | return VerifyResult.Valid;
334 | }
335 |
336 | ///
337 | /// Verifies the entry in the importer at the specified slice.
338 | ///
339 | /// The texture slice. Must be an index in the 'textures' array.
340 | /// Returns a human readable string that specifies if anything is wrong, or an empty string if it is ok.
341 | public string GetVerifyString(int slice)
342 | {
343 | var result = Verify(slice);
344 | switch (result)
345 | {
346 | case VerifyResult.Valid:
347 | {
348 | return "";
349 | }
350 |
351 | case VerifyResult.MasterNull:
352 | {
353 | return "The texture for slice 0 must not be 'None'.";
354 | }
355 |
356 | case VerifyResult.Null:
357 | {
358 | return $"The texture for slice {slice} must not be 'None'.";
359 | }
360 |
361 | case VerifyResult.FormatMismatch:
362 | {
363 | var master = m_Textures[0];
364 | var texture = m_Textures[slice];
365 |
366 | return $"Texture '{texture.name}' uses '{texture.format}' as format, but must be using '{master.format}' instead, because the texture for slice 0 '{master.name}' is using '{master.format}' too.";
367 | }
368 |
369 | case VerifyResult.MipmapMismatch:
370 | {
371 | var master = m_Textures[0];
372 | var texture = m_Textures[slice];
373 |
374 | return $"Texture '{texture.name}' has '{texture.mipmapCount}' mipmap(s), but must have '{master.mipmapCount}' instead, because the texture for slice 0 '{master.name}' is having '{master.mipmapCount}' mipmap(s). Please check if the 'Generate Mip Maps' setting for both textures is the same.";
375 | }
376 |
377 | case VerifyResult.SRGBTextureMismatch:
378 | {
379 | var master = m_Textures[0];
380 | var texture = m_Textures[slice];
381 |
382 | return $"Texture '{texture.name}' uses different 'sRGB' setting than slice 0 texture '{master.name}'.";
383 | }
384 |
385 | case VerifyResult.WidthMismatch:
386 | case VerifyResult.HeightMismatch:
387 | {
388 | var master = m_Textures[0];
389 | var texture = m_Textures[slice];
390 |
391 | return $"Texture '{texture.name}' is {texture.width}x{texture.height} in size, but must be using the same size as the texture for slice 0 '{master.name}', which is {master.width}x{master.height}.";
392 | }
393 |
394 | case VerifyResult.MasterNotAnAsset:
395 | case VerifyResult.NotAnAsset:
396 | {
397 | var texture = m_Textures[slice];
398 |
399 | return $"Texture '{texture.name}' is not saved to disk. Only texture assets that exist on disk can be added to a Texture2DArray asset.";
400 | }
401 | }
402 |
403 | return "Unhandled validation issue.";
404 | }
405 |
406 | [MenuItem("Assets/Create/Texture2D Array", priority = 310)]
407 | static void CreateTexture2DArrayMenuItem()
408 | {
409 | // https://forum.unity.com/threads/how-to-implement-create-new-asset.759662/
410 | string directoryPath = "Assets";
411 | foreach (Object obj in Selection.GetFiltered(typeof(Object), SelectionMode.Assets))
412 | {
413 | directoryPath = AssetDatabase.GetAssetPath(obj);
414 | if (!string.IsNullOrEmpty(directoryPath) && File.Exists(directoryPath))
415 | {
416 | directoryPath = Path.GetDirectoryName(directoryPath);
417 | break;
418 | }
419 | }
420 | directoryPath = directoryPath.Replace("\\", "/");
421 | if (directoryPath.Length > 0 && directoryPath[directoryPath.Length - 1] != '/')
422 | directoryPath += "/";
423 | if (string.IsNullOrEmpty(directoryPath))
424 | directoryPath = "Assets/";
425 |
426 | var fileName = $"New Texture2DArray.{kFileExtension}";
427 | directoryPath = AssetDatabase.GenerateUniqueAssetPath(directoryPath + fileName);
428 | ProjectWindowUtil.CreateAssetWithContent(directoryPath, "This file represents a Texture2DArray asset for Unity.\nYou need the 'Texture2DArray Import Pipeline' package available at https://github.com/pschraut/UnityTexture2DArrayImportPipeline to properly import this file in Unity.");
429 | }
430 | }
431 | }
432 |
--------------------------------------------------------------------------------