using System;
using System.Collections.Generic;
using LibMMD.Material;
using UnityEngine;
using UnityEngine.Rendering;
namespace LibMMD.Unity3D
{
[Flags]
public enum MmdMaterialSettings
{
None = 0,
ReceiveShadows = 1 << 0,
CastShadows = 1 << 1,
Outline = 1 << 2,
TextureAlphaScan = 1 << 3,
All = ReceiveShadows | CastShadows | Outline | TextureAlphaScan,
}
///
/// Optional adapter metadata used by applications to build a shader-specific settings UI.
/// Kept separate from IMmdMaterialAdapter so existing third-party adapters remain compatible.
///
public interface IMmdMaterialSettingsProvider
{
MmdMaterialSettings SupportedSettings { get; }
}
public static class MmdMaterialAdapterIds
{
public const string Mmd = "mmd";
public const string Lit = "lit";
// Legacy IDs kept for loading existing project/user settings.
public const string Standard = "standard";
public const string UrpLit = "urp-lit";
public const string LilToon = "liltoon";
public const string Custom = "custom";
}
public sealed class MmdMaterialContext
{
public MmdUnityConfig Config;
public Texture MainTexture;
public Texture ToonTexture;
public Texture SphereTexture;
public MmdMaterial.SubTextureTypeEnum SphereTextureType;
public bool IsTransparent;
public bool IsTransparentOverlay;
public bool EnableOutline;
public bool CastShadows;
public bool ReceiveShadows;
public bool EnableSelfShadow;
}
public interface IMmdMaterialAdapter
{
string Id { get; }
string DisplayName { get; }
Shader ResolveShader(MmdMaterial source, MmdMaterialContext context);
void Apply(UnityEngine.Material target, MmdMaterial source, MmdMaterialContext context);
}
public interface IMmdMaterialAdapterRegistry
{
IEnumerable Adapters { get; }
IMmdMaterialAdapter Get(string id);
void Register(IMmdMaterialAdapter adapter);
bool Unregister(string id);
}
public sealed class MmdMaterialAdapterRegistry : IMmdMaterialAdapterRegistry
{
private readonly Dictionary _adapters =
new Dictionary(StringComparer.OrdinalIgnoreCase);
public static MmdMaterialAdapterRegistry Default { get; } = CreateDefault();
public IEnumerable Adapters => _adapters.Values;
private static MmdMaterialAdapterRegistry CreateDefault()
{
var registry = new MmdMaterialAdapterRegistry();
registry.Register(new MmdBuiltInMaterialAdapter());
registry.Register(new LitMmdMaterialAdapter());
registry.Register(new CustomMmdMaterialAdapter());
return registry;
}
public IMmdMaterialAdapter Get(string id)
{
if (string.IsNullOrWhiteSpace(id)) id = MmdMaterialAdapterIds.Mmd;
if (string.Equals(id, MmdMaterialAdapterIds.Standard, StringComparison.OrdinalIgnoreCase)
|| string.Equals(id, MmdMaterialAdapterIds.UrpLit, StringComparison.OrdinalIgnoreCase))
id = MmdMaterialAdapterIds.Lit;
IMmdMaterialAdapter adapter;
return _adapters.TryGetValue(id, out adapter) ? adapter : null;
}
public void Register(IMmdMaterialAdapter adapter)
{
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
if (string.IsNullOrWhiteSpace(adapter.Id)) throw new ArgumentException("Adapter ID is required.", nameof(adapter));
_adapters[adapter.Id] = adapter;
}
public bool Unregister(string id) =>
!string.IsNullOrWhiteSpace(id) && _adapters.Remove(id);
}
public abstract class MmdMaterialAdapterBase : IMmdMaterialAdapter, IMmdMaterialSettingsProvider
{
public abstract string Id { get; }
public abstract string DisplayName { get; }
public virtual MmdMaterialSettings SupportedSettings => MmdMaterialSettings.All;
public abstract Shader ResolveShader(MmdMaterial source, MmdMaterialContext context);
public virtual void Apply(UnityEngine.Material target, MmdMaterial source, MmdMaterialContext context)
{
SetColor(target, "_Color", source.DiffuseColor);
SetTexture(target, "_MainTex", context.MainTexture);
SetColor(target, "_SpecColor", source.SpecularColor);
SetFloat(target, "_OutlineWidth", source.EdgeSize);
SetColor(target, "_OutlineColor", source.EdgeColor);
}
protected static Shader FindFirst(params string[] shaderNames)
{
foreach (var shaderName in shaderNames)
{
if (string.IsNullOrWhiteSpace(shaderName)) continue;
var shader = Shader.Find(shaderName);
if (shader != null) return shader;
}
return null;
}
protected static void SetColor(UnityEngine.Material material, string name, Color value)
{
if (material.HasProperty(name)) material.SetColor(name, value);
}
protected static void SetFloat(UnityEngine.Material material, string name, float value)
{
if (material.HasProperty(name)) material.SetFloat(name, value);
}
protected static void SetTexture(UnityEngine.Material material, string name, Texture value)
{
if (value == null || !material.HasProperty(name)) return;
material.SetTexture(name, value);
material.SetTextureScale(name, Vector2.one);
}
protected static void SetKeyword(UnityEngine.Material material, string keyword, bool enabled)
{
if (enabled) material.EnableKeyword(keyword); else material.DisableKeyword(keyword);
}
protected static void ConfigureBlend(UnityEngine.Material material, bool transparent, string transparentKeyword)
{
SetFloat(material, "_SrcBlend", transparent ? (float)BlendMode.SrcAlpha : (float)BlendMode.One);
SetFloat(material, "_DstBlend", transparent ? (float)BlendMode.OneMinusSrcAlpha : (float)BlendMode.Zero);
SetFloat(material, "_ZWrite", transparent ? 0.0f : 1.0f);
if (!string.IsNullOrEmpty(transparentKeyword)) SetKeyword(material, transparentKeyword, transparent);
material.renderQueue = transparent ? (int)RenderQueue.Transparent : -1;
}
}
}