mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-04 08:52:22 -05:00
Merge pull request #830 from ousttrue/feature/vrmshaders_materialfactory
マテリアル生成を VRMShaders に移動
This commit is contained in:
commit
270a2d3eed
|
|
@ -57,17 +57,27 @@ namespace UniGLTF
|
|||
s_foldTextures = EditorGUILayout.Foldout(s_foldTextures, "Remapped Textures");
|
||||
if (s_foldTextures)
|
||||
{
|
||||
DrawRemapGUI<UnityEngine.Texture2D>(importer, GltfTextureEnumerator.Enumerate(parser).Select(x =>
|
||||
{
|
||||
switch (x.TextureType)
|
||||
var names = GltfTextureEnumerator.Enumerate(parser)
|
||||
.Select(x =>
|
||||
{
|
||||
case TextureImportTypes.NormalMap:
|
||||
return x.GltfName;
|
||||
if (x.TextureType != TextureImportTypes.StandardMap && !string.IsNullOrEmpty(x.Uri))
|
||||
{
|
||||
// GLTF の 無変換テクスチャーをスキップする
|
||||
return null;
|
||||
}
|
||||
|
||||
default:
|
||||
return x.ConvertedName;
|
||||
}
|
||||
}));
|
||||
switch (x.TextureType)
|
||||
{
|
||||
case TextureImportTypes.NormalMap:
|
||||
return x.GltfName;
|
||||
|
||||
default:
|
||||
return x.ConvertedName;
|
||||
}
|
||||
})
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
;
|
||||
DrawRemapGUI<UnityEngine.Texture2D>(importer, names);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Clear"))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||
using UnityEngine;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using VRMShaders;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
|
|
@ -35,6 +36,8 @@ namespace UniGLTF
|
|||
MaterialFactory m_materialFactory;
|
||||
public MaterialFactory MaterialFactory => m_materialFactory;
|
||||
|
||||
public readonly GltfMaterialImporter GltfMaterialImporter = new GltfMaterialImporter();
|
||||
|
||||
TextureFactory m_textureFactory;
|
||||
public TextureFactory TextureFactory => m_textureFactory;
|
||||
|
||||
|
|
@ -44,8 +47,8 @@ namespace UniGLTF
|
|||
IEnumerable<(string, UnityEngine.Object)> externalObjectMap = null)
|
||||
{
|
||||
m_parser = parser;
|
||||
m_textureFactory = new TextureFactory(GLTF, Storage, externalObjectMap);
|
||||
m_materialFactory = new MaterialFactory(m_parser, externalObjectMap);
|
||||
m_textureFactory = new TextureFactory(externalObjectMap);
|
||||
m_materialFactory = new MaterialFactory(externalObjectMap);
|
||||
}
|
||||
|
||||
#region Source
|
||||
|
|
@ -110,7 +113,7 @@ namespace UniGLTF
|
|||
|
||||
using (MeasureTime("LoadMaterials"))
|
||||
{
|
||||
await m_materialFactory.LoadMaterialsAsync(m_awaitCaller, m_textureFactory.GetTextureAsync);
|
||||
await LoadMaterialsAsync();
|
||||
}
|
||||
|
||||
var meshImporter = new MeshImporter();
|
||||
|
|
@ -170,6 +173,24 @@ namespace UniGLTF
|
|||
await OnLoadModel(m_awaitCaller, MeasureTime);
|
||||
}
|
||||
|
||||
public async Task LoadMaterialsAsync()
|
||||
{
|
||||
if (m_parser.GLTF.materials == null || m_parser.GLTF.materials.Count == 0)
|
||||
{
|
||||
// no material. work around.
|
||||
var param = GltfMaterialImporter.CreateParam(m_parser, 0);
|
||||
var material = await MaterialFactory.LoadAsync(param, TextureFactory.GetTextureAsync);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < m_parser.GLTF.materials.Count; ++i)
|
||||
{
|
||||
var param = GltfMaterialImporter.CreateParam(m_parser, i);
|
||||
var material = await MaterialFactory.LoadAsync(param, TextureFactory.GetTextureAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task OnLoadModel(IAwaitCaller awaitCaller, Func<string, IDisposable> MeasureTime)
|
||||
{
|
||||
// do nothing
|
||||
|
|
@ -283,7 +304,7 @@ namespace UniGLTF
|
|||
/// Root ヒエラルキーで使っているリソース
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual void TransferOwnership(TakeOwnershipFunc take)
|
||||
public virtual void TransferOwnership(Func<UnityEngine.Object, bool> take)
|
||||
{
|
||||
var list = new List<UnityEngine.Object>();
|
||||
foreach (var mesh in Meshes)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public delegate bool TryCreateMaterialParamFromGltf(GltfParser parser, int i, out MaterialImportParam param);
|
||||
|
||||
public class GltfMaterialImporter
|
||||
{
|
||||
/// <summary>
|
||||
/// gltfMaterialを解釈する関数。
|
||||
/// 拡張するには、先頭に挿入するべし。
|
||||
/// </summary>
|
||||
/// <typeparam name="TryCreateMaterialParamFromGltf"></typeparam>
|
||||
/// <returns></returns>
|
||||
public readonly List<TryCreateMaterialParamFromGltf> GltfMaterialParamProcessors = new List<TryCreateMaterialParamFromGltf>();
|
||||
|
||||
public GltfMaterialImporter()
|
||||
{
|
||||
// unlit を試し
|
||||
GltfMaterialParamProcessors.Add(GltfUnlitMaterial.TryCreateParam);
|
||||
// PBR を作成する(失敗しない)
|
||||
GltfMaterialParamProcessors.Add(GltfPBRMaterial.TryCreateParam);
|
||||
}
|
||||
|
||||
public static string MaterialName(int index, glTFMaterial src)
|
||||
{
|
||||
if (src != null && !string.IsNullOrEmpty(src.name))
|
||||
{
|
||||
return src.name;
|
||||
}
|
||||
return $"material_{index:00}";
|
||||
}
|
||||
|
||||
public MaterialImportParam CreateParam(GltfParser parser, int i)
|
||||
{
|
||||
foreach (var tryCreate in GltfMaterialParamProcessors)
|
||||
{
|
||||
if (tryCreate(parser, i, out MaterialImportParam param))
|
||||
{
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
#if VRM_DEVELOP
|
||||
Debug.LogWarning($"material: {i} out of range. fallback");
|
||||
#endif
|
||||
return new MaterialImportParam(MaterialName(i, null), GltfPBRMaterial.ShaderName);
|
||||
}
|
||||
|
||||
public static (Vector2, Vector2) GetTextureOffsetAndScale(glTFTextureInfo textureInfo)
|
||||
{
|
||||
Vector2 offset = new Vector2(0, 0);
|
||||
Vector2 scale = new Vector2(1, 1);
|
||||
if (glTF_KHR_texture_transform.TryGet(textureInfo, out glTF_KHR_texture_transform textureTransform))
|
||||
{
|
||||
if (textureTransform.offset != null && textureTransform.offset.Length == 2)
|
||||
{
|
||||
offset = new Vector2(textureTransform.offset[0], textureTransform.offset[1]);
|
||||
}
|
||||
if (textureTransform.scale != null && textureTransform.scale.Length == 2)
|
||||
{
|
||||
scale = new Vector2(textureTransform.scale[0], textureTransform.scale[1]);
|
||||
}
|
||||
|
||||
offset.y = (offset.y + scale.y - 1.0f) * -1.0f;
|
||||
}
|
||||
return (offset, scale);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// for unittest
|
||||
/// </summary>
|
||||
public static glTF CreateMaterialForTest(glTFMaterial material)
|
||||
{
|
||||
return new glTF
|
||||
{
|
||||
materials = new System.Collections.Generic.List<glTFMaterial> {
|
||||
material
|
||||
},
|
||||
textures = new List<glTFTexture>{
|
||||
new glTFTexture{
|
||||
name = "texture_0"
|
||||
}
|
||||
},
|
||||
images = new List<glTFImage>{
|
||||
new glTFImage{
|
||||
name = "image_0",
|
||||
mimeType = "image/png",
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab2b998b9235dc94a90ccaf2e40a50a6
|
||||
guid: 06d7ddbd9b8b38544a74013a6992210b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
213
Assets/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/GltfPBRMaterial.cs
Normal file
213
Assets/UniGLTF/Runtime/UniGLTF/IO/MaterialIO/GltfPBRMaterial.cs
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// Gltf から MaterialImportParam に変換する
|
||||
///
|
||||
/// StandardShader variables
|
||||
///
|
||||
/// _Color
|
||||
/// _MainTex
|
||||
/// _Cutoff
|
||||
/// _Glossiness
|
||||
/// _Metallic
|
||||
/// _MetallicGlossMap
|
||||
/// _BumpScale
|
||||
/// _BumpMap
|
||||
/// _Parallax
|
||||
/// _ParallaxMap
|
||||
/// _OcclusionStrength
|
||||
/// _OcclusionMap
|
||||
/// _EmissionColor
|
||||
/// _EmissionMap
|
||||
/// _DetailMask
|
||||
/// _DetailAlbedoMap
|
||||
/// _DetailNormalMapScale
|
||||
/// _DetailNormalMap
|
||||
/// _UVSec
|
||||
/// _EmissionScaleUI
|
||||
/// _EmissionColorUI
|
||||
/// _Mode
|
||||
/// _SrcBlend
|
||||
/// _DstBlend
|
||||
/// _ZWrite
|
||||
///
|
||||
/// </summary>
|
||||
public static class GltfPBRMaterial
|
||||
{
|
||||
public const string ShaderName = "Standard";
|
||||
|
||||
private enum BlendMode
|
||||
{
|
||||
Opaque,
|
||||
Cutout,
|
||||
Fade,
|
||||
Transparent
|
||||
}
|
||||
|
||||
public static TextureImportParam BaseColorTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(src.pbrMetallicRoughness.baseColorTexture);
|
||||
return GltfTextureImporter.CreateSRGB(parser, src.pbrMetallicRoughness.baseColorTexture.index, offset, scale);
|
||||
}
|
||||
|
||||
public static TextureImportParam StandardTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var metallicFactor = 1.0f;
|
||||
var roughnessFactor = 1.0f;
|
||||
if (src.pbrMetallicRoughness != null)
|
||||
{
|
||||
metallicFactor = src.pbrMetallicRoughness.metallicFactor;
|
||||
roughnessFactor = src.pbrMetallicRoughness.roughnessFactor;
|
||||
}
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(src.pbrMetallicRoughness.metallicRoughnessTexture);
|
||||
return GltfTextureImporter.CreateStandard(parser,
|
||||
src.pbrMetallicRoughness?.metallicRoughnessTexture?.index,
|
||||
src.occlusionTexture?.index,
|
||||
offset, scale,
|
||||
metallicFactor,
|
||||
roughnessFactor);
|
||||
}
|
||||
|
||||
public static TextureImportParam NormalTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(src.normalTexture);
|
||||
return GltfTextureImporter.CreateNormal(parser, src.normalTexture.index, offset, scale);
|
||||
}
|
||||
|
||||
public static bool TryCreateParam(GltfParser parser, int i, out MaterialImportParam param)
|
||||
{
|
||||
if (i < 0 || i >= parser.GLTF.materials.Count)
|
||||
{
|
||||
param = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var src = parser.GLTF.materials[i];
|
||||
param = new MaterialImportParam(GltfMaterialImporter.MaterialName(i, src), ShaderName);
|
||||
|
||||
var standardParam = default(TextureImportParam);
|
||||
if (src.pbrMetallicRoughness != null || src.occlusionTexture != null)
|
||||
{
|
||||
if (src.pbrMetallicRoughness.metallicRoughnessTexture != null || src.occlusionTexture != null)
|
||||
{
|
||||
standardParam = StandardTexture(parser, src);
|
||||
}
|
||||
|
||||
if (src.pbrMetallicRoughness.baseColorFactor != null && src.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = src.pbrMetallicRoughness.baseColorFactor;
|
||||
param.Colors.Add("_Color", (new Color(color[0], color[1], color[2], color[3])).gamma);
|
||||
}
|
||||
|
||||
if (src.pbrMetallicRoughness.baseColorTexture != null && src.pbrMetallicRoughness.baseColorTexture.index != -1)
|
||||
{
|
||||
var textureParam = BaseColorTexture(parser, src);
|
||||
param.TextureSlots.Add("_MainTex", textureParam);
|
||||
}
|
||||
|
||||
if (src.pbrMetallicRoughness.metallicRoughnessTexture != null && src.pbrMetallicRoughness.metallicRoughnessTexture.index != -1)
|
||||
{
|
||||
param.Actions.Add(material => material.EnableKeyword("_METALLICGLOSSMAP"));
|
||||
param.TextureSlots.Add("_MetallicGlossMap", standardParam);
|
||||
// Set 1.0f as hard-coded. See: https://github.com/dwango/UniVRM/issues/212.
|
||||
param.FloatValues.Add("_Metallic", 1.0f);
|
||||
param.FloatValues.Add("_GlossMapScale", 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
param.FloatValues.Add("_Metallic", src.pbrMetallicRoughness.metallicFactor);
|
||||
param.FloatValues.Add("_Glossiness", 1.0f - src.pbrMetallicRoughness.roughnessFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if (src.normalTexture != null && src.normalTexture.index != -1)
|
||||
{
|
||||
param.Actions.Add(material => material.EnableKeyword("_NORMALMAP"));
|
||||
var textureParam = NormalTexture(parser, src);
|
||||
param.TextureSlots.Add("_BumpMap", textureParam);
|
||||
param.FloatValues.Add("_BumpScale", src.normalTexture.scale);
|
||||
}
|
||||
|
||||
if (src.occlusionTexture != null && src.occlusionTexture.index != -1)
|
||||
{
|
||||
param.TextureSlots.Add("_OcclusionMap", standardParam);
|
||||
param.FloatValues.Add("_OcclusionStrength", src.occlusionTexture.strength);
|
||||
}
|
||||
|
||||
if (src.emissiveFactor != null
|
||||
|| (src.emissiveTexture != null && src.emissiveTexture.index != -1))
|
||||
{
|
||||
param.Actions.Add(material => {
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.globalIlluminationFlags &= ~MaterialGlobalIlluminationFlags.EmissiveIsBlack;
|
||||
});
|
||||
|
||||
if (src.emissiveFactor != null && src.emissiveFactor.Length == 3)
|
||||
{
|
||||
param.Colors.Add("_EmissionColor", new Color(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]));
|
||||
}
|
||||
|
||||
if (src.emissiveTexture != null && src.emissiveTexture.index != -1)
|
||||
{
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(src.emissiveTexture);
|
||||
var textureParam = GltfTextureImporter.CreateSRGB(parser, src.emissiveTexture.index, offset, scale);
|
||||
param.TextureSlots.Add("_EmissionMap", textureParam);
|
||||
}
|
||||
}
|
||||
|
||||
param.Actions.Add(material => {
|
||||
BlendMode blendMode = BlendMode.Opaque;
|
||||
// https://forum.unity.com/threads/standard-material-shader-ignoring-setfloat-property-_mode.344557/#post-2229980
|
||||
switch (src.alphaMode)
|
||||
{
|
||||
case "BLEND":
|
||||
blendMode = BlendMode.Fade;
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
material.SetInt("_ZWrite", 0);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.EnableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 3000;
|
||||
break;
|
||||
|
||||
case "MASK":
|
||||
blendMode = BlendMode.Cutout;
|
||||
material.SetOverrideTag("RenderType", "TransparentCutout");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.SetFloat("_Cutoff", src.alphaCutoff);
|
||||
material.EnableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 2450;
|
||||
|
||||
break;
|
||||
|
||||
default: // OPAQUE
|
||||
blendMode = BlendMode.Opaque;
|
||||
material.SetOverrideTag("RenderType", "");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
material.SetFloat("_Mode", (float)blendMode);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ namespace UniGLTF
|
|||
// base color
|
||||
if (m.pbrMetallicRoughness?.baseColorTexture != null)
|
||||
{
|
||||
yield return PBRMaterialItem.BaseColorTexture(parser, m);
|
||||
yield return GltfPBRMaterial.BaseColorTexture(parser, m);
|
||||
}
|
||||
|
||||
// metallic roughness
|
||||
|
|
@ -55,14 +55,14 @@ namespace UniGLTF
|
|||
// emission
|
||||
if (m.emissiveTexture != null)
|
||||
{
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(m.emissiveTexture);
|
||||
yield return TextureFactory.CreateSRGB(parser, m.emissiveTexture.index, offset, scale);
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(m.emissiveTexture);
|
||||
yield return GltfTextureImporter.CreateSRGB(parser, m.emissiveTexture.index, offset, scale);
|
||||
}
|
||||
|
||||
// normal
|
||||
if (m.normalTexture != null)
|
||||
{
|
||||
yield return PBRMaterialItem.NormalTexture(parser, m);
|
||||
yield return GltfPBRMaterial.NormalTexture(parser, m);
|
||||
}
|
||||
|
||||
// occlusion
|
||||
|
|
@ -75,7 +75,7 @@ namespace UniGLTF
|
|||
// metallicSmooth and occlusion
|
||||
if (metallicRoughnessTexture.HasValue || occlusionTexture.HasValue)
|
||||
{
|
||||
yield return PBRMaterialItem.StandardTexture(parser, m);
|
||||
yield return GltfPBRMaterial.StandardTexture(parser, m);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class GltfUnlitMaterial
|
||||
{
|
||||
public const string ShaderName = "UniGLTF/UniUnlit";
|
||||
|
||||
public static bool TryCreateParam(GltfParser parser, int i, out MaterialImportParam param)
|
||||
{
|
||||
if (i < 0 || i >= parser.GLTF.materials.Count)
|
||||
{
|
||||
param = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var src = parser.GLTF.materials[i];
|
||||
if (!glTF_KHR_materials_unlit.IsEnable(src))
|
||||
{
|
||||
param = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
param = new MaterialImportParam(GltfMaterialImporter.MaterialName(i, src), ShaderName);
|
||||
|
||||
// texture
|
||||
if (src.pbrMetallicRoughness.baseColorTexture != null)
|
||||
{
|
||||
var (offset, scale) = GltfMaterialImporter.GetTextureOffsetAndScale(src.pbrMetallicRoughness.baseColorTexture);
|
||||
var textureParam = GltfTextureImporter.CreateSRGB(parser, src.pbrMetallicRoughness.baseColorTexture.index, offset, scale);
|
||||
param.TextureSlots.Add("_MainTex", textureParam);
|
||||
}
|
||||
|
||||
// color
|
||||
if (src.pbrMetallicRoughness.baseColorFactor != null && src.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = src.pbrMetallicRoughness.baseColorFactor;
|
||||
param.Colors.Add("_Color", (new Color(color[0], color[1], color[2], color[3])).gamma);
|
||||
}
|
||||
|
||||
//renderMode
|
||||
param.Actions.Add(material =>
|
||||
{
|
||||
if (src.alphaMode == "OPAQUE")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
else if (src.alphaMode == "BLEND")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Transparent);
|
||||
}
|
||||
else if (src.alphaMode == "MASK")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Cutout);
|
||||
material.SetFloat("_Cutoff", src.alphaCutoff);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default OPAQUE
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
|
||||
// culling
|
||||
if (src.doubleSided)
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Off);
|
||||
}
|
||||
else
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Back);
|
||||
}
|
||||
|
||||
// VColor
|
||||
var hasVertexColor = parser.GLTF.MaterialHasVertexColor(i);
|
||||
if (hasVertexColor)
|
||||
{
|
||||
UniUnlit.Utils.SetVColBlendMode(material, UniUnlit.UniUnlitVertexColorBlendOp.Multiply);
|
||||
}
|
||||
|
||||
UniUnlit.Utils.ValidateProperties(material, true);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58277bcba49b13142acfa67d40041970
|
||||
guid: f9748317ec6964a47b0a1c4150fe62e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public class MaterialFactory : IDisposable
|
||||
{
|
||||
GltfParser m_parser;
|
||||
Dictionary<string, Material> m_externalMap;
|
||||
public bool TryGetExternal(int index, out Material external)
|
||||
{
|
||||
if (m_externalMap != null)
|
||||
{
|
||||
var gltfMaterial = m_parser.GLTF.materials[index];
|
||||
if (m_externalMap.TryGetValue(gltfMaterial.name, out external))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
external = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public MaterialFactory(GltfParser parser, IEnumerable<(string, UnityEngine.Object)> externalMap)
|
||||
{
|
||||
m_parser = parser;
|
||||
if (externalMap != null)
|
||||
{
|
||||
m_externalMap = externalMap
|
||||
.Select(kv => (kv.Item1, kv.Item2 as Material))
|
||||
.Where(kv => kv.Item2 != null)
|
||||
.ToDictionary(kv => kv.Item1, kv => kv.Item2)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate Task<Material> CreateMaterialAsyncFunc(IAwaitCaller awaitCaller, GltfParser parser, int i, GetTextureAsyncFunc getTexture);
|
||||
CreateMaterialAsyncFunc m_createMaterialAsync;
|
||||
public CreateMaterialAsyncFunc CreateMaterialAsync
|
||||
{
|
||||
set
|
||||
{
|
||||
m_createMaterialAsync = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
if (m_createMaterialAsync == null)
|
||||
{
|
||||
m_createMaterialAsync = MaterialFactory.DefaultCreateMaterialAsync;
|
||||
}
|
||||
return m_createMaterialAsync;
|
||||
}
|
||||
}
|
||||
|
||||
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権)を移譲する
|
||||
/// </summary>
|
||||
/// <param name="take"></param>
|
||||
public void TransferOwnership(TakeOwnershipFunc 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// テクスチャ生成
|
||||
/// </summary>
|
||||
/// <param name="getTexture"></param>
|
||||
/// <returns></returns>
|
||||
public async Task LoadMaterialsAsync(IAwaitCaller awaitCaller, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
if (m_parser.GLTF.materials == null || m_parser.GLTF.materials.Count == 0)
|
||||
{
|
||||
// no material. work around.
|
||||
var material = await CreateMaterialAsync(awaitCaller, m_parser, 0, getTexture);
|
||||
m_materials.Add(new MaterialLoadInfo(material, false));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_parser.GLTF.materials.Count; ++i)
|
||||
{
|
||||
if (TryGetExternal(i, out Material material))
|
||||
{
|
||||
m_materials.Add(new MaterialLoadInfo(material, true));
|
||||
continue;
|
||||
}
|
||||
|
||||
material = await CreateMaterialAsync(awaitCaller, m_parser, i, getTexture);
|
||||
m_materials.Add(new MaterialLoadInfo(material, false));
|
||||
}
|
||||
}
|
||||
|
||||
public static string MaterialName(int index, glTFMaterial src)
|
||||
{
|
||||
if(src!=null && !string.IsNullOrEmpty(src.name))
|
||||
{
|
||||
return src.name;
|
||||
}
|
||||
return $"material_{index:00}";
|
||||
}
|
||||
|
||||
public static (Vector2, Vector2) GetTextureOffsetAndScale(glTFTextureInfo textureInfo)
|
||||
{
|
||||
Vector2 offset = new Vector2(0, 0);
|
||||
Vector2 scale = new Vector2(1, 1);
|
||||
if (glTF_KHR_texture_transform.TryGet(textureInfo, out glTF_KHR_texture_transform textureTransform))
|
||||
{
|
||||
if (textureTransform.offset != null && textureTransform.offset.Length == 2)
|
||||
{
|
||||
offset = new Vector2(textureTransform.offset[0], textureTransform.offset[1]);
|
||||
}
|
||||
if (textureTransform.scale != null && textureTransform.scale.Length == 2)
|
||||
{
|
||||
scale = new Vector2(textureTransform.scale[0], textureTransform.scale[1]);
|
||||
}
|
||||
|
||||
offset.y = (offset.y + scale.y - 1.0f) * -1.0f;
|
||||
}
|
||||
return (offset, scale);
|
||||
}
|
||||
|
||||
public static void SetTextureOffsetAndScale(Material material, string propertyName, Vector2 offset, Vector2 scale)
|
||||
{
|
||||
material.SetTextureOffset(propertyName, offset);
|
||||
material.SetTextureScale(propertyName, scale);
|
||||
}
|
||||
|
||||
public static Task<Material> DefaultCreateMaterialAsync(IAwaitCaller awaitCaller, GltfParser parser, int i, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
if (i < 0 || i >= parser.GLTF.materials.Count)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("glTFMaterial is empty");
|
||||
return PBRMaterialItem.CreateAsync(awaitCaller, parser, i, getTexture);
|
||||
}
|
||||
var x = parser.GLTF.materials[i];
|
||||
|
||||
if (glTF_KHR_materials_unlit.IsEnable(x))
|
||||
{
|
||||
var hasVertexColor = parser.GLTF.MaterialHasVertexColor(i);
|
||||
return UnlitMaterialItem.CreateAsync(awaitCaller, parser, i, getTexture, hasVertexColor);
|
||||
}
|
||||
|
||||
return PBRMaterialItem.CreateAsync(awaitCaller, parser, i, getTexture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for unittest
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <param name="material"></param>
|
||||
/// <param name="getTexture"></param>
|
||||
/// <returns></returns>
|
||||
public static Material CreateMaterialForTest(int i, glTFMaterial material)
|
||||
{
|
||||
var gltf = new glTF
|
||||
{
|
||||
materials = new System.Collections.Generic.List<glTFMaterial> { material },
|
||||
textures = new List<glTFTexture>{
|
||||
new glTFTexture{
|
||||
name = "texture_0"
|
||||
}
|
||||
},
|
||||
images = new List<glTFImage>{
|
||||
new glTFImage{
|
||||
name = "image_0",
|
||||
mimeType = "image/png",
|
||||
}
|
||||
},
|
||||
};
|
||||
var task = DefaultCreateMaterialAsync(default(ImmediateCaller), new GltfParser{GLTF = gltf}, i, null);
|
||||
return task.Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// StandardShader variables
|
||||
///
|
||||
/// _Color
|
||||
/// _MainTex
|
||||
/// _Cutoff
|
||||
/// _Glossiness
|
||||
/// _Metallic
|
||||
/// _MetallicGlossMap
|
||||
/// _BumpScale
|
||||
/// _BumpMap
|
||||
/// _Parallax
|
||||
/// _ParallaxMap
|
||||
/// _OcclusionStrength
|
||||
/// _OcclusionMap
|
||||
/// _EmissionColor
|
||||
/// _EmissionMap
|
||||
/// _DetailMask
|
||||
/// _DetailAlbedoMap
|
||||
/// _DetailNormalMapScale
|
||||
/// _DetailNormalMap
|
||||
/// _UVSec
|
||||
/// _EmissionScaleUI
|
||||
/// _EmissionColorUI
|
||||
/// _Mode
|
||||
/// _SrcBlend
|
||||
/// _DstBlend
|
||||
/// _ZWrite
|
||||
public static class PBRMaterialItem
|
||||
{
|
||||
public const string ShaderName = "Standard";
|
||||
|
||||
private enum BlendMode
|
||||
{
|
||||
Opaque,
|
||||
Cutout,
|
||||
Fade,
|
||||
Transparent
|
||||
}
|
||||
|
||||
public static TextureImportParam BaseColorTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.pbrMetallicRoughness.baseColorTexture);
|
||||
return TextureFactory.CreateSRGB(parser, src.pbrMetallicRoughness.baseColorTexture.index, offset, scale);
|
||||
}
|
||||
|
||||
public static TextureImportParam StandardTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var metallicFactor = 1.0f;
|
||||
var roughnessFactor = 1.0f;
|
||||
if (src.pbrMetallicRoughness != null)
|
||||
{
|
||||
metallicFactor = src.pbrMetallicRoughness.metallicFactor;
|
||||
roughnessFactor = src.pbrMetallicRoughness.roughnessFactor;
|
||||
}
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.pbrMetallicRoughness.metallicRoughnessTexture);
|
||||
return TextureFactory.CreateStandard(parser,
|
||||
src.pbrMetallicRoughness?.metallicRoughnessTexture?.index,
|
||||
src.occlusionTexture?.index,
|
||||
offset, scale,
|
||||
metallicFactor,
|
||||
roughnessFactor);
|
||||
}
|
||||
|
||||
public static TextureImportParam NormalTexture(GltfParser parser, glTFMaterial src)
|
||||
{
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.normalTexture);
|
||||
return TextureFactory.CreateNormal(parser, src.normalTexture.index, offset, scale);
|
||||
}
|
||||
|
||||
public static async Task<Material> CreateAsync(IAwaitCaller awaitCaller, GltfParser parser, int i, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
if (getTexture == null)
|
||||
{
|
||||
getTexture = (IAwaitCaller _awaitCaller, glTF _gltf, TextureImportParam _param) => Task.FromResult<Texture2D>(null);
|
||||
}
|
||||
|
||||
var material = new Material(Shader.Find(ShaderName));
|
||||
if (i < 0 || i >= parser.GLTF.materials.Count)
|
||||
{
|
||||
material.name = MaterialFactory.MaterialName(i, null);
|
||||
return material;
|
||||
}
|
||||
|
||||
var src = parser.GLTF.materials[i];
|
||||
material.name = MaterialFactory.MaterialName(i, src);
|
||||
var standardParam = default(TextureImportParam);
|
||||
if (src.pbrMetallicRoughness != null || src.occlusionTexture != null)
|
||||
{
|
||||
if (src.pbrMetallicRoughness.metallicRoughnessTexture != null || src.occlusionTexture != null)
|
||||
{
|
||||
standardParam = StandardTexture(parser, src);
|
||||
}
|
||||
if (src.pbrMetallicRoughness.baseColorFactor != null && src.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = src.pbrMetallicRoughness.baseColorFactor;
|
||||
material.color = (new Color(color[0], color[1], color[2], color[3])).gamma;
|
||||
}
|
||||
|
||||
if (src.pbrMetallicRoughness.baseColorTexture != null && src.pbrMetallicRoughness.baseColorTexture.index != -1)
|
||||
{
|
||||
var param = BaseColorTexture(parser, src);
|
||||
material.mainTexture = await getTexture(awaitCaller, parser.GLTF, param);
|
||||
|
||||
// Texture Offset and Scale
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_MainTex", param.Offset, param.Scale);
|
||||
}
|
||||
|
||||
if (src.pbrMetallicRoughness.metallicRoughnessTexture != null && src.pbrMetallicRoughness.metallicRoughnessTexture.index != -1)
|
||||
{
|
||||
material.EnableKeyword("_METALLICGLOSSMAP");
|
||||
|
||||
var texture = await getTexture(awaitCaller, parser.GLTF, standardParam);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture(TextureImportParam.METALLIC_GLOSS_PROP, texture);
|
||||
}
|
||||
|
||||
material.SetFloat("_Metallic", 1.0f);
|
||||
// Set 1.0f as hard-coded. See: https://github.com/dwango/UniVRM/issues/212.
|
||||
material.SetFloat("_GlossMapScale", 1.0f);
|
||||
|
||||
// Texture Offset and Scale
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.pbrMetallicRoughness.metallicRoughnessTexture);
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_MetallicGlossMap", offset, scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetFloat("_Metallic", src.pbrMetallicRoughness.metallicFactor);
|
||||
material.SetFloat("_Glossiness", 1.0f - src.pbrMetallicRoughness.roughnessFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if (src.normalTexture != null && src.normalTexture.index != -1)
|
||||
{
|
||||
material.EnableKeyword("_NORMALMAP");
|
||||
var param = NormalTexture(parser, src);
|
||||
var texture = await getTexture(awaitCaller, parser.GLTF, param);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture(TextureImportParam.NORMAL_PROP, texture);
|
||||
material.SetFloat("_BumpScale", src.normalTexture.scale);
|
||||
}
|
||||
|
||||
// Texture Offset and Scale
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_BumpMap", param.Offset, param.Scale);
|
||||
}
|
||||
|
||||
if (src.occlusionTexture != null && src.occlusionTexture.index != -1)
|
||||
{
|
||||
var texture = await getTexture(awaitCaller, parser.GLTF, standardParam);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture(TextureImportParam.OCCLUSION_PROP, texture);
|
||||
material.SetFloat("_OcclusionStrength", src.occlusionTexture.strength);
|
||||
}
|
||||
|
||||
// Texture Offset and Scale
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.occlusionTexture);
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_OcclusionMap", offset, scale);
|
||||
}
|
||||
|
||||
if (src.emissiveFactor != null
|
||||
|| (src.emissiveTexture != null && src.emissiveTexture.index != -1))
|
||||
{
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.globalIlluminationFlags &= ~MaterialGlobalIlluminationFlags.EmissiveIsBlack;
|
||||
|
||||
if (src.emissiveFactor != null && src.emissiveFactor.Length == 3)
|
||||
{
|
||||
material.SetColor("_EmissionColor", new Color(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]));
|
||||
}
|
||||
|
||||
if (src.emissiveTexture != null && src.emissiveTexture.index != -1)
|
||||
{
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.emissiveTexture);
|
||||
var param = TextureFactory.CreateSRGB(parser, src.emissiveTexture.index, offset, scale);
|
||||
var texture = await getTexture(awaitCaller, parser.GLTF, param);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture("_EmissionMap", texture);
|
||||
}
|
||||
|
||||
// Texture Offset and Scale
|
||||
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_EmissionMap", param.Offset, param.Scale);
|
||||
}
|
||||
}
|
||||
|
||||
BlendMode blendMode = BlendMode.Opaque;
|
||||
// https://forum.unity.com/threads/standard-material-shader-ignoring-setfloat-property-_mode.344557/#post-2229980
|
||||
switch (src.alphaMode)
|
||||
{
|
||||
case "BLEND":
|
||||
blendMode = BlendMode.Fade;
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
material.SetInt("_ZWrite", 0);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.EnableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 3000;
|
||||
break;
|
||||
|
||||
case "MASK":
|
||||
blendMode = BlendMode.Cutout;
|
||||
material.SetOverrideTag("RenderType", "TransparentCutout");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.SetFloat("_Cutoff", src.alphaCutoff);
|
||||
material.EnableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = 2450;
|
||||
|
||||
break;
|
||||
|
||||
default: // OPAQUE
|
||||
blendMode = BlendMode.Opaque;
|
||||
material.SetOverrideTag("RenderType", "");
|
||||
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
||||
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
||||
material.SetInt("_ZWrite", 1);
|
||||
material.DisableKeyword("_ALPHATEST_ON");
|
||||
material.DisableKeyword("_ALPHABLEND_ON");
|
||||
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
material.renderQueue = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
material.SetFloat("_Mode", (float)blendMode);
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class UnlitMaterialItem
|
||||
{
|
||||
public const string ShaderName = "UniGLTF/UniUnlit";
|
||||
|
||||
public static async Task<Material> CreateAsync(IAwaitCaller awaitCaller, GltfParser parser, int i, GetTextureAsyncFunc getTexture, bool hasVertexColor)
|
||||
{
|
||||
if (getTexture == null)
|
||||
{
|
||||
getTexture = (_x, _y, _z) => Task.FromResult<Texture2D>(default);
|
||||
}
|
||||
|
||||
var src = parser.GLTF.materials[i];
|
||||
var material = new Material(Shader.Find(ShaderName));
|
||||
material.name = MaterialFactory.MaterialName(i, src);
|
||||
|
||||
// texture
|
||||
if (src.pbrMetallicRoughness.baseColorTexture != null)
|
||||
{
|
||||
var (offset, scale) = MaterialFactory.GetTextureOffsetAndScale(src.pbrMetallicRoughness.baseColorTexture);
|
||||
material.mainTexture = await getTexture(awaitCaller, parser.GLTF, TextureFactory.CreateSRGB(parser, src.pbrMetallicRoughness.baseColorTexture.index, offset, scale));
|
||||
|
||||
// Texture Offset and Scale
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, "_MainTex", offset, scale);
|
||||
}
|
||||
|
||||
// color
|
||||
if (src.pbrMetallicRoughness.baseColorFactor != null && src.pbrMetallicRoughness.baseColorFactor.Length == 4)
|
||||
{
|
||||
var color = src.pbrMetallicRoughness.baseColorFactor;
|
||||
material.color = (new Color(color[0], color[1], color[2], color[3])).gamma;
|
||||
}
|
||||
|
||||
//renderMode
|
||||
if (src.alphaMode == "OPAQUE")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
else if (src.alphaMode == "BLEND")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Transparent);
|
||||
}
|
||||
else if (src.alphaMode == "MASK")
|
||||
{
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Cutout);
|
||||
material.SetFloat("_Cutoff", src.alphaCutoff);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default OPAQUE
|
||||
UniUnlit.Utils.SetRenderMode(material, UniUnlit.UniUnlitRenderMode.Opaque);
|
||||
}
|
||||
|
||||
// culling
|
||||
if (src.doubleSided)
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Off);
|
||||
}
|
||||
else
|
||||
{
|
||||
UniUnlit.Utils.SetCullMode(material, UniUnlit.UniUnlitCullMode.Back);
|
||||
}
|
||||
|
||||
// VColor
|
||||
if (hasVertexColor)
|
||||
{
|
||||
UniUnlit.Utils.SetVColBlendMode(material, UniUnlit.UniUnlitVertexColorBlendOp.Multiply);
|
||||
}
|
||||
|
||||
UniUnlit.Utils.ValidateProperties(material, true);
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
using VRMShaders;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// 所有権を移動する関数。
|
||||
///
|
||||
/// * 所有権が移動する。return true => ImporterContext.Dispose の対象から外れる
|
||||
/// * 所有権が移動しない。return false => Importer.Context.Dispose でDestroyされる
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="o">対象のオブジェクト</param>
|
||||
/// <returns>所有権が移動したらtrue</returns>
|
||||
public delegate bool TakeOwnershipFunc(UnityEngine.Object o);
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// glTFTexture を TextureImportParam に変換する
|
||||
/// </summary>
|
||||
public static class GltfTextureImporter
|
||||
{
|
||||
static Byte[] ToArray(ArraySegment<byte> bytes)
|
||||
{
|
||||
if (bytes.Array == null)
|
||||
{
|
||||
return new byte[] { };
|
||||
}
|
||||
else if (bytes.Offset == 0 && bytes.Count == bytes.Array.Length)
|
||||
{
|
||||
return bytes.Array;
|
||||
}
|
||||
else
|
||||
{
|
||||
Byte[] result = new byte[bytes.Count];
|
||||
Buffer.BlockCopy(bytes.Array, bytes.Offset, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateSRGB(GltfParser parser, int textureIndex, Vector2 offset, Vector2 scale)
|
||||
{
|
||||
var name = CreateNameExt(parser.GLTF, textureIndex, TextureImportTypes.sRGB);
|
||||
var sampler = CreateSampler(parser.GLTF, textureIndex);
|
||||
GetTextureBytesAsync getTextureBytesAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, textureIndex)));
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.sRGB, default, default, getTextureBytesAsync, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateNormal(GltfParser parser, int textureIndex, Vector2 offset, Vector2 scale)
|
||||
{
|
||||
var name = CreateNameExt(parser.GLTF, textureIndex, TextureImportTypes.NormalMap);
|
||||
var sampler = CreateSampler(parser.GLTF, textureIndex);
|
||||
GetTextureBytesAsync getTextureBytesAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, textureIndex)));
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.NormalMap, default, default, getTextureBytesAsync, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateStandard(GltfParser parser, int? metallicRoughnessTextureIndex, int? occlusionTextureIndex, Vector2 offset, Vector2 scale, float metallicFactor, float roughnessFactor)
|
||||
{
|
||||
TextureImportName name = default;
|
||||
|
||||
GetTextureBytesAsync getMetallicRoughnessAsync = default;
|
||||
SamplerParam sampler = default;
|
||||
if (metallicRoughnessTextureIndex.HasValue)
|
||||
{
|
||||
name = CreateNameExt(parser.GLTF, metallicRoughnessTextureIndex.Value, TextureImportTypes.StandardMap);
|
||||
sampler = CreateSampler(parser.GLTF, metallicRoughnessTextureIndex.Value);
|
||||
getMetallicRoughnessAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, metallicRoughnessTextureIndex.Value)));
|
||||
}
|
||||
|
||||
GetTextureBytesAsync getOcclusionAsync = default;
|
||||
if (occlusionTextureIndex.HasValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name.GltfName))
|
||||
{
|
||||
name = CreateNameExt(parser.GLTF, occlusionTextureIndex.Value, TextureImportTypes.StandardMap);
|
||||
}
|
||||
sampler = CreateSampler(parser.GLTF, occlusionTextureIndex.Value);
|
||||
getOcclusionAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, occlusionTextureIndex.Value)));
|
||||
}
|
||||
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.StandardMap, metallicFactor, roughnessFactor, getMetallicRoughnessAsync, getOcclusionAsync, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportName CreateNameExt(glTF gltf, int textureIndex, TextureImportTypes textureType)
|
||||
{
|
||||
if (textureIndex < 0 || textureIndex >= gltf.textures.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var gltfTexture = gltf.textures[textureIndex];
|
||||
if (gltfTexture.source < 0 || gltfTexture.source >= gltf.images.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var gltfImage = gltf.images[gltfTexture.source];
|
||||
return new TextureImportName(textureType, gltfTexture.name, gltfImage.GetExt(), gltfImage.uri);
|
||||
}
|
||||
|
||||
public static SamplerParam CreateSampler(glTF gltf, int index)
|
||||
{
|
||||
var gltfTexture = gltf.textures[index];
|
||||
if (gltfTexture.sampler < 0 || gltfTexture.sampler >= gltf.samplers.Count)
|
||||
{
|
||||
// default
|
||||
return new SamplerParam
|
||||
{
|
||||
FilterMode = FilterMode.Bilinear,
|
||||
WrapModes = new (SamplerWrapType, TextureWrapMode)[] { },
|
||||
};
|
||||
}
|
||||
|
||||
var gltfSampler = gltf.samplers[gltfTexture.sampler];
|
||||
return new SamplerParam
|
||||
{
|
||||
WrapModes = GetUnityWrapMode(gltfSampler).ToArray(),
|
||||
FilterMode = ImportFilterMode(gltfSampler.minFilter),
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<(SamplerWrapType, TextureWrapMode)> GetUnityWrapMode(glTFTextureSampler sampler)
|
||||
{
|
||||
if (sampler.wrapS == sampler.wrapT)
|
||||
{
|
||||
switch (sampler.wrapS)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sampler.wrapS)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
switch (sampler.wrapT)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FilterMode ImportFilterMode(glFilter filterMode)
|
||||
{
|
||||
switch (filterMode)
|
||||
{
|
||||
case glFilter.NEAREST:
|
||||
case glFilter.NEAREST_MIPMAP_LINEAR:
|
||||
case glFilter.NEAREST_MIPMAP_NEAREST:
|
||||
return FilterMode.Point;
|
||||
|
||||
case glFilter.NONE:
|
||||
case glFilter.LINEAR:
|
||||
case glFilter.LINEAR_MIPMAP_NEAREST:
|
||||
return FilterMode.Bilinear;
|
||||
|
||||
case glFilter.LINEAR_MIPMAP_LINEAR:
|
||||
return FilterMode.Trilinear;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 14bc3a4ce3e24924a9ae8bb1654a1b7b
|
||||
guid: c9128dde9a44e1f43a808330ebfba145
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -1,447 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
[Flags]
|
||||
public enum TextureLoadFlags
|
||||
{
|
||||
None = 0,
|
||||
Used = 1,
|
||||
External = 1 << 1,
|
||||
}
|
||||
|
||||
public struct TextureLoadInfo
|
||||
{
|
||||
public readonly Texture2D Texture;
|
||||
public readonly TextureLoadFlags Flags;
|
||||
public bool IsUsed => Flags.HasFlag(TextureLoadFlags.Used);
|
||||
public bool IsExternal => Flags.HasFlag(TextureLoadFlags.External);
|
||||
|
||||
public bool IsSubAsset => IsUsed && !IsExternal;
|
||||
|
||||
public TextureLoadInfo(Texture2D texture, bool used, bool isExternal)
|
||||
{
|
||||
Texture = texture;
|
||||
var flags = TextureLoadFlags.None;
|
||||
if (used)
|
||||
{
|
||||
flags |= TextureLoadFlags.Used;
|
||||
}
|
||||
if (isExternal)
|
||||
{
|
||||
flags |= TextureLoadFlags.External;
|
||||
}
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate Task<Texture2D> GetTextureAsyncFunc(IAwaitCaller awaitCaller, glTF gltf, TextureImportParam param);
|
||||
public class TextureFactory : IDisposable
|
||||
{
|
||||
glTF m_gltf;
|
||||
IStorage m_storage;
|
||||
|
||||
public readonly Dictionary<string, Texture2D> ExternalMap;
|
||||
|
||||
public TextureFactory(glTF gltf, IStorage storage, IEnumerable<(string, UnityEngine.Object)> externalMap)
|
||||
{
|
||||
m_gltf = gltf;
|
||||
m_storage = storage;
|
||||
|
||||
if (externalMap != null)
|
||||
{
|
||||
ExternalMap = externalMap
|
||||
.Select(kv => (kv.Item1, kv.Item2 as Texture2D))
|
||||
.Where(kv => kv.Item2 != null)
|
||||
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Action<UnityEngine.Object> destroy = UnityResourceDestroyer.DestroyResource();
|
||||
foreach (var kv in m_textureCache)
|
||||
{
|
||||
if (!kv.Value.IsExternal)
|
||||
{
|
||||
#if VRM_DEVELOP
|
||||
// Debug.Log($"Destroy {kv.Value.Texture}");
|
||||
#endif
|
||||
destroy(kv.Value.Texture);
|
||||
}
|
||||
}
|
||||
m_textureCache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有権(Dispose権)を移譲する
|
||||
/// </summary>
|
||||
/// <param name="take"></param>
|
||||
public void TransferOwnership(TakeOwnershipFunc take)
|
||||
{
|
||||
var keys = new List<string>();
|
||||
foreach (var x in m_textureCache)
|
||||
{
|
||||
if (x.Value.IsUsed && !x.Value.IsExternal)
|
||||
{
|
||||
// マテリアルから参照されていて
|
||||
// 外部のAssetからロードしていない。
|
||||
if (take(x.Value.Texture))
|
||||
{
|
||||
keys.Add(x.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var x in keys)
|
||||
{
|
||||
m_textureCache.Remove(x);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="string"></typeparam>
|
||||
/// <typeparam name="TextureLoadInfo"></typeparam>
|
||||
/// <returns></returns>
|
||||
Dictionary<string, TextureLoadInfo> m_textureCache = new Dictionary<string, TextureLoadInfo>();
|
||||
|
||||
public IEnumerable<TextureLoadInfo> Textures => m_textureCache.Values;
|
||||
|
||||
static Byte[] ToArray(ArraySegment<byte> bytes)
|
||||
{
|
||||
if (bytes.Array == null)
|
||||
{
|
||||
return new byte[] { };
|
||||
}
|
||||
else if (bytes.Offset == 0 && bytes.Count == bytes.Array.Length)
|
||||
{
|
||||
return bytes.Array;
|
||||
}
|
||||
else
|
||||
{
|
||||
Byte[] result = new byte[bytes.Count];
|
||||
Buffer.BlockCopy(bytes.Array, bytes.Offset, result, 0, result.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
async Task<TextureLoadInfo> GetOrCreateBaseTexture(IAwaitCaller awaitCaller, TextureImportParam param, GetTextureBytesAsync getTextureBytesAsync, RenderTextureReadWrite colorSpace, bool used)
|
||||
{
|
||||
var name = param.GltfName;
|
||||
if (m_textureCache.TryGetValue(name, out TextureLoadInfo cacheInfo))
|
||||
{
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
// not found. load new
|
||||
var imageBytes = await getTextureBytesAsync();
|
||||
|
||||
//
|
||||
// texture from image(png etc) bytes
|
||||
//
|
||||
var texture = new Texture2D(2, 2, TextureFormat.ARGB32, false, colorSpace == RenderTextureReadWrite.Linear);
|
||||
texture.name = name;
|
||||
if (imageBytes != null)
|
||||
{
|
||||
texture.LoadImage(imageBytes);
|
||||
}
|
||||
|
||||
SetSampler(texture, param);
|
||||
|
||||
cacheInfo = new TextureLoadInfo(texture, used, false);
|
||||
m_textureCache.Add(name, cacheInfo);
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
public static void SetSampler(Texture2D texture, TextureImportParam param)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (key, value) in param.Sampler.WrapModes)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case SamplerWrapType.All:
|
||||
texture.wrapMode = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.U:
|
||||
texture.wrapModeU = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.V:
|
||||
texture.wrapModeV = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.W:
|
||||
texture.wrapModeW = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
texture.filterMode = param.Sampler.FilterMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// テクスチャーをロード、必要であれば変換して返す。
|
||||
/// 同じものはキャッシュを返す
|
||||
/// </summary>
|
||||
/// <param name="texture_type">変換の有無を判断する: METALLIC_GLOSS_PROP</param>
|
||||
/// <param name="roughnessFactor">METALLIC_GLOSS_PROPの追加パラメーター</param>
|
||||
/// <param name="indices">gltf の texture index</param>
|
||||
/// <returns></returns>
|
||||
public async Task<Texture2D> GetTextureAsync(IAwaitCaller awaitCaller, glTF gltf, TextureImportParam param)
|
||||
{
|
||||
//
|
||||
// ExtractKey で External とのマッチングを試みる
|
||||
//
|
||||
// Normal => GltfName
|
||||
// Standard => ConvertedName
|
||||
// sRGB => GltfName
|
||||
// Linear => GltfName
|
||||
//
|
||||
if (param.Index0 != null && ExternalMap != null)
|
||||
{
|
||||
if (ExternalMap.TryGetValue(param.ExtractKey, out Texture2D external))
|
||||
{
|
||||
return external;
|
||||
}
|
||||
}
|
||||
|
||||
switch (param.TextureType)
|
||||
{
|
||||
case TextureImportTypes.NormalMap:
|
||||
// Runtime/SubAsset 用に変換する
|
||||
{
|
||||
if (!m_textureCache.TryGetValue(param.ConvertedName, out TextureLoadInfo info))
|
||||
{
|
||||
var baseTexture = await GetOrCreateBaseTexture(awaitCaller, param, param.Index0, RenderTextureReadWrite.Linear, false);
|
||||
var converted = NormalConverter.Import(baseTexture.Texture);
|
||||
converted.name = param.ConvertedName;
|
||||
info = new TextureLoadInfo(converted, true, false);
|
||||
m_textureCache.Add(converted.name, info);
|
||||
}
|
||||
return info.Texture;
|
||||
}
|
||||
|
||||
case TextureImportTypes.StandardMap:
|
||||
// 変換する
|
||||
{
|
||||
if (!m_textureCache.TryGetValue(param.ConvertedName, out TextureLoadInfo info))
|
||||
{
|
||||
TextureLoadInfo baseTexture = default;
|
||||
if (param.Index0!=null)
|
||||
{
|
||||
baseTexture = await GetOrCreateBaseTexture(awaitCaller, param, param.Index0, RenderTextureReadWrite.Linear, false);
|
||||
}
|
||||
TextureLoadInfo occlusionBaseTexture = default;
|
||||
if (param.Index1!=null)
|
||||
{
|
||||
occlusionBaseTexture = await GetOrCreateBaseTexture(awaitCaller, param, param.Index1, RenderTextureReadWrite.Linear, false);
|
||||
}
|
||||
var converted = OcclusionMetallicRoughnessConverter.Import(baseTexture.Texture, param.MetallicFactor, param.RoughnessFactor, occlusionBaseTexture.Texture);
|
||||
converted.name = param.ConvertedName;
|
||||
info = new TextureLoadInfo(converted, true, false);
|
||||
m_textureCache.Add(converted.name, info);
|
||||
}
|
||||
return info.Texture;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var baseTexture = await GetOrCreateBaseTexture(awaitCaller, param, param.Index0, RenderTextureReadWrite.sRGB, true);
|
||||
return baseTexture.Texture;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateSRGB(GltfParser parser, int textureIndex, Vector2 offset, Vector2 scale)
|
||||
{
|
||||
var name = CreateNameExt(parser.GLTF, textureIndex, TextureImportTypes.sRGB);
|
||||
var sampler = CreateSampler(parser.GLTF, textureIndex);
|
||||
GetTextureBytesAsync getTextureBytesAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, textureIndex)));
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.sRGB, default, default, getTextureBytesAsync, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateNormal(GltfParser parser, int textureIndex, Vector2 offset, Vector2 scale)
|
||||
{
|
||||
var name = CreateNameExt(parser.GLTF, textureIndex, TextureImportTypes.NormalMap);
|
||||
var sampler = CreateSampler(parser.GLTF, textureIndex);
|
||||
GetTextureBytesAsync getTextureBytesAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, textureIndex)));
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.NormalMap, default, default, getTextureBytesAsync, default, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportParam CreateStandard(GltfParser parser, int? metallicRoughnessTextureIndex, int? occlusionTextureIndex, Vector2 offset, Vector2 scale, float metallicFactor, float roughnessFactor)
|
||||
{
|
||||
TextureImportName name = default;
|
||||
|
||||
GetTextureBytesAsync getMetallicRoughnessAsync = default;
|
||||
SamplerParam sampler = default;
|
||||
if (metallicRoughnessTextureIndex.HasValue)
|
||||
{
|
||||
name = CreateNameExt(parser.GLTF, metallicRoughnessTextureIndex.Value, TextureImportTypes.StandardMap);
|
||||
sampler = CreateSampler(parser.GLTF, metallicRoughnessTextureIndex.Value);
|
||||
getMetallicRoughnessAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, metallicRoughnessTextureIndex.Value)));
|
||||
}
|
||||
|
||||
GetTextureBytesAsync getOcclusionAsync = default;
|
||||
if (occlusionTextureIndex.HasValue)
|
||||
{
|
||||
if(string.IsNullOrEmpty(name.GltfName)){
|
||||
name = CreateNameExt(parser.GLTF, occlusionTextureIndex.Value, TextureImportTypes.StandardMap);
|
||||
}
|
||||
sampler = CreateSampler(parser.GLTF, occlusionTextureIndex.Value);
|
||||
getOcclusionAsync = () => Task.FromResult(ToArray(parser.GLTF.GetImageBytesFromTextureIndex(parser.Storage, occlusionTextureIndex.Value)));
|
||||
}
|
||||
|
||||
return new TextureImportParam(name, offset, scale, sampler, TextureImportTypes.StandardMap, metallicFactor, roughnessFactor, getMetallicRoughnessAsync, getOcclusionAsync, default, default, default, default);
|
||||
}
|
||||
|
||||
public static TextureImportName CreateNameExt(glTF gltf, int textureIndex, TextureImportTypes textureType)
|
||||
{
|
||||
if (textureIndex < 0 || textureIndex >= gltf.textures.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var gltfTexture = gltf.textures[textureIndex];
|
||||
if (gltfTexture.source < 0 || gltfTexture.source >= gltf.images.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
var gltfImage = gltf.images[gltfTexture.source];
|
||||
return new TextureImportName(textureType, gltfTexture.name, gltfImage.GetExt(), gltfImage.uri);
|
||||
}
|
||||
|
||||
public static SamplerParam CreateSampler(glTF gltf, int index)
|
||||
{
|
||||
var gltfTexture = gltf.textures[index];
|
||||
if (gltfTexture.sampler < 0 || gltfTexture.sampler >= gltf.samplers.Count)
|
||||
{
|
||||
// default
|
||||
return new SamplerParam
|
||||
{
|
||||
FilterMode = FilterMode.Bilinear,
|
||||
WrapModes = new (SamplerWrapType, TextureWrapMode)[] { },
|
||||
};
|
||||
}
|
||||
|
||||
var gltfSampler = gltf.samplers[gltfTexture.sampler];
|
||||
return new SamplerParam
|
||||
{
|
||||
WrapModes = GetUnityWrapMode(gltfSampler).ToArray(),
|
||||
FilterMode = ImportFilterMode(gltfSampler.minFilter),
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<(SamplerWrapType, TextureWrapMode)> GetUnityWrapMode(glTFTextureSampler sampler)
|
||||
{
|
||||
if (sampler.wrapS == sampler.wrapT)
|
||||
{
|
||||
switch (sampler.wrapS)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.All, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sampler.wrapS)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.U, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
switch (sampler.wrapT)
|
||||
{
|
||||
case glWrap.NONE: // default
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.CLAMP_TO_EDGE:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Clamp);
|
||||
break;
|
||||
|
||||
case glWrap.REPEAT:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Repeat);
|
||||
break;
|
||||
|
||||
case glWrap.MIRRORED_REPEAT:
|
||||
yield return (SamplerWrapType.V, TextureWrapMode.Mirror);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FilterMode ImportFilterMode(glFilter filterMode)
|
||||
{
|
||||
switch (filterMode)
|
||||
{
|
||||
case glFilter.NEAREST:
|
||||
case glFilter.NEAREST_MIPMAP_LINEAR:
|
||||
case glFilter.NEAREST_MIPMAP_NEAREST:
|
||||
return FilterMode.Point;
|
||||
|
||||
case glFilter.NONE:
|
||||
case glFilter.LINEAR:
|
||||
case glFilter.LINEAR_MIPMAP_NEAREST:
|
||||
return FilterMode.Bilinear;
|
||||
|
||||
case glFilter.LINEAR_MIPMAP_LINEAR:
|
||||
return FilterMode.Trilinear;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using UnityEngine;
|
||||
using UniJSON;
|
||||
using System.Linq;
|
||||
using VRMShaders;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
|
|
@ -31,12 +32,11 @@ namespace UniGLTF
|
|||
var gltfMaterial = materialExporter.ExportMaterial(srcMaterial, textureManager);
|
||||
gltfMaterial.pbrMetallicRoughness.baseColorTexture.extensions = gltfMaterial.pbrMetallicRoughness.baseColorTexture.extensions.Deserialize();
|
||||
|
||||
var dstMaterial = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
|
||||
Assert.AreEqual(dstMaterial.mainTextureOffset.x, offset.x, 0.3f);
|
||||
Assert.AreEqual(dstMaterial.mainTextureOffset.y, offset.y, 0.2f);
|
||||
Assert.AreEqual(dstMaterial.mainTextureScale.x, scale.x, 0.5f);
|
||||
Assert.AreEqual(dstMaterial.mainTextureScale.y, scale.y, 0.6f);
|
||||
Assert.IsTrue(glTF_KHR_texture_transform.TryGet(gltfMaterial.pbrMetallicRoughness.baseColorTexture, out glTF_KHR_texture_transform t));
|
||||
Assert.AreEqual(t.offset[0], offset.x, 0.3f);
|
||||
Assert.AreEqual(t.offset[1], offset.y, 0.2f);
|
||||
Assert.AreEqual(t.scale[0], scale.x, 0.5f);
|
||||
Assert.AreEqual(t.scale[1], scale.y, 0.6f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -79,8 +79,7 @@ namespace UniGLTF
|
|||
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,8 +97,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -116,8 +114,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -135,8 +132,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -150,8 +146,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -168,8 +163,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -187,8 +181,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -205,8 +198,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -224,8 +216,7 @@ namespace UniGLTF
|
|||
},
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -234,16 +225,14 @@ namespace UniGLTF
|
|||
{
|
||||
extensions = glTF_KHR_materials_unlit.Serialize().Deserialize(),
|
||||
};
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, gltfMaterial);
|
||||
Assert.AreEqual("UniGLTF/UniUnlit", material.shader.name);
|
||||
Assert.IsTrue(glTF_KHR_materials_unlit.IsEnable(gltfMaterial));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MaterialImportTest()
|
||||
{
|
||||
var material = MaterialFactory.CreateMaterialForTest(0, new glTFMaterial { });
|
||||
Assert.AreEqual("Standard", material.shader.name);
|
||||
Assert.IsFalse(glTF_KHR_materials_unlit.IsEnable(new glTFMaterial { }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace VRM
|
|||
//
|
||||
// convert images(metallic roughness, occlusion map)
|
||||
//
|
||||
var task = m_context.MaterialFactory.LoadMaterialsAsync(default(ImmediateCaller), m_context.TextureFactory.GetTextureAsync);
|
||||
var task = m_context.LoadMaterialsAsync();
|
||||
if (!task.IsCompleted)
|
||||
{
|
||||
throw new Exception();
|
||||
|
|
|
|||
107
Assets/VRM/Runtime/IO/MToonMaterialImporter.cs
Normal file
107
Assets/VRM/Runtime/IO/MToonMaterialImporter.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System.Collections.Generic;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public class MToonMaterialImporter
|
||||
{
|
||||
public static bool TryCreateParam(GltfParser parser, int i, glTF_VRM_Material vrmMaterial, out MaterialImportParam param)
|
||||
{
|
||||
if (vrmMaterial.shader == VRM.glTF_VRM_Material.VRM_USE_GLTFSHADER)
|
||||
{
|
||||
// fallback to gltf
|
||||
param = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// restore VRM material
|
||||
//
|
||||
// use material.name, because material name may renamed in GltfParser.
|
||||
var name = parser.GLTF.materials[i].name;
|
||||
param = new MaterialImportParam(name, vrmMaterial.shader);
|
||||
|
||||
param.Actions.Add(material => material.renderQueue = vrmMaterial.renderQueue);
|
||||
|
||||
foreach (var kv in vrmMaterial.floatProperties)
|
||||
{
|
||||
param.FloatValues.Add(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
var offsetScaleMap = new Dictionary<string, float[]>();
|
||||
foreach (var kv in vrmMaterial.vectorProperties)
|
||||
{
|
||||
if (vrmMaterial.textureProperties.ContainsKey(kv.Key))
|
||||
{
|
||||
// texture offset & scale
|
||||
offsetScaleMap.Add(kv.Key, kv.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// vector4
|
||||
var v = new Vector4(kv.Value[0], kv.Value[1], kv.Value[2], kv.Value[3]);
|
||||
param.Vectors.Add(kv.Key, v);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kv in vrmMaterial.textureProperties)
|
||||
{
|
||||
var (offset, scale) = (Vector2.zero, Vector2.one);
|
||||
if (offsetScaleMap.TryGetValue(kv.Key, out float[] value))
|
||||
{
|
||||
offset = new Vector2(value[0], value[1]);
|
||||
scale = new Vector2(value[2], value[3]);
|
||||
}
|
||||
|
||||
var textureParam = MToonTextureParam.Create(parser, kv.Value, offset, scale, kv.Key, 1, 1);
|
||||
param.TextureSlots.Add(kv.Key, textureParam);
|
||||
}
|
||||
|
||||
foreach (var kv in vrmMaterial.keywordMap)
|
||||
{
|
||||
if (kv.Value)
|
||||
{
|
||||
param.Actions.Add(material => material.EnableKeyword(kv.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
param.Actions.Add(material => material.DisableKeyword(kv.Key));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kv in vrmMaterial.tagMap)
|
||||
{
|
||||
param.Actions.Add(material => material.SetOverrideTag(kv.Key, kv.Value));
|
||||
}
|
||||
|
||||
if (vrmMaterial.shader == MToon.Utils.ShaderName)
|
||||
{
|
||||
// TODO: Material拡張にMToonの項目が追加されたら旧バージョンのshaderPropから変換をかける
|
||||
// インポート時にUniVRMに含まれるMToonのバージョンに上書きする
|
||||
param.FloatValues[MToon.Utils.PropVersion] = MToon.Utils.VersionNumber;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
List<glTF_VRM_Material> m_materials;
|
||||
public MToonMaterialImporter(List<glTF_VRM_Material> materials)
|
||||
{
|
||||
m_materials = materials;
|
||||
}
|
||||
|
||||
public bool TryCreateParam(GltfParser parser, int i, out MaterialImportParam param)
|
||||
{
|
||||
if (TryCreateParam(parser, i, m_materials[i], out param))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
param = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 77db57076c0650c469c3ff0d4853ce21
|
||||
timeCreated: 1533624711
|
||||
licenseType: Free
|
||||
guid: 35c90d5d3fa706b4f87a92ce4dc59008
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
|
|
@ -3,19 +3,20 @@ using UniGLTF;
|
|||
using UnityEngine;
|
||||
using VRMShaders;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public static class VRMTextureParam
|
||||
public static class MToonTextureParam
|
||||
{
|
||||
public static TextureImportParam Create(GltfParser parser, int index, Vector2 offset, Vector2 scale, string prop, float metallicFactor, float roughnessFactor)
|
||||
{
|
||||
switch (prop)
|
||||
{
|
||||
case TextureImportParam.NORMAL_PROP:
|
||||
return TextureFactory.CreateNormal(parser, index, offset, scale);
|
||||
return GltfTextureImporter.CreateNormal(parser, index, offset, scale);
|
||||
|
||||
default:
|
||||
return TextureFactory.CreateSRGB(parser, index, offset, scale);
|
||||
return GltfTextureImporter.CreateSRGB(parser, index, offset, scale);
|
||||
|
||||
case TextureImportParam.OCCLUSION_PROP:
|
||||
case TextureImportParam.METALLIC_GLOSS_PROP:
|
||||
11
Assets/VRM/Runtime/IO/MToonTextureParam.cs.meta
Normal file
11
Assets/VRM/Runtime/IO/MToonTextureParam.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7af3a3bafe41a64db660a18b1c25097
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -26,7 +26,7 @@ namespace VRM
|
|||
{
|
||||
VRM = vrm;
|
||||
// override material importer
|
||||
MaterialFactory.CreateMaterialAsync = new VRMMaterialImporter(VRM.materialProperties).CreateMaterialAsync;
|
||||
GltfMaterialImporter.GltfMaterialParamProcessors.Insert(0, new MToonMaterialImporter(VRM.materialProperties).TryCreateParam);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -297,7 +297,7 @@ namespace VRM
|
|||
meta.Title = gltfMeta.title;
|
||||
if (gltfMeta.texture >= 0)
|
||||
{
|
||||
meta.Thumbnail = await TextureFactory.GetTextureAsync(awaitCaller, GLTF, TextureFactory.CreateSRGB(Parser, gltfMeta.texture, Vector2.zero, Vector2.one));
|
||||
meta.Thumbnail = await TextureFactory.GetTextureAsync(GltfTextureImporter.CreateSRGB(Parser, gltfMeta.texture, Vector2.zero, Vector2.one));
|
||||
}
|
||||
meta.AllowedUser = gltfMeta.allowedUser;
|
||||
meta.ViolentUssage = gltfMeta.violentUssage;
|
||||
|
|
@ -311,7 +311,7 @@ namespace VRM
|
|||
return meta;
|
||||
}
|
||||
|
||||
public override void TransferOwnership(TakeOwnershipFunc take)
|
||||
public override void TransferOwnership(Func<UnityEngine.Object, bool> take)
|
||||
{
|
||||
// VRM 固有のリソース(ScriptableObject)
|
||||
if (take(HumanoidAvatar))
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public static class MToonMaterialItem
|
||||
{
|
||||
static string[] VRM_SHADER_NAMES =
|
||||
{
|
||||
"Standard",
|
||||
"VRM/MToon",
|
||||
"UniGLTF/UniUnlit",
|
||||
|
||||
"VRM/UnlitTexture",
|
||||
"VRM/UnlitCutout",
|
||||
"VRM/UnlitTransparent",
|
||||
"VRM/UnlitTransparentZWrite",
|
||||
};
|
||||
|
||||
public static async Task<Material> CreateAsync(IAwaitCaller awaitCaller, GltfParser parser, int m_index, glTF_VRM_Material vrmMaterial, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
var item = vrmMaterial;
|
||||
var shaderName = item.shader;
|
||||
var shader = Shader.Find(shaderName);
|
||||
if (shader == null)
|
||||
{
|
||||
//
|
||||
// no shader
|
||||
//
|
||||
if (VRM_SHADER_NAMES.Contains(shaderName))
|
||||
{
|
||||
Debug.LogErrorFormat("shader {0} not found. set Assets/VRM/Shaders/VRMShaders to Edit - project setting - Graphics - preloaded shaders", shaderName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// #if VRM_DEVELOP
|
||||
// Debug.LogWarningFormat("unknown shader {0}.", shaderName);
|
||||
// #endif
|
||||
}
|
||||
return await MaterialFactory.DefaultCreateMaterialAsync(awaitCaller, parser, m_index, getTexture);
|
||||
}
|
||||
|
||||
//
|
||||
// restore VRM material
|
||||
//
|
||||
var material = new Material(shader);
|
||||
// use material.name, because material name may renamed in GltfParser.
|
||||
material.name = parser.GLTF.materials[m_index].name;
|
||||
material.renderQueue = item.renderQueue;
|
||||
|
||||
foreach (var kv in item.floatProperties)
|
||||
{
|
||||
material.SetFloat(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
var offsetScaleMap = new Dictionary<string, float[]>();
|
||||
foreach (var kv in item.vectorProperties)
|
||||
{
|
||||
if (item.textureProperties.ContainsKey(kv.Key))
|
||||
{
|
||||
// texture offset & scale
|
||||
offsetScaleMap.Add(kv.Key, kv.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// vector4
|
||||
var v = new Vector4(kv.Value[0], kv.Value[1], kv.Value[2], kv.Value[3]);
|
||||
material.SetVector(kv.Key, v);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kv in item.textureProperties)
|
||||
{
|
||||
var (offset, scale) = (Vector2.zero, Vector2.one);
|
||||
if (offsetScaleMap.TryGetValue(kv.Key, out float[] value))
|
||||
{
|
||||
offset = new Vector2(value[0], value[1]);
|
||||
scale = new Vector2(value[2], value[3]);
|
||||
}
|
||||
|
||||
var param = VRMTextureParam.Create(parser, kv.Value, offset, scale, kv.Key, 1, 1);
|
||||
var texture = await getTexture(awaitCaller, parser.GLTF, param);
|
||||
if (texture != null)
|
||||
{
|
||||
material.SetTexture(kv.Key, texture);
|
||||
MaterialFactory.SetTextureOffsetAndScale(material, kv.Key, offset, scale);
|
||||
}
|
||||
}
|
||||
foreach (var kv in item.keywordMap)
|
||||
{
|
||||
if (kv.Value)
|
||||
{
|
||||
material.EnableKeyword(kv.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
material.DisableKeyword(kv.Key);
|
||||
}
|
||||
}
|
||||
foreach (var kv in item.tagMap)
|
||||
{
|
||||
material.SetOverrideTag(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
if (shaderName == MToon.Utils.ShaderName)
|
||||
{
|
||||
// TODO: Material拡張にMToonの項目が追加されたら旧バージョンのshaderPropから変換をかける
|
||||
// インポート時にUniVRMに含まれるMToonのバージョンに上書きする
|
||||
material.SetFloat(MToon.Utils.PropVersion, MToon.Utils.VersionNumber);
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
public class VRMMaterialImporter
|
||||
{
|
||||
List<glTF_VRM_Material> m_materials;
|
||||
public VRMMaterialImporter(List<glTF_VRM_Material> materials)
|
||||
{
|
||||
m_materials = materials;
|
||||
}
|
||||
|
||||
public Task<Material> CreateMaterialAsync(IAwaitCaller awaitCaller, GltfParser parser, int i, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
if (i == 0 && m_materials.Count == 0)
|
||||
{
|
||||
// dummy
|
||||
return MaterialFactory.DefaultCreateMaterialAsync(awaitCaller, parser, i, getTexture);
|
||||
}
|
||||
|
||||
return MToonMaterialItem.CreateAsync(awaitCaller, parser, i, m_materials[i], getTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ namespace VRM
|
|||
}
|
||||
|
||||
// SRGB color or normalmap
|
||||
yield return VRMTextureParam.Create(parser, kv.Value, offset, scale, kv.Key, default, default);
|
||||
yield return MToonTextureParam.Create(parser, kv.Value, offset, scale, kv.Key, default, default);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ namespace VRM
|
|||
// thumbnail
|
||||
if (m_vrm.meta != null && m_vrm.meta.texture != -1)
|
||||
{
|
||||
var textureInfo = TextureFactory.CreateSRGB(parser, m_vrm.meta.texture, Vector2.zero, Vector2.one);
|
||||
var textureInfo = GltfTextureImporter.CreateSRGB(parser, m_vrm.meta.texture, Vector2.zero, Vector2.one);
|
||||
if (used.Add(textureInfo.ExtractKey))
|
||||
{
|
||||
yield return textureInfo;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace VRM
|
|||
var vrmMaterial = VRMMaterialExporter.CreateFromMaterial(srcMaterial, textureManager);
|
||||
Assert.AreEqual(vrmMaterial.vectorProperties["_MainTex"], new float[]{0.3f, 0.2f, 0.5f, 0.6f});
|
||||
|
||||
var materialImporter = new VRMMaterialImporter(new System.Collections.Generic.List<glTF_VRM_Material>{ vrmMaterial });
|
||||
var materialImporter = new MToonMaterialImporter(new System.Collections.Generic.List<glTF_VRM_Material>{ vrmMaterial });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
163
Assets/VRMShaders/Runtime/MaterialFactory.cs
Normal file
163
Assets/VRMShaders/Runtime/MaterialFactory.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRMShaders
|
||||
{
|
||||
public delegate Task<Texture2D> GetTextureAsyncFunc(TextureImportParam param);
|
||||
|
||||
public class MaterialFactory : IDisposable
|
||||
{
|
||||
Dictionary<string, Material> m_externalMap;
|
||||
|
||||
public MaterialFactory(IEnumerable<(string, UnityEngine.Object)> externalMap)
|
||||
{
|
||||
if (externalMap == null)
|
||||
{
|
||||
externalMap = Enumerable.Empty<(string, UnityEngine.Object)>();
|
||||
}
|
||||
m_externalMap = externalMap
|
||||
.Select(kv => (kv.Item1, kv.Item2 as Material))
|
||||
.Where(kv => kv.Item2 != null)
|
||||
.ToDictionary(kv => kv.Item1, kv => kv.Item2)
|
||||
;
|
||||
}
|
||||
|
||||
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(MaterialImportParam param, GetTextureAsyncFunc getTexture)
|
||||
{
|
||||
if (m_externalMap.TryGetValue(param.Name, out Material material))
|
||||
{
|
||||
m_materials.Add(new MaterialLoadInfo(material, true));
|
||||
return material;
|
||||
}
|
||||
|
||||
if (getTexture == null)
|
||||
{
|
||||
getTexture = (_) => Task.FromResult<Texture2D>(null);
|
||||
}
|
||||
|
||||
material = new Material(Shader.Find(param.ShaderName));
|
||||
material.name = param.Name;
|
||||
|
||||
foreach(var kv in param.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 param.Colors)
|
||||
{
|
||||
material.SetColor(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
foreach(var kv in param.Vectors)
|
||||
{
|
||||
material.SetVector(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
foreach(var kv in param.FloatValues)
|
||||
{
|
||||
material.SetFloat(kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
foreach(var action in param.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Assets/VRMShaders/Runtime/MaterialImportParam.cs
Normal file
24
Assets/VRMShaders/Runtime/MaterialImportParam.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRMShaders
|
||||
{
|
||||
public class MaterialImportParam
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly string ShaderName;
|
||||
public readonly Dictionary<string, TextureImportParam> TextureSlots = new Dictionary<string, TextureImportParam>();
|
||||
public readonly Dictionary<string, float> FloatValues = new Dictionary<string, float>();
|
||||
public readonly Dictionary<string, Color> Colors = new Dictionary<string, Color>();
|
||||
public readonly Dictionary<string, Vector4> Vectors = new Dictionary<string, Vector4>();
|
||||
public readonly List<Action<Material>> Actions = new List<Action<Material>>();
|
||||
|
||||
public MaterialImportParam(string name, string shaderName)
|
||||
{
|
||||
Name = name;
|
||||
ShaderName = shaderName;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRMShaders/Runtime/MaterialImportParam.cs.meta
Normal file
11
Assets/VRMShaders/Runtime/MaterialImportParam.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0844821341efaf644b91bff72f813a2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
261
Assets/VRMShaders/Runtime/TextureFactory.cs
Normal file
261
Assets/VRMShaders/Runtime/TextureFactory.cs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace VRMShaders
|
||||
{
|
||||
[Flags]
|
||||
public enum TextureLoadFlags
|
||||
{
|
||||
None = 0,
|
||||
Used = 1,
|
||||
External = 1 << 1,
|
||||
}
|
||||
|
||||
public struct TextureLoadInfo
|
||||
{
|
||||
public readonly Texture2D Texture;
|
||||
public readonly TextureLoadFlags Flags;
|
||||
public bool IsUsed => Flags.HasFlag(TextureLoadFlags.Used);
|
||||
public bool IsExternal => Flags.HasFlag(TextureLoadFlags.External);
|
||||
|
||||
public bool IsSubAsset => IsUsed && !IsExternal;
|
||||
|
||||
public TextureLoadInfo(Texture2D texture, bool used, bool isExternal)
|
||||
{
|
||||
Texture = texture;
|
||||
var flags = TextureLoadFlags.None;
|
||||
if (used)
|
||||
{
|
||||
flags |= TextureLoadFlags.Used;
|
||||
}
|
||||
if (isExternal)
|
||||
{
|
||||
flags |= TextureLoadFlags.External;
|
||||
}
|
||||
Flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
public class TextureFactory : IDisposable
|
||||
{
|
||||
public readonly Dictionary<string, Texture2D> ExternalMap;
|
||||
|
||||
public TextureFactory(IEnumerable<(string, UnityEngine.Object)> externalMap)
|
||||
{
|
||||
if (externalMap != null)
|
||||
{
|
||||
ExternalMap = externalMap
|
||||
.Select(kv => (kv.Item1, kv.Item2 as Texture2D))
|
||||
.Where(kv => kv.Item2 != null)
|
||||
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
public static Action<UnityEngine.Object> DestroyResource()
|
||||
{
|
||||
Action<UnityEngine.Object> des = (UnityEngine.Object o) => UnityEngine.Object.Destroy(o);
|
||||
Action<UnityEngine.Object> desi = (UnityEngine.Object o) => UnityEngine.Object.DestroyImmediate(o);
|
||||
Action<UnityEngine.Object> func = Application.isPlaying
|
||||
? des
|
||||
: desi
|
||||
;
|
||||
return func;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Action<UnityEngine.Object> destroy = DestroyResource();
|
||||
foreach (var kv in m_textureCache)
|
||||
{
|
||||
if (!kv.Value.IsExternal)
|
||||
{
|
||||
#if VRM_DEVELOP
|
||||
// Debug.Log($"Destroy {kv.Value.Texture}");
|
||||
#endif
|
||||
destroy(kv.Value.Texture);
|
||||
}
|
||||
}
|
||||
m_textureCache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有権(Dispose権)を移譲する
|
||||
/// </summary>
|
||||
/// <param name="take"></param>
|
||||
public void TransferOwnership(Func<UnityEngine.Object, bool> take)
|
||||
{
|
||||
var keys = new List<string>();
|
||||
foreach (var x in m_textureCache)
|
||||
{
|
||||
if (x.Value.IsUsed && !x.Value.IsExternal)
|
||||
{
|
||||
// マテリアルから参照されていて
|
||||
// 外部のAssetからロードしていない。
|
||||
if (take(x.Value.Texture))
|
||||
{
|
||||
keys.Add(x.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var x in keys)
|
||||
{
|
||||
m_textureCache.Remove(x);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="string"></typeparam>
|
||||
/// <typeparam name="TextureLoadInfo"></typeparam>
|
||||
/// <returns></returns>
|
||||
Dictionary<string, TextureLoadInfo> m_textureCache = new Dictionary<string, TextureLoadInfo>();
|
||||
|
||||
public IEnumerable<TextureLoadInfo> Textures => m_textureCache.Values;
|
||||
|
||||
|
||||
async Task<TextureLoadInfo> GetOrCreateBaseTexture(TextureImportParam param, GetTextureBytesAsync getTextureBytesAsync, RenderTextureReadWrite colorSpace, bool used)
|
||||
{
|
||||
var name = param.GltfName;
|
||||
if (m_textureCache.TryGetValue(name, out TextureLoadInfo cacheInfo))
|
||||
{
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
// not found. load new
|
||||
var imageBytes = await getTextureBytesAsync();
|
||||
|
||||
//
|
||||
// texture from image(png etc) bytes
|
||||
//
|
||||
var texture = new Texture2D(2, 2, TextureFormat.ARGB32, false, colorSpace == RenderTextureReadWrite.Linear);
|
||||
texture.name = name;
|
||||
if (imageBytes != null)
|
||||
{
|
||||
texture.LoadImage(imageBytes);
|
||||
}
|
||||
|
||||
SetSampler(texture, param);
|
||||
|
||||
cacheInfo = new TextureLoadInfo(texture, used, false);
|
||||
m_textureCache.Add(name, cacheInfo);
|
||||
return cacheInfo;
|
||||
}
|
||||
|
||||
public static void SetSampler(Texture2D texture, TextureImportParam param)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (key, value) in param.Sampler.WrapModes)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case SamplerWrapType.All:
|
||||
texture.wrapMode = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.U:
|
||||
texture.wrapModeU = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.V:
|
||||
texture.wrapModeV = value;
|
||||
break;
|
||||
|
||||
case SamplerWrapType.W:
|
||||
texture.wrapModeW = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
texture.filterMode = param.Sampler.FilterMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// テクスチャーをロード、必要であれば変換して返す。
|
||||
/// 同じものはキャッシュを返す
|
||||
/// </summary>
|
||||
/// <param name="texture_type">変換の有無を判断する: METALLIC_GLOSS_PROP</param>
|
||||
/// <param name="roughnessFactor">METALLIC_GLOSS_PROPの追加パラメーター</param>
|
||||
/// <param name="indices">gltf の texture index</param>
|
||||
/// <returns></returns>
|
||||
public async Task<Texture2D> GetTextureAsync(TextureImportParam param)
|
||||
{
|
||||
//
|
||||
// ExtractKey で External とのマッチングを試みる
|
||||
//
|
||||
// Normal => GltfName
|
||||
// Standard => ConvertedName
|
||||
// sRGB => GltfName
|
||||
// Linear => GltfName
|
||||
//
|
||||
if (param.Index0 != null && ExternalMap != null)
|
||||
{
|
||||
if (ExternalMap.TryGetValue(param.ExtractKey, out Texture2D external))
|
||||
{
|
||||
return external;
|
||||
}
|
||||
}
|
||||
|
||||
switch (param.TextureType)
|
||||
{
|
||||
case TextureImportTypes.NormalMap:
|
||||
// Runtime/SubAsset 用に変換する
|
||||
{
|
||||
if (!m_textureCache.TryGetValue(param.ConvertedName, out TextureLoadInfo info))
|
||||
{
|
||||
var baseTexture = await GetOrCreateBaseTexture(param, param.Index0, RenderTextureReadWrite.Linear, false);
|
||||
var converted = NormalConverter.Import(baseTexture.Texture);
|
||||
converted.name = param.ConvertedName;
|
||||
info = new TextureLoadInfo(converted, true, false);
|
||||
m_textureCache.Add(converted.name, info);
|
||||
}
|
||||
return info.Texture;
|
||||
}
|
||||
|
||||
case TextureImportTypes.StandardMap:
|
||||
// 変換する
|
||||
{
|
||||
if (!m_textureCache.TryGetValue(param.ConvertedName, out TextureLoadInfo info))
|
||||
{
|
||||
TextureLoadInfo baseTexture = default;
|
||||
if (param.Index0!=null)
|
||||
{
|
||||
baseTexture = await GetOrCreateBaseTexture(param, param.Index0, RenderTextureReadWrite.Linear, false);
|
||||
}
|
||||
TextureLoadInfo occlusionBaseTexture = default;
|
||||
if (param.Index1!=null)
|
||||
{
|
||||
occlusionBaseTexture = await GetOrCreateBaseTexture(param, param.Index1, RenderTextureReadWrite.Linear, false);
|
||||
}
|
||||
var converted = OcclusionMetallicRoughnessConverter.Import(baseTexture.Texture, param.MetallicFactor, param.RoughnessFactor, occlusionBaseTexture.Texture);
|
||||
converted.name = param.ConvertedName;
|
||||
info = new TextureLoadInfo(converted, true, false);
|
||||
m_textureCache.Add(converted.name, info);
|
||||
}
|
||||
return info.Texture;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var baseTexture = await GetOrCreateBaseTexture(param, param.Index0, RenderTextureReadWrite.sRGB, true);
|
||||
return baseTexture.Texture;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user