diff --git a/Runtime/Scripts/GltfImport.cs b/Runtime/Scripts/GltfImport.cs index b11dfd0b..1bf23b6c 100755 --- a/Runtime/Scripts/GltfImport.cs +++ b/Runtime/Scripts/GltfImport.cs @@ -1708,6 +1708,70 @@ void SetTextureGamma(TextureInfoBase textureInfo) } } + // Compute per-texture channel mask based on material slot references. + var textureChannels = new byte[Root.Textures.Count]; + if (Root.Materials != null) + { + const byte R = 1, G = 2, B = 4, A = 8, RG = R | G, GB = G | B, RGB = R | G | B; + void AddChannels(TextureInfoBase textureInfo, byte mask) + { + if (textureInfo is { index: >= 0 } && textureInfo.index < Root.Textures.Count) + textureChannels[textureInfo.index] |= mask; + } + + foreach (var material in Root.Materials) + { + if (material.PbrMetallicRoughness != null) + { + // baseColor: RGB, plus A unless the material is fully opaque. + var baseColorMask = RGB; + if (material.GetAlphaMode() != MaterialBase.AlphaMode.Opaque) + baseColorMask |= A; + AddChannels(material.PbrMetallicRoughness.BaseColorTexture, baseColorMask); + + // metallic-roughness: roughness=G, metallic=B (occlusion adds R when packed/ORM). + AddChannels(material.PbrMetallicRoughness.MetallicRoughnessTexture, GB); + } + AddChannels(material.OcclusionTexture, R); + AddChannels(material.NormalTexture, RG); + AddChannels(material.EmissiveTexture, RGB); + + var ext = material.Extensions; + if (ext != null) + { + if (ext.KHR_materials_pbrSpecularGlossiness != null) + { + AddChannels(ext.KHR_materials_pbrSpecularGlossiness.diffuseTexture, RGB); + AddChannels(ext.KHR_materials_pbrSpecularGlossiness.specularGlossinessTexture, RGB); + } + if (ext.KHR_materials_transmission != null) + { + // KHR_materials_transmission: transmission factor in R. + AddChannels(ext.KHR_materials_transmission.transmissionTexture, R); + } + if (ext.KHR_materials_clearcoat != null) + { + // KHR_materials_clearcoat: clearcoat=R, clearcoat roughness=G, plus a normal map. + AddChannels(ext.KHR_materials_clearcoat.clearcoatTexture, R); + AddChannels(ext.KHR_materials_clearcoat.clearcoatRoughnessTexture, G); + AddChannels(ext.KHR_materials_clearcoat.clearcoatNormalTexture, RG); + } + if (ext.KHR_materials_sheen != null) + { + // KHR_materials_sheen: sheen color=RGB, sheen roughness=A. + AddChannels(ext.KHR_materials_sheen.sheenColorTexture, RGB); + AddChannels(ext.KHR_materials_sheen.sheenRoughnessTexture, A); + } + if (ext.KHR_materials_specular != null) + { + // KHR_materials_specular: specular strength=A, specular color=RGB. + AddChannels(ext.KHR_materials_specular.specularTexture, A); + AddChannels(ext.KHR_materials_specular.specularColorTexture, RGB); + } + } + } + } + #if !UNITY_VISIONOS HashSet readableTextureIndices = null; #endif @@ -1820,7 +1884,8 @@ void SetTextureGamma(TextureInfoBase textureInfo) m_Settings.GenerateMipMaps, cancellationToken, loader, - DeferAgent + DeferAgent, + textureChannels[textureIndex] ); continue; } @@ -1848,7 +1913,8 @@ void SetTextureGamma(TextureInfoBase textureInfo) m_Settings.GenerateMipMaps, cancellationToken, defaultLoader, - DeferAgent + DeferAgent, + textureChannels[textureIndex] ); return true; } @@ -1907,7 +1973,8 @@ void SetTextureGamma(TextureInfoBase textureInfo) m_Settings.GenerateMipMaps, GetImageDataAsync(img, cancellationToken), m_Addons, - cancellationToken + cancellationToken, + textureChannels[textureIndex] ); } } diff --git a/Runtime/Scripts/ITextureImageLoader.cs b/Runtime/Scripts/ITextureImageLoader.cs index 8aac3099..42807a3d 100644 --- a/Runtime/Scripts/ITextureImageLoader.cs +++ b/Runtime/Scripts/ITextureImageLoader.cs @@ -32,6 +32,28 @@ Task LoadImage( CancellationToken cancellationToken ); + /// + /// Channel-aware variant of . + /// is a bitmask of the channels the referencing materials actually consume, so a + /// loader can pick the most efficient format. The default implementation ignores the mask and forwards to the + /// channel-unaware overload, so existing loaders keep working unchanged. + /// + /// Raw, compressed image data. + /// If true, the texture being created is in linear space. If false, it is in sRGB space. + /// If true, the resulting texture should remain readable (). + /// If true, mipmap levels should get generated. + /// Token to submit cancellation requests. The default value is None. + /// Bitmask of channels consumed by referencing materials (bit0=R..bit3=A). + /// An image texture result + Task LoadImage( + NativeArray.ReadOnly data, + bool linear, + bool readable, + bool generateMipMaps, + CancellationToken cancellationToken, + int channelMask + ) => LoadImage(data, linear, readable, generateMipMaps, cancellationToken); + /// /// Determines if this loader can load the given texture, and if so, returns the corresponding image index. /// The typical use-case is a glTF texture extension that adds support for a new image format, diff --git a/Runtime/Scripts/ImageImport.cs b/Runtime/Scripts/ImageImport.cs index 5f236ff6..21330316 100644 --- a/Runtime/Scripts/ImageImport.cs +++ b/Runtime/Scripts/ImageImport.cs @@ -24,7 +24,8 @@ internal static async Task LoadImageAsync( bool generateMipMaps, Task dataTask, ImportAddonInstanceCollection addons, - CancellationToken cancellationToken + CancellationToken cancellationToken, + int channelMask = 0xF ) { using var data = await dataTask; @@ -46,7 +47,8 @@ CancellationToken cancellationToken linear, readable, generateMipMaps, - cancellationToken); + cancellationToken, + channelMask); if (task != null) { return await task; @@ -99,7 +101,8 @@ internal static async Task LoadImageAsync( bool generateMipMaps, CancellationToken cancellationToken, ITextureImageLoader loader, - IDeferAgent deferAgent + IDeferAgent deferAgent, + int channelMask = 0xF ) { using var data = await dataTask; @@ -112,7 +115,7 @@ IDeferAgent deferAgent cancellationToken.ThrowIfCancellationRequestedWithTracking(); await Task.Yield(); } - return await loader.LoadImage(data.Data, linear, readable, generateMipMaps, cancellationToken); + return await loader.LoadImage(data.Data, linear, readable, generateMipMaps, cancellationToken, channelMask); } internal static async Task LoadDataAsync(