diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/TextureIO/GltfTextureExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/TextureIO/GltfTextureExporter.cs index cd7df68be..c80a0fd67 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/TextureIO/GltfTextureExporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/TextureIO/GltfTextureExporter.cs @@ -21,9 +21,9 @@ namespace UniGLTF /// /// /// gltf texture index - public static int PushGltfTexture(this glTF gltf, int bufferIndex, Texture2D texture, Func getTextureBytes) + public static int PushGltfTexture(this glTF gltf, int bufferIndex, Texture2D texture, ColorSpace textureColorSpace, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes) { - var bytesWithMime = getTextureBytes(texture); + var bytesWithMime = getTextureBytes(texture, textureColorSpace); // add view var view = gltf.buffers[bufferIndex].Append(bytesWithMime.bytes, glBufferTarget.NONE); diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs index 4174e7ae2..b109dd140 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs @@ -214,12 +214,21 @@ namespace UniGLTF return false; } - public virtual void ExportExtensions(Func getTextureBytes) + public virtual void ExportExtensions(GetBytesWithMimeFromTexture2D getTextureBytes) { // do nothing } - public virtual void Export(MeshExportSettings meshExportSettings, Func useAsset, Func getTextureBytes) + /// + /// Texture2D から実際のバイト列を取得するデリゲート。 + /// + /// textureColorSpace は Texture2D をコピーする際に用いる。 + /// Texture2D 単体では、色空間を知ることができないため。 + /// 一般には、その Texture2D がアサインされる glTF のプロパティの仕様が定める色空間と一致する。 + /// + public delegate (byte[] bytes, string mime) GetBytesWithMimeFromTexture2D(Texture2D texture, ColorSpace textureColorSpace); + + public virtual void Export(MeshExportSettings meshExportSettings, Func useAsset, GetBytesWithMimeFromTexture2D getTextureBytes) { var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); @@ -362,8 +371,8 @@ namespace UniGLTF // Extension で Texture が増える場合があるので最後に呼ぶ for (int i = 0; i < TextureManager.Exported.Count; ++i) { - var unityTexture = TextureManager.Exported[i]; - glTF.PushGltfTexture(bufferIndex, unityTexture, getTextureBytes); + var (unityTexture, colorSpace) = TextureManager.Exported[i]; + glTF.PushGltfTexture(bufferIndex, unityTexture, colorSpace, getTextureBytes); } } #endregion diff --git a/Assets/UniGLTF/Tests/UniGLTF/TextureTests.cs b/Assets/UniGLTF/Tests/UniGLTF/TextureTests.cs index 2ccb579e8..43232395c 100644 --- a/Assets/UniGLTF/Tests/UniGLTF/TextureTests.cs +++ b/Assets/UniGLTF/Tests/UniGLTF/TextureTests.cs @@ -26,7 +26,7 @@ namespace UniGLTF var materialExporter = new MaterialExporter(); materialExporter.ExportMaterial(material, textureManager); - var convTex0 = textureManager.Exported[0]; + var (convTex0, colorSpace) = textureManager.Exported[0]; var sampler = TextureSamplerUtil.Export(convTex0); Assert.AreEqual(glWrap.CLAMP_TO_EDGE, sampler.wrapS); diff --git a/Assets/VRM/Runtime/IO/VRMExporter.cs b/Assets/VRM/Runtime/IO/VRMExporter.cs index ed567d485..87b02bcce 100644 --- a/Assets/VRM/Runtime/IO/VRMExporter.cs +++ b/Assets/VRM/Runtime/IO/VRMExporter.cs @@ -3,6 +3,7 @@ using System.Linq; using UniGLTF; using UniJSON; using UnityEngine; +using ColorSpace = UniGLTF.ColorSpace; namespace VRM @@ -14,7 +15,7 @@ namespace VRM return new VRMMaterialExporter(); } - public static glTF Export(MeshExportSettings configuration, GameObject go, Func useAsset, Func getTextureBytes) + public static glTF Export(MeshExportSettings configuration, GameObject go, Func useAsset, GetBytesWithMimeFromTexture2D getTextureBytes) { var gltf = new glTF(); using (var exporter = new VRMExporter(gltf)) @@ -32,7 +33,7 @@ namespace VRM gltf.extensionsUsed.Add(glTF_VRM_extensions.ExtensionName); } - public override void ExportExtensions(Func getTextureBytes) + public override void ExportExtensions(GetBytesWithMimeFromTexture2D getTextureBytes) { // avatar var animator = Copy.GetComponent(); @@ -110,7 +111,7 @@ namespace VRM VRM.meta.title = meta.Title; if (meta.Thumbnail != null) { - VRM.meta.texture = glTF.PushGltfTexture(glTF.buffers.Count - 1, meta.Thumbnail, getTextureBytes); + VRM.meta.texture = glTF.PushGltfTexture(glTF.buffers.Count - 1, meta.Thumbnail, ColorSpace.sRGB, getTextureBytes); } VRM.meta.licenseType = meta.LicenseType; diff --git a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs index 042e72e5a..da0c5b722 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Exporter.cs @@ -127,7 +127,7 @@ namespace UniVRM10 return new float[] { -v.x, v.y, v.z }; } - public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, Func getTextureBytes, VRM10MetaObject metaObject = null) + public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes, VRM10MetaObject metaObject = null) { ExportAsset(model); @@ -180,8 +180,8 @@ namespace UniVRM10 // Extension で Texture が増える場合があるので最後に呼ぶ for (int i = 0; i < m_textureExporter.Exported.Count; ++i) { - var unityTexture = m_textureExporter.Exported[i]; - Storage.Gltf.PushGltfTexture(0, unityTexture, getTextureBytes); + var (unityTexture, texColorSpace) = m_textureExporter.Exported[i]; + Storage.Gltf.PushGltfTexture(0, unityTexture, texColorSpace, getTextureBytes); } if (thumbnailTextureIndex.HasValue) @@ -775,7 +775,7 @@ namespace UniVRM10 /// /// /// - public static byte[] Export(GameObject go, Func getTextureBytes = null) + public static byte[] Export(GameObject go, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes = null) { if (getTextureBytes == null) { diff --git a/Assets/VRMShaders/GLTF/IO/Editor/AssetTextureUtil.cs b/Assets/VRMShaders/GLTF/IO/Editor/AssetTextureUtil.cs index c69ae48ed..b736e0d1c 100644 --- a/Assets/VRMShaders/GLTF/IO/Editor/AssetTextureUtil.cs +++ b/Assets/VRMShaders/GLTF/IO/Editor/AssetTextureUtil.cs @@ -2,6 +2,7 @@ using System.IO; using System.Reflection; using UnityEditor; using UnityEngine; +using ColorSpace = UniGLTF.ColorSpace; namespace VRMShaders { @@ -107,14 +108,14 @@ namespace VRMShaders return false; } - public static (byte[], string) GetTextureBytesWithMime(Texture2D texture) + public static (byte[], string) GetTextureBytesWithMime(Texture2D texture, ColorSpace colorSpace) { if (TryGetBytesWithMime(texture, out byte[] bytes, out string mime)) { return (bytes, mime); } - return TextureExporter.GetTextureBytesWithMime(texture); + return TextureExporter.GetTextureBytesWithMime(texture, colorSpace); } } } diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/TextureConverter.cs b/Assets/VRMShaders/GLTF/IO/Runtime/TextureConverter.cs index 54ac445f9..67d0eb6f0 100644 --- a/Assets/VRMShaders/GLTF/IO/Runtime/TextureConverter.cs +++ b/Assets/VRMShaders/GLTF/IO/Runtime/TextureConverter.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using UnityEngine; +using ColorSpace = UniGLTF.ColorSpace; namespace VRMShaders @@ -22,10 +23,27 @@ namespace VRMShaders } public static Texture2D CopyTexture(Texture src, TextureImportTypes textureType, Material material) + { + return CopyTexture(src, textureType.GetColorSpace(), material); + } + + public static Texture2D CopyTexture(Texture src, ColorSpace colorSpace, Material material) { Texture2D dst = null; - RenderTextureReadWrite colorSpace = textureType.GetColorSpace(); - var renderTexture = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.ARGB32, colorSpace); + RenderTextureReadWrite readWrite; + switch (colorSpace) + { + case ColorSpace.sRGB: + readWrite = RenderTextureReadWrite.sRGB; + break; + case ColorSpace.Linear: + readWrite = RenderTextureReadWrite.Linear; + break; + default: + throw new ArgumentOutOfRangeException(nameof(colorSpace), colorSpace, null); + } + + var renderTexture = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.ARGB32, readWrite); if (material != null) { @@ -36,7 +54,7 @@ namespace VRMShaders Graphics.Blit(src, renderTexture); } - dst = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false, colorSpace == RenderTextureReadWrite.Linear); + dst = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false, readWrite == RenderTextureReadWrite.Linear); dst.ReadPixels(new Rect(0, 0, src.width, src.height), 0, 0); dst.name = src.name; dst.anisoLevel = src.anisoLevel; diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/TextureExporter.cs b/Assets/VRMShaders/GLTF/IO/Runtime/TextureExporter.cs index 0a7b49f6e..8434e456e 100644 --- a/Assets/VRMShaders/GLTF/IO/Runtime/TextureExporter.cs +++ b/Assets/VRMShaders/GLTF/IO/Runtime/TextureExporter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using UnityEngine; +using ColorSpace = UniGLTF.ColorSpace; namespace VRMShaders @@ -55,7 +56,7 @@ namespace VRMShaders /// /// /// - public readonly List Exported = new List(); + public readonly List<(Texture2D, ColorSpace)> Exported = new List<(Texture2D, ColorSpace)>(); /// /// Texture の export index を得る @@ -101,7 +102,7 @@ namespace VRMShaders { texture2D = TextureConverter.CopyTexture(src, TextureImportTypes.sRGB, null); } - Exported.Add(texture2D); + Exported.Add((texture2D, ColorSpace.sRGB)); m_exportMap.Add(new ExportKey(src, ConvertTypes.None), index); return index; @@ -114,7 +115,33 @@ namespace VRMShaders /// public int ExportLinear(Texture src) { - throw new NotImplementedException(); + if (src == null) + { + return -1; + } + + var exportKey = new ExportKey(src, ConvertTypes.None); + + // search cache + if (m_exportMap.TryGetValue(exportKey, out var index)) + { + return index; + } + + index = Exported.Count; + var texture2d = src as Texture2D; + if (m_useAsset(texture2d)) + { + // do nothing + } + else + { + texture2d = TextureConverter.CopyTexture(src, TextureImportTypes.Linear, null); + } + Exported.Add((texture2d, ColorSpace.Linear)); + m_exportMap.Add(exportKey, index); + + return index; } /// @@ -147,7 +174,7 @@ namespace VRMShaders index = Exported.Count; var texture2D = OcclusionMetallicRoughnessConverter.Export(metallicSmoothTexture, smoothness, occlusionTexture); - Exported.Add(texture2D); + Exported.Add((texture2D, ColorSpace.Linear)); if (metallicSmoothTexture != null) { m_exportMap.Add(new ExportKey(metallicSmoothTexture, ConvertTypes.OcclusionMetallicRoughness), index); @@ -191,7 +218,7 @@ namespace VRMShaders texture2D = NormalConverter.Export(src); } - Exported.Add(texture2D); + Exported.Add((texture2D, ColorSpace.Linear)); m_exportMap.Add(new ExportKey(src, ConvertTypes.Normal), index); return index; @@ -200,9 +227,7 @@ namespace VRMShaders /// /// 画像のバイト列を得る /// - /// - /// - public static (byte[] bytes, string mime) GetTextureBytesWithMime(Texture2D texture) + public static (byte[] bytes, string mime) GetTextureBytesWithMime(Texture2D texture, ColorSpace colorSpace) { try { @@ -211,22 +236,38 @@ namespace VRMShaders { return (png, "image/png"); } + else + { + // 単純に EncodeToPNG できないため、コピーしてから EncodeToPNG する。 + return CopyTextureAndGetBytesWithMime(texture, colorSpace); + } } catch (ArgumentException ex) { // fail to EncodeToPng // System.ArgumentException: not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings. + // example, ".DDS" Texture object in Editor. Debug.LogWarning(ex); - - // Read/Write が許可されていない Texture2D オブジェクトはこの関数に渡されるべきではない。 - // なぜなら Texture2D の色空間は、対応する glTF プロパティ指定の色空間と一致していなければならないが - // Read/Write が許可されていない場合、その条件を守って変換することができないからである。 - // したがって、この関数に渡す前に glTF プロパティ指定の色空間を加味して Copy Texture して、それを渡すべきである。 - throw; + // 単純に EncodeToPNG できないため、コピーしてから EncodeToPNG する。 + return CopyTextureAndGetBytesWithMime(texture, colorSpace); + } + } + + private static (byte[] bytes, string mime) CopyTextureAndGetBytesWithMime(Texture2D texture, ColorSpace colorSpace) + { + var copiedTex = TextureConverter.CopyTexture(texture, colorSpace, null); + var bytes = copiedTex.EncodeToPNG(); + if (Application.isPlaying) + { + UnityEngine.Object.Destroy(copiedTex); + } + else + { + UnityEngine.Object.DestroyImmediate(copiedTex); } - throw new ArgumentException("Invalid Texture2D"); + return (bytes, "image/png"); } } } diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/TextureImportTypes.cs b/Assets/VRMShaders/GLTF/IO/Runtime/TextureImportTypes.cs index 4b928153b..c47f14a51 100644 --- a/Assets/VRMShaders/GLTF/IO/Runtime/TextureImportTypes.cs +++ b/Assets/VRMShaders/GLTF/IO/Runtime/TextureImportTypes.cs @@ -1,5 +1,6 @@ using System; using UnityEngine; +using ColorSpace = UniGLTF.ColorSpace; namespace VRMShaders { @@ -31,16 +32,16 @@ namespace VRMShaders public static class TextureImportTypesExtensions { - public static RenderTextureReadWrite GetColorSpace(this TextureImportTypes textureType) + public static ColorSpace GetColorSpace(this TextureImportTypes textureType) { switch (textureType) { case TextureImportTypes.sRGB: - return RenderTextureReadWrite.sRGB; + return ColorSpace.sRGB; case TextureImportTypes.Linear: case TextureImportTypes.StandardMap: case TextureImportTypes.NormalMap: - return RenderTextureReadWrite.Linear; + return ColorSpace.Linear; default: throw new NotImplementedException(); }