diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs index 91061f642..fba3887a2 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs @@ -90,7 +90,7 @@ namespace UniGLTF if (info.Materials.Take(info.Mesh.subMeshCount).Any(x => x == null)) { // material に null が含まれる(unity で magenta になっているはず) - yield return Validation.Error(Messages.MATERIALS_CONTAINS_NULL.Msg(), ValidationContext.Create(info.Renderers[0].Item1)); + yield return Validation.Warning(Messages.MATERIALS_CONTAINS_NULL.Msg(), ValidationContext.Create(info.Renderers[0].Item1)); } } diff --git a/Assets/UniGLTF/Runtime/Extensions/IndexExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/IndexExtensions.cs index 7599e5b95..e855768a5 100644 --- a/Assets/UniGLTF/Runtime/Extensions/IndexExtensions.cs +++ b/Assets/UniGLTF/Runtime/Extensions/IndexExtensions.cs @@ -15,5 +15,24 @@ namespace UniGLTF } return true; } + + public static bool HasValidIndex(this int? self, int collectionLength) + { + if (!self.HasValue) + { + return false; + } + if (self.Value < 0) + { + // 古いモデルで index の無効値に -1 を使っている場合がある + return false; + } + if (self.Value >= collectionLength) + { + // ついでに上限もチェック + return false; + } + return true; + } } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/Format/GltfSerializer.g.cs b/Assets/UniGLTF/Runtime/UniGLTF/Format/GltfSerializer.g.cs index 326834f80..b4227c291 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/Format/GltfSerializer.g.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/Format/GltfSerializer.g.cs @@ -972,9 +972,9 @@ public static void Serialize_gltf_meshes__primitives_ITEM(JsonFormatter f, glTFP Serialize_gltf_meshes__primitives__attributes(f, value.attributes); } - if(value.material>=0){ + if(value.material.HasValidIndex()){ f.Key("material"); - f.Value(value.material); + f.Value(value.material.Value); } if(value.targets!=null&&value.targets.Count>=1){ diff --git a/Assets/UniGLTF/Runtime/UniGLTF/Format/glTFMesh.cs b/Assets/UniGLTF/Runtime/UniGLTF/Format/glTFMesh.cs index 9d945e064..bc65a325c 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/Format/glTFMesh.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/Format/glTFMesh.cs @@ -85,7 +85,7 @@ namespace UniGLTF public glTFAttributes attributes; [JsonSchema(Minimum = 0)] - public int material; + public int? material; [JsonSchema(MinItems = 1, ExplicitIgnorableItemLength = 0)] [ItemJsonSchema(SkipSchemaComparison = true)] diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs index e05d60b09..a2605d732 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs @@ -49,7 +49,8 @@ namespace UniGLTF Data.MigrationFlags.IsRoughnessTextureValueSquared); MaterialFactory = new MaterialFactory(ExternalObjectMap .Where(x => x.Value is Material) - .ToDictionary(x => x.Key, x => (Material)x.Value)); + .ToDictionary(x => x.Key, x => (Material)x.Value), + MaterialDescriptorGenerator.GetGltfDefault()); AnimationClipFactory = new AnimationClipFactory(ExternalObjectMap .Where(x => x.Value is AnimationClip) .ToDictionary(x => x.Key, x => (AnimationClip)x.Value)); @@ -285,14 +286,7 @@ namespace UniGLTF throw new ArgumentNullException(); } - if (Data.GLTF.materials == null || Data.GLTF.materials.Count == 0) - { - // no material. work around. - // TODO: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#default-material - var param = MaterialDescriptorGenerator.GetGltfDefault(); - await MaterialFactory.LoadAsync(param, TextureFactory.GetTextureAsync, awaitCaller); - } - else + if (Data.GLTF.materials != null) { for (int i = 0; i < Data.GLTF.materials.Count; ++i) { @@ -313,7 +307,18 @@ namespace UniGLTF { using (MeasureTime("BuildMesh")) { - var meshWithMaterials = await MeshUploader.BuildMeshAndUploadAsync(awaitCaller, meshData, MaterialFactory.GetMaterial); + var meshWithMaterials = await MeshUploader.BuildMeshAndUploadAsync(awaitCaller, meshData, + (int? materialIndex) => + { + if (materialIndex.HasValidIndex()) + { + return MaterialFactory.GetMaterial(materialIndex.Value); + } + else + { + return MaterialFactory.DefaultMaterial; + } + }); var mesh = meshWithMaterials.Mesh; // mesh name diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs index 2bffd253a..b96f05c9f 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs @@ -12,16 +12,16 @@ namespace UniGLTF { private int _currentVertexCount = 0; private int _currentIndexCount = 0; - + private NativeArray _indices; private NativeArray _vertices0; private NativeArray _vertices1; private NativeArray _vertices2; - + private readonly List _subMeshes = new List(); - private readonly List _materialIndices = new List(); + private readonly List _materialIndices = new List(); private readonly List _blendShapes = new List(); - + public NativeArray Vertices0 => _vertices0.GetSubArray(0, _currentVertexCount); public NativeArray Vertices1 => _vertices1.GetSubArray(0, _currentVertexCount); public NativeArray Vertices2 => _vertices2.GetSubArray(0, _currentVertexCount); @@ -29,7 +29,7 @@ namespace UniGLTF public NativeArray Indices => _indices.GetSubArray(0, _currentIndexCount); public IReadOnlyList SubMeshes => _subMeshes; - public IReadOnlyList MaterialIndices => _materialIndices; + public IReadOnlyList MaterialIndices => _materialIndices; public IReadOnlyList BlendShapes => _blendShapes; @@ -336,7 +336,7 @@ namespace UniGLTF var texCoord1 = texCoords1 != null ? texCoords1.Value[i].ReverseUV() : Vector2.zero; var color = colors != null ? colors.Value[i] : Color.white; - + _vertices0[_currentVertexCount] = new MeshVertex0( position, normal diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs index e796b4fb2..133ad6c11 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs @@ -110,7 +110,7 @@ namespace UniGLTF m_weights.Add(new Vector4(boneWeight.weight0, boneWeight.weight1, boneWeight.weight2, boneWeight.weight3)); } - public glTFPrimitives ToGltfPrimitive(ExportingGltfData data, int materialIndex, IEnumerable indices) + public glTFPrimitives ToGltfPrimitive(ExportingGltfData data, int? materialIndex, IEnumerable indices) { var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.Select(x => (uint)m_vertexIndexMap[x]).ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); var positions = m_positions.ToArray(); diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs index 5540a6df3..9fefcc477 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs @@ -91,7 +91,7 @@ namespace UniGLTF public static async Task BuildMeshAndUploadAsync( IAwaitCaller awaitCaller, MeshData data, - Func materialFromIndex) + Func materialFromIndex) { //Debug.Log(prims.ToJson()); diff --git a/Assets/UniGLTF/Tests/UniGLTF/UniGLTFTests.cs b/Assets/UniGLTF/Tests/UniGLTF/UniGLTFTests.cs index 73c9e7c07..d2770cd22 100644 --- a/Assets/UniGLTF/Tests/UniGLTF/UniGLTFTests.cs +++ b/Assets/UniGLTF/Tests/UniGLTF/UniGLTFTests.cs @@ -602,10 +602,10 @@ namespace UniGLTF Assert.AreEqual(2, gltf.meshes.Count); - var red = gltf.materials[gltf.meshes[0].primitives[0].material]; + var red = gltf.materials[gltf.meshes[0].primitives[0].material.Value]; Assert.AreEqual(new float[] { 1, 0, 0, 1 }, red.pbrMetallicRoughness.baseColorFactor); - var blue = gltf.materials[gltf.meshes[1].primitives[0].material]; + var blue = gltf.materials[gltf.meshes[1].primitives[0].material.Value]; Assert.AreEqual(new float[] { 0, 0, 1, 1 }, blue.pbrMetallicRoughness.baseColorFactor); Assert.AreEqual(2, gltf.nodes.Count); diff --git a/Assets/VRM10/Runtime/IO/Model/ModelExporter.cs b/Assets/VRM10/Runtime/IO/Model/ModelExporter.cs index dd9e3d044..b7f3302e9 100644 --- a/Assets/VRM10/Runtime/IO/Model/ModelExporter.cs +++ b/Assets/VRM10/Runtime/IO/Model/ModelExporter.cs @@ -112,6 +112,10 @@ namespace UniVRM10 var materials = renderer.sharedMaterials; // avoid copy foreach (var material in materials) { + if (material == null) + { + continue; + } if (Materials.Contains(material)) { continue; diff --git a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs index 98d7ac94b..352e8807d 100644 --- a/Assets/VRM10/Runtime/IO/Vrm10Importer.cs +++ b/Assets/VRM10/Runtime/IO/Vrm10Importer.cs @@ -188,7 +188,19 @@ namespace UniVRM10 Meshes.Add(new MeshWithMaterials { Mesh = mesh, - Materials = src.Meshes[0].Submeshes.Select(x => MaterialFactory.Materials[x.Material].Asset).ToArray(), + Materials = src.Meshes[0].Submeshes.Select( + x => + { + if (x.Material.HasValidIndex()) + { + return MaterialFactory.Materials[x.Material.Value].Asset; + } + else + { + return null; + } + } + ).ToArray(), }); @@ -228,7 +240,7 @@ namespace UniVRM10 continue; } - CreateRenderer(node, go, map, MaterialFactory.Materials); + CreateRenderer(node, go, map, MaterialFactory); await awaitCaller.NextFrame(); } } @@ -790,8 +802,7 @@ namespace UniVRM10 /// /// MeshFilter + MeshRenderer もしくは SkinnedMeshRenderer を構築する /// - public static Renderer CreateRenderer(VrmLib.Node node, GameObject go, ModelMap map, - IReadOnlyList materialLoadInfos) + public static Renderer CreateRenderer(VrmLib.Node node, GameObject go, ModelMap map, MaterialFactory materialFactory) { Renderer renderer = null; var hasBlendShape = node.MeshGroup.Meshes[0].MorphTargets.Any(); @@ -825,12 +836,35 @@ namespace UniVRM10 } else if (node.MeshGroup.Meshes.Count == 1) { - var materials = node.MeshGroup.Meshes[0].Submeshes.Select(x => materialLoadInfos[x.Material].Asset).ToArray(); + var materials = node.MeshGroup.Meshes[0].Submeshes.Select( + x => + { + if (x.Material.HasValidIndex()) + { + return materialFactory.Materials[x.Material.Value].Asset; + } + else + { + return materialFactory.DefaultMaterial; + } + } + ).ToArray(); renderer.sharedMaterials = materials; } else { - var materials = node.MeshGroup.Meshes.Select(x => materialLoadInfos[x.Submeshes[0].Material].Asset).ToArray(); + var materials = node.MeshGroup.Meshes.Select(x => + { + if (x.Submeshes[0].Material.HasValidIndex()) + { + return materialFactory.Materials[x.Submeshes[0].Material.Value].Asset; + } + else + { + return materialFactory.DefaultMaterial; + } + } + ).ToArray(); renderer.sharedMaterials = materials; } diff --git a/Assets/VRM10/vrmlib/Runtime/Mesh.cs b/Assets/VRM10/vrmlib/Runtime/Mesh.cs index ceb69e45e..2a7e7790f 100644 --- a/Assets/VRM10/vrmlib/Runtime/Mesh.cs +++ b/Assets/VRM10/vrmlib/Runtime/Mesh.cs @@ -35,18 +35,18 @@ namespace VrmLib { public int Offset; public int DrawCount; - public int Material; + public int? Material; public override string ToString() { return $"{Material}({DrawCount})"; } - public Submesh(int material) : this(0, 0, material) + public Submesh(int? material) : this(0, 0, material) { } - public Submesh(int offset, int drawCount, int material) + public Submesh(int offset, int drawCount, int? material) { Offset = offset; DrawCount = drawCount; diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/Material/Importer/MaterialFactory.cs b/Assets/VRMShaders/GLTF/IO/Runtime/Material/Importer/MaterialFactory.cs index 882818fbb..0e578c92c 100644 --- a/Assets/VRMShaders/GLTF/IO/Runtime/Material/Importer/MaterialFactory.cs +++ b/Assets/VRMShaders/GLTF/IO/Runtime/Material/Importer/MaterialFactory.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using UnityEngine; @@ -13,9 +12,33 @@ namespace VRMShaders { private readonly IReadOnlyDictionary m_externalMap; - public MaterialFactory(IReadOnlyDictionary externalMaterialMap) + MaterialDescriptor m_defaultMaterialParams; + + /// + /// gltfPritmitive.material が無い場合のデフォルトマテリアル + /// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#default-material + /// + Material m_defaultMaterial; + + public Material DefaultMaterial + { + get + { + if (m_defaultMaterial == null) + { + // default material にバリエーションがある? + var task = LoadAsync(m_defaultMaterialParams, (_x, _y) => Task.FromResult(null), new ImmediateCaller()); + task.Wait(); + m_defaultMaterial = task.Result; + } + return m_defaultMaterial; + } + } + + public MaterialFactory(IReadOnlyDictionary externalMaterialMap, MaterialDescriptor defaultMaterialParams) { m_externalMap = externalMaterialMap; + m_defaultMaterialParams = defaultMaterialParams; } public struct MaterialLoadInfo