├── .gitignore ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Documentation~ ├── TableOfContents.md ├── images │ ├── bitbuckettags.png │ ├── createnewasset.png │ ├── createnewassetwaiting.png │ ├── errorfromformat.png │ ├── inspector.png │ ├── newasset.png │ ├── newassetadd.png │ ├── newassetapply.png │ ├── packagemanegeraddgit.png │ └── packagemanegerdone.png └── index.md ├── Editor.meta ├── Editor ├── Texture2DArrayImporter.cs ├── Texture2DArrayImporter.cs.meta ├── Texture2DArrayImporterInspector.cs ├── Texture2DArrayImporterInspector.cs.meta ├── Unity.Texture2DArrayImportPipeline.Editor.asmdef └── Unity.Texture2DArrayImportPipeline.Editor.asmdef.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Shaders.meta ├── Shaders ├── Unlit-Texture2DArray.shader └── Unlit-Texture2DArray.shader.meta ├── Tests.meta ├── Tests ├── Editor.meta └── Editor │ ├── Texture2DArrayImporterTests.cs │ ├── Texture2DArrayImporterTests.cs.meta │ ├── Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef │ └── Unity.Texture2DArrayImportPipeline.Editor.Tests.asmdef.meta ├── package.json └── package.json.meta /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 93cc9556bb730f240b63e102b07efc92 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Documentation~/TableOfContents.md: -------------------------------------------------------------------------------- 1 | Please see [README](../README.md) -------------------------------------------------------------------------------- /Documentation~/images/bitbuckettags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/bitbuckettags.png -------------------------------------------------------------------------------- /Documentation~/images/createnewasset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/createnewasset.png -------------------------------------------------------------------------------- /Documentation~/images/createnewassetwaiting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/createnewassetwaiting.png -------------------------------------------------------------------------------- /Documentation~/images/errorfromformat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/errorfromformat.png -------------------------------------------------------------------------------- /Documentation~/images/inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/inspector.png -------------------------------------------------------------------------------- /Documentation~/images/newasset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/newasset.png -------------------------------------------------------------------------------- /Documentation~/images/newassetadd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/newassetadd.png -------------------------------------------------------------------------------- /Documentation~/images/newassetapply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/newassetapply.png -------------------------------------------------------------------------------- /Documentation~/images/packagemanegeraddgit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/packagemanegeraddgit.png -------------------------------------------------------------------------------- /Documentation~/images/packagemanegerdone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschraut/UnityTexture2DArrayImportPipeline/1c03fc00208f9bfc5c76ccbdb351b06c6d7ccc66/Documentation~/images/packagemanegerdone.png -------------------------------------------------------------------------------- /Documentation~/index.md: -------------------------------------------------------------------------------- 1 | Please see [README](../README.md) -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6928e63a501eb394389a3489818e682b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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: -------------------------------------------------------------------------------- 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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /Editor/Unity.Texture2DArrayImportPipeline.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e547bd6f8a19ba49b452f98af61e722 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 74df93c92e3d89b41b9c76afb1a5e381 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /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 | ![alt text](Documentation~/images/inspector.png "Custom Texture2DArray Inspector") 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://img.youtube.com/vi/tghQHb6QWnU/0.jpg)](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 | ![alt text](Documentation~/images/createnewasset.png "Create Texture2DArray asset") 88 | 89 | ![alt text](Documentation~/images/createnewassetwaiting.png "") 90 | 91 | Once you created the asset, it's selected in the Inspector. 92 | 93 | ![alt text](Documentation~/images/newasset.png "") 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 | ![alt text](Documentation~/images/newassetadd.png "") 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 | ![alt text](Documentation~/images/newassetapply.png "") 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 | ![alt text](Documentation~/images/errorfromformat.png "") 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 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 648a69268f7a4fb4f8a19d8672d5bc22 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01c3fa9e7f68f614fbfa107ef229da88 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f78f2069318e8424a93849039bebcda0 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------