diff --git a/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs b/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs
new file mode 100644
index 000000000..49e12de51
--- /dev/null
+++ b/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using UniJSON;
+
+
+namespace UniGLTF
+{
+ ///
+ /// https://github.com/KhronosGroup/glTF/issues/1036
+ ///
+ [Serializable]
+ public partial class glTFMesh_extras : ExtraBase
+ {
+ [JsonSchema(Required = true, MinItems = 1)]
+ public List targetNames = new List();
+
+ [JsonSerializeMembers]
+ void PrimitiveMembers(GLTFJsonFormatter f)
+ {
+ if (targetNames.Count > 0)
+ {
+ f.Key("targetNames");
+ f.BeginList();
+ foreach (var x in targetNames)
+ {
+ f.Value(x);
+ }
+ f.EndList();
+ }
+ }
+ }
+}
diff --git a/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs.meta b/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs.meta
new file mode 100644
index 000000000..139962cd6
--- /dev/null
+++ b/Assets/VRM/UniGLTF/Scripts/Format/ExtensionsAndExtras/glTFMesh.extras.targetNames.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 97b77d4c1c0d44b594e9187e41b3152d
+timeCreated: 1582269878
\ No newline at end of file
diff --git a/Assets/VRM/UniGLTF/Scripts/Format/glTFMesh.cs b/Assets/VRM/UniGLTF/Scripts/Format/glTFMesh.cs
index 072084d3d..c2d11a466 100644
--- a/Assets/VRM/UniGLTF/Scripts/Format/glTFMesh.cs
+++ b/Assets/VRM/UniGLTF/Scripts/Format/glTFMesh.cs
@@ -146,9 +146,11 @@ namespace UniGLTF
[JsonSchema(MinItems = 1)]
public float[] weights;
+ [JsonSchema(SkipSchemaComparison = true)]
+ public glTFMesh_extras extras = null;
+
// empty schemas
public object extensions;
- public object extras;
public glTFMesh()
{
diff --git a/Assets/VRM/UniGLTF/Scripts/IO/MeshImporter.cs b/Assets/VRM/UniGLTF/Scripts/IO/MeshImporter.cs
index d30be935a..e233eeacf 100644
--- a/Assets/VRM/UniGLTF/Scripts/IO/MeshImporter.cs
+++ b/Assets/VRM/UniGLTF/Scripts/IO/MeshImporter.cs
@@ -12,6 +12,155 @@ namespace UniGLTF
const float FRAME_WEIGHT = 100.0f;
+ // mesh is sharing morph targets.
+ private static MeshContext _ImportMeshSharingMorphTarget(ImporterContext ctx, glTFMesh gltfMesh)
+ {
+ var positions = new List();
+ var normals = new List();
+ var tangents = new List();
+ var uv = new List();
+ var colors = new List();
+ var blendShapes = new List();
+ var meshContext = new MeshContext();
+
+ // blendshapes
+ var targetNames = gltfMesh.extras.targetNames;
+ for (int i = 1; i < gltfMesh.primitives.Count; ++i)
+ {
+ if (gltfMesh.primitives[i].targets.Count != targetNames.Count)
+ {
+ throw new FormatException(string.Format("different targets length: {0} with targetNames length.",
+ gltfMesh.primitives[i]));
+ }
+ }
+ for (var i = 0; i < targetNames.Count; i++)
+ {
+ var blendShape = new BlendShape(!string.IsNullOrEmpty(targetNames[i]) ? targetNames[i] : i.ToString());
+ blendShapes.Add(blendShape);
+ }
+
+ foreach (var prim in gltfMesh.primitives)
+ {
+ var indexOffset = positions.Count;
+ var indexBuffer = prim.indices;
+
+ var positionCount = positions.Count;
+ positions.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.POSITION).Select(x => x.ReverseZ()));
+ positionCount = positions.Count - positionCount;
+
+ // normal
+ if (prim.attributes.NORMAL != -1)
+ {
+ normals.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.NORMAL).Select(x => x.ReverseZ()));
+ }
+
+ if (prim.attributes.TANGENT != -1)
+ {
+ tangents.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.TANGENT).Select(x => x.ReverseZ()));
+ }
+
+ // uv
+ if (prim.attributes.TEXCOORD_0 != -1)
+ {
+ if (ctx.IsGeneratedUniGLTFAndOlder(1, 16))
+ {
+#pragma warning disable 0612
+ // backward compatibility
+ uv.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY()));
+#pragma warning restore 0612
+ }
+ else
+ {
+ uv.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.TEXCOORD_0).Select(x => x.ReverseUV()));
+ }
+ }
+ else
+ {
+ // for inconsistent attributes in primitives
+ uv.AddRange(new Vector2[positionCount]);
+ }
+
+ // color
+ if (prim.attributes.COLOR_0 != -1)
+ {
+ colors.AddRange(ctx.GLTF.GetArrayFromAccessor(prim.attributes.COLOR_0));
+ }
+
+ // skin
+ if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1)
+ {
+ var joints0 = ctx.GLTF.GetArrayFromAccessor(prim.attributes.JOINTS_0); // uint4
+ var weights0 = ctx.GLTF.GetArrayFromAccessor(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray();
+
+ for (int j = 0; j < joints0.Length; ++j)
+ {
+ var bw = new BoneWeight();
+
+ bw.boneIndex0 = joints0[j].x;
+ bw.weight0 = weights0[j].x;
+
+ bw.boneIndex1 = joints0[j].y;
+ bw.weight1 = weights0[j].y;
+
+ bw.boneIndex2 = joints0[j].z;
+ bw.weight2 = weights0[j].z;
+
+ bw.boneIndex3 = joints0[j].w;
+ bw.weight3 = weights0[j].w;
+
+ meshContext.boneWeights.Add(bw);
+ }
+ }
+
+ // blendshape
+ if (prim.targets != null && prim.targets.Count > 0)
+ {
+ for (int i = 0; i < prim.targets.Count; ++i)
+ {
+ var primTarget = prim.targets[i];
+ if (primTarget.POSITION != -1)
+ {
+ blendShapes[i].Positions.AddRange(
+ ctx.GLTF.GetArrayFromAccessor(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray());
+ }
+ if (primTarget.NORMAL != -1)
+ {
+ blendShapes[i].Normals.AddRange(
+ ctx.GLTF.GetArrayFromAccessor(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray());
+ }
+ if (primTarget.TANGENT != -1)
+ {
+ blendShapes[i].Tangents.AddRange(
+ ctx.GLTF.GetArrayFromAccessor(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray());
+ }
+ }
+ }
+
+ var indices =
+ (indexBuffer >= 0)
+ ? ctx.GLTF.GetIndices(indexBuffer)
+ : TriangleUtil.FlipTriangle(Enumerable.Range(0, meshContext.positions.Length)).ToArray() // without index array
+ ;
+ for (int i = 0; i < indices.Length; ++i)
+ {
+ indices[i] += indexOffset;
+ }
+
+ meshContext.subMeshes.Add(indices);
+
+ // material
+ meshContext.materialIndices.Add(prim.material);
+ }
+
+ meshContext.positions = positions.ToArray();
+ meshContext.normals = normals.ToArray();
+ meshContext.tangents = tangents.ToArray();
+ meshContext.uv = uv.ToArray();
+ meshContext.blendShapes = blendShapes;
+
+ return meshContext;
+ }
+
// multiple submMesh is not sharing a VertexBuffer.
// each subMesh use a independent VertexBuffer.
private static MeshContext _ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh)
@@ -352,22 +501,33 @@ namespace UniGLTF
public MeshContext ReadMesh(ImporterContext ctx, int meshIndex)
{
var gltfMesh = ctx.GLTF.meshes[meshIndex];
- glTFAttributes lastAttributes = null;
- var sharedAttributes = true;
- foreach (var prim in gltfMesh.primitives)
+
+ bool sharedMorphTarget = gltfMesh.extras != null && gltfMesh.extras.targetNames.Count > 0;
+ MeshContext meshContext;
+ if (sharedMorphTarget)
{
- if (lastAttributes != null && !prim.attributes.Equals(lastAttributes))
+ meshContext = _ImportMeshSharingMorphTarget(ctx, gltfMesh);
+ }
+ else
+ {
+ glTFAttributes lastAttributes = null;
+ var sharedAttributes = true;
+ foreach (var prim in gltfMesh.primitives)
{
- sharedAttributes = false;
- break;
+ if (lastAttributes != null && !prim.attributes.Equals(lastAttributes))
+ {
+ sharedAttributes = false;
+ break;
+ }
+
+ lastAttributes = prim.attributes;
}
- lastAttributes = prim.attributes;
+
+ meshContext = sharedAttributes
+ ? _ImportMeshSharingVertexBuffer(ctx, gltfMesh)
+ : _ImportMeshIndependentVertexBuffer(ctx, gltfMesh);
}
- var meshContext = sharedAttributes
- ? _ImportMeshSharingVertexBuffer(ctx, gltfMesh)
- : _ImportMeshIndependentVertexBuffer(ctx, gltfMesh)
- ;
meshContext.name = gltfMesh.name;
if (string.IsNullOrEmpty(meshContext.name))
{