Hi there.
I am trying to generate Texture2DArrays in edit mode with an editor script.
I have an issue where i cannot generate a Texture2Darray from comporessed Texures with mipmaps.
Creating an array with compressed Textures works if mipmaps are disabled.
Careating an array with mipmaps enabled works if the Textures are uncompressed.
Trying to use compressed AND mipmaps results in this error:
Rebuilding mipmaps of compressed 2DArray textures is not supported
UnityEngine.Texture2DArray:Apply()
My Methods for creating the array:
using System.Linq;
using UnityEditor;
using UnityEngine;
public static class Texture2DArrayExtensions
{
public static Texture2DArray CreateTexure2DArray(TextureFormat format, params Texture2D[] tex)
{
bool usingMips = tex[0].mipmapCount > 1;
var texArray = new Texture2DArray(
tex[0].width,
tex[0].height,
tex.Length,
format,
usingMips,
false
);
texArray.anisoLevel = tex[0].anisoLevel;
texArray.filterMode = tex[0].filterMode;
texArray.wrapMode = tex[0].wrapMode;
// Go over all the textures and add to array
for (int i = 0; i < tex.Length; i++)
{
if (tex[i] != null)
texArray.CopyTextureToTextureArray(tex[i], i);
}
Debug.Log("Array is using Mips: " + usingMips);
// appaerntly apply also creates mipmaps?
texArray.Apply();
// this doesn't work on Texture2DArrays
//EditorUtility.CompressTexture(textureArray, TextureFormat.DXT5, TextureCompressionQuality.Best);
return texArray;
}
public static void CopyTextureToTextureArray(this Texture2DArray texArray, Texture2D sourceTex, int index)
{
for (int mip = 0; mip < sourceTex.mipmapCount; mip++)
Graphics.CopyTexture(sourceTex, 0, mip, texArray, index, mip);
}
public static Texture2D CreateTempTextureInFormat(this Texture2D source, TextureFormat format, bool mips)
{
Texture2D dest = new Texture2D(source.width, source.height, format, mips);
dest.hideFlags = HideFlags.HideAndDontSave;
for (int mip = 0; mip < source.mipmapCount; mip++)
{
Graphics.CopyTexture(source, 0, mip, dest, 0, mip);
if (!mips) break;
}
// apply creates mips?
dest.Apply();
EditorUtility.CompressTexture(dest, format, TextureCompressionQuality.Best);
return dest;
}
public static Texture2D[] GetTempTextures(TextureFormat format, bool mips, params Texture2D[] source)
{
return source
.Select(q => q.CreateTempTextureInFormat(format, mips))
.ToArray();
}
}
Triggering the Generation in ScriptableObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine.Serialization;
using System.Linq;
using System.IO;
using UnityEditor.Build;
[CreateAssetMenu(fileName = "new Texture2DArray Profile", menuName = "Texture2DArray Profile")]
[HideMonoScript]
public class Texture2DArrayProfile : ScriptableObject//, IPreprocessBuild
{
[SerializeField] private AviableFormats format = AviableFormats.DXT5;
[SerializeField] private bool useMipMaps = true;
[SerializeField] private Material targetMaterial;
[SerializeField] private string targetSlot = "_AlbedoMaps";
[SerializeField] private List<Texture2D> textures = new List<Texture2D>();
string targetPath
{
get
{
var pathToMe = AssetDatabase.GetAssetPath(this);
return Path.GetDirectoryName(pathToMe) + "\\" + Path.GetFileNameWithoutExtension(pathToMe) + "_GEN.asset";
}
}
[ContextMenu("Generate now")]
void GenerateNow()
{
Debug.Log("Generating " + this.name);
var tempTextures = Texture2DArrayExtensions.GetTempTextures((TextureFormat)format, useMipMaps, textures.ToArray());
var Array = Texture2DArrayExtensions.CreateTexure2DArray((TextureFormat)format, tempTextures);
foreach (var item in tempTextures)
DestroyImmediate(item);
AssetDatabase.CreateAsset(Array, targetPath);
Debug.Log("Saved asset to " + targetPath);
// load again to use asset from disk
var loaded = AssetDatabase.LoadAssetAtPath<Texture2DArray>(targetPath);
targetMaterial.SetTexture(targetSlot, loaded);
}
//public void OnPreprocessBuild(BuildTarget target, string path)
//{
// Debug.Log("PREPROCESSING TEXTUREARRAY: " + name);
// GenerateNow();
//}
public enum AviableFormats
{
ARGB32 = TextureFormat.ARGB32,
DXT1 = TextureFormat.DXT1,
DXT1Crunched = TextureFormat.DXT1Crunched,
DXT5 = TextureFormat.DXT5,
DXT5Crunched = TextureFormat.DXT5Crunched,
PVRTC_RGB2 = TextureFormat.PVRTC_RGB2,
PVRTC_RGBA2 = TextureFormat.PVRTC_RGBA2,
}
}
My results:
-
ARGB32 + mipmaps → works
-
DXT1 (original Textures are DXT1) + mipmaps →
“Rebuilding mipmaps of compressed 2DArray textures is not supported”
but array is created -
DXT1 (original Textures are DXT1), no mipmaps → works
-
DXT5 (original Textures are DXT5), no mipmaps → works
-
DXT5 (original Textures are DXT5) + mipmaps →
“Rebuilding mipmaps of compressed 2DArray textures is not supported”
but array is created
I create the temp Texures for format conversion and to make sure that formats of Texture2Ds and Texture2Darray match.
It seems like CopyTexture works only when source and dest Texures are of the same format, is this correct?
If so, how can i create a temporary texture in another format?
Using setPixels does not work on DXT1 or DXT5.
Writing all this i think it comes down to a few questions:
- How can i create a temporary Texture in a set format (e.g. DXT1 or 5, PVRTC, etc) with optional mipmaps as a copy of an existing texture asset?
I think mipmaps could also be just regenerated instead of copied over. - How can i use these to create a Texture2DArray that has the same format and mipmaps settings?
e.g. a Texture2DArray in DXT5 with mipmaps