// 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 }
});
}
}
}
}