// SPDX-FileCopyrightText: 2023 Unity Technologies and the glTFast authors // SPDX-License-Identifier: Apache-2.0 using System; using UnityEngine; using UnityEngine.Rendering; namespace GLTFast.Export { using Logging; using Schema; /// public abstract class MaterialExportBase : IMaterialExport { // These property IDs might be useful for developing custom IMaterialExport implementations, // thus they are public. // ReSharper disable MemberCanBeProtected.Global // ReSharper disable MemberCanBePrivate.Global /// /// _BaseColor shader property identifier /// public static readonly int BaseColorProperty = Shader.PropertyToID("_BaseColor"); /// /// _MainTex shader property identifier /// public static readonly int MainTexProperty = Shader.PropertyToID("_MainTex"); /// /// _Color shader property identifier /// public static readonly int ColorProperty = Shader.PropertyToID("_Color"); /// /// _Metallic shader property identifier /// public static readonly int MetallicProperty = Shader.PropertyToID("_Metallic"); /// /// _Smoothness shader property identifier /// public static readonly int SmoothnessProperty = Shader.PropertyToID("_Smoothness"); /// /// _Cutoff shader property identifier /// public static readonly int CutoffProperty = Shader.PropertyToID("_Cutoff"); // ReSharper restore MemberCanBeProtected.Global // ReSharper restore MemberCanBePrivate.Global /// /// Converts a Unity material into a glTF material /// /// Source material /// Resulting glTF material /// glTF to export material to. Will be used to add required texture images /// Custom logger /// True if material was converted successfully, false otherwise public abstract bool ConvertMaterial(UnityEngine.Material uMaterial, out Material material, IGltfWritable gltf, ICodeLogger logger); /// /// Applies alpha mode and cutoff /// /// Source Unity Material /// glTF material to apply settings on protected static void SetAlphaModeAndCutoff(UnityEngine.Material uMaterial, Material material) { switch (uMaterial.GetTag("RenderType", false, "")) { case "TransparentCutout": if (uMaterial.HasProperty(CutoffProperty)) { material.alphaCutoff = uMaterial.GetFloat(CutoffProperty); } material.SetAlphaMode(Material.AlphaMode.Mask); break; case "Transparent": case "Fade": material.SetAlphaMode(Material.AlphaMode.Blend); break; default: material.SetAlphaMode(Material.AlphaMode.Opaque); break; } } /// /// Retrieves whether material is double-sided. /// /// Material to analyze. /// CullMode property id. /// True if material is double-sided, false otherwise. protected static bool IsDoubleSided(UnityEngine.Material uMaterial, int cullPropId) { return uMaterial.HasProperty(cullPropId) && uMaterial.GetInt(cullPropId) == (int)CullMode.Off; } /// /// Retrieves whether material is unlit /// /// Material to analyze /// True if material uses unlit shader, false otherwise protected static bool IsUnlit(UnityEngine.Material material) { return material.shader.name.ToLowerInvariant().Contains("unlit"); } /// /// Converts an unlit Unity material into a glTF material /// /// Destination glTF material /// Source Unity material /// Main texture property ID /// Context glTF to export to /// Custom logger protected void ExportUnlit( Material material, UnityEngine.Material uMaterial, int mainTexProperty, IGltfWritable gltf, ICodeLogger logger ) { gltf.RegisterExtensionUsage(Extension.MaterialsUnlit); material.extensions = material.extensions ?? new MaterialExtensions(); material.extensions.KHR_materials_unlit = new MaterialUnlit(); var pbr = material.pbrMetallicRoughness ?? new PbrMetallicRoughness(); if (GetUnlitColor(uMaterial, out var baseColor)) { pbr.BaseColor = baseColor.linear; } if (uMaterial.HasProperty(mainTexProperty)) { var mainTex = uMaterial.GetTexture(mainTexProperty); if (mainTex != null) { if (mainTex is Texture2D) { pbr.baseColorTexture = ExportTextureInfo(mainTex, gltf); if (pbr.baseColorTexture != null) { ExportTextureTransform(pbr.baseColorTexture, uMaterial, mainTexProperty, gltf); } } else { logger?.Error(LogCode.TextureInvalidType, "main", material.name); } } } material.pbrMetallicRoughness = pbr; } /// /// Returns the color of an unlit material /// /// Unity material /// Resulting unlit color /// True if the unlit color was retrieved, false otherwise protected virtual bool GetUnlitColor(UnityEngine.Material uMaterial, out Color baseColor) { if (uMaterial.HasProperty(BaseColorProperty)) { baseColor = uMaterial.GetColor(BaseColorProperty); return true; } if (uMaterial.HasProperty(ColorProperty)) { baseColor = uMaterial.GetColor(ColorProperty); return true; } baseColor = Color.magenta; return false; } /// /// Export a Unity texture to a glTF. /// /// Texture to export. /// Context glTF to export to /// Desired image format /// glTF texture info protected static TextureInfo ExportTextureInfo( UnityEngine.Texture texture, IGltfWritable gltf, ImageFormat format = ImageFormat.Unknown ) { var texture2d = texture as Texture2D; if (texture2d == null) { return null; } var imageExport = new ImageExport(texture2d, format); if (MaterialExport.AddImageExport(gltf, imageExport, out var textureId)) { return new TextureInfo { index = textureId, // texCoord = 0 // TODO: figure out which UV set was used }; } return null; } /// /// Export a normal texture from Unity to glTF. /// /// Normal texture to export /// Material the normal is used on /// Context glTF to export to /// Normal scale property ID /// glTF texture info protected static NormalTextureInfo ExportNormalTextureInfo( UnityEngine.Texture texture, UnityEngine.Material material, IGltfWritable gltf, int normalScalePropId ) { var texture2d = texture as Texture2D; if (texture2d == null) { return null; } var imageExport = new NormalImageExport(texture2d); if (MaterialExport.AddImageExport(gltf, imageExport, out var textureId)) { var info = new NormalTextureInfo { index = textureId, // texCoord = 0 // TODO: figure out which UV set was used }; if (material.HasProperty(normalScalePropId)) { info.scale = material.GetFloat(normalScalePropId); } return info; } return null; } /// [Obsolete("Use MaterialExport.AddImageExport instead.")] protected static bool AddImageExport(IGltfWritable gltf, ImageExportBase imageExport, out int textureId) { return MaterialExport.AddImageExport(gltf, imageExport, out textureId); } /// /// Calculates a texture's transform and adds a KHR_texture_transform glTF extension, if required /// /// glTF TextureInfo to edit /// Source Material /// Texture property to fetch transformation from /// Context glTF to export to (for registering extension usage) protected static void ExportTextureTransform(TextureInfoBase def, UnityEngine.Material mat, int texPropertyId, IGltfWritable gltf) { var offset = mat.GetTextureOffset(texPropertyId); var scale = mat.GetTextureScale(texPropertyId); if (offset != Vector2.zero || scale != Vector2.one) { gltf.RegisterExtensionUsage(Extension.TextureTransform); def.SetTextureTransform(new TextureTransform { scale = new[] { scale.x, scale.y }, offset = new[] { offset.x, 1 - offset.y - scale.y } }); } } } }