UniVRM/Assets/VRMShaders/GLTF/IO/Runtime/MaterialFactory.cs
2021-05-27 15:56:51 +09:00

161 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
namespace VRMShaders
{
public delegate Task<Texture> GetTextureAsyncFunc(TextureDescriptor texDesc);
public class MaterialFactory : IDisposable
{
private readonly IReadOnlyDictionary<SubAssetKey, Material> m_externalMap;
public MaterialFactory(IReadOnlyDictionary<SubAssetKey, Material> externalMaterialMap)
{
m_externalMap = externalMaterialMap;
}
public struct MaterialLoadInfo
{
public readonly Material Asset;
public readonly bool UseExternal;
public bool IsSubAsset => !UseExternal;
public MaterialLoadInfo(Material asset, bool useExternal)
{
Asset = asset;
UseExternal = useExternal;
}
}
List<MaterialLoadInfo> m_materials = new List<MaterialLoadInfo>();
public IReadOnlyList<MaterialLoadInfo> Materials => m_materials;
void Remove(Material material)
{
var index = m_materials.FindIndex(x => x.Asset == material);
if (index >= 0)
{
m_materials.RemoveAt(index);
}
}
public void Dispose()
{
foreach (var x in m_materials)
{
if (!x.UseExternal)
{
// 外部の '.asset' からロードしていない
#if VRM_DEVELOP
// Debug.Log($"Destroy {x.Asset}");
#endif
UnityEngine.Object.DestroyImmediate(x.Asset, false);
}
}
}
/// <summary>
/// 所有権(Dispose権)を移譲する
///
/// 所有権を移動する関数。
///
/// * 所有権が移動する。return true => ImporterContext.Dispose の対象から外れる
/// * 所有権が移動しない。return false => Importer.Context.Dispose でDestroyされる
///
/// </summary>
/// <param name="take"></param>
public void TransferOwnership(Func<UnityEngine.Object, bool> take)
{
var list = new List<Material>();
foreach (var x in m_materials)
{
if (!x.UseExternal)
{
// 外部の '.asset' からロードしていない
if (take(x.Asset))
{
list.Add(x.Asset);
}
}
}
foreach (var x in list)
{
Remove(x);
}
}
public Material GetMaterial(int index)
{
if (index < 0) return null;
if (index >= m_materials.Count) return null;
return m_materials[index].Asset;
}
public async Task<Material> LoadAsync(MaterialDescriptor matDesc, GetTextureAsyncFunc getTexture)
{
if (m_externalMap.TryGetValue(matDesc.SubAssetKey, out Material material))
{
m_materials.Add(new MaterialLoadInfo(material, true));
return material;
}
if (getTexture == null)
{
getTexture = (_) => Task.FromResult<Texture>(null);
}
material = new Material(Shader.Find(matDesc.ShaderName));
material.name = matDesc.SubAssetKey.Name;
foreach(var kv in matDesc.TextureSlots)
{
var texture = await getTexture(kv.Value);
if(texture!=null){
material.SetTexture(kv.Key, texture);
SetTextureOffsetAndScale(material, kv.Key, kv.Value.Offset, kv.Value.Scale);
}
}
foreach(var kv in matDesc.Colors)
{
material.SetColor(kv.Key, kv.Value);
}
foreach(var kv in matDesc.Vectors)
{
material.SetVector(kv.Key, kv.Value);
}
foreach(var kv in matDesc.FloatValues)
{
material.SetFloat(kv.Key, kv.Value);
}
if (matDesc.RenderQueue.HasValue)
{
material.renderQueue = matDesc.RenderQueue.Value;
}
foreach(var action in matDesc.Actions)
{
action(material);
}
m_materials.Add(new MaterialLoadInfo(material, false));
return material;
}
public static void SetTextureOffsetAndScale(Material material, string propertyName, Vector2 offset, Vector2 scale)
{
material.SetTextureOffset(propertyName, offset);
material.SetTextureScale(propertyName, scale);
}
}
}