public void MeshTest() { var model = new glTFMesh("mesh") { primitives = new List <glTFPrimitives> { new glTFPrimitives { attributes = new glTFAttributes { POSITION = 0, } } }, }; var json = model.ToJson(); Assert.AreEqual(@"{""name"":""mesh"",""primitives"":[{""mode"":0,""indices"":-1,""attributes"":{""POSITION"":0},""material"":0}]}", json); Debug.Log(json); var c = new JsonSchemaValidationContext("") { EnableDiagnosisForNotRequiredFields = true, }; var json2 = JsonSchema.FromType <glTFMesh>().Serialize(model, c); Assert.AreEqual(@"{""name"":""mesh"",""primitives"":[{""mode"":0,""attributes"":{""POSITION"":0},""material"":0}]}", json2); }
public static bool TryGet(glTFMesh mesh, out List <string> targetNames) { if (mesh.extras is glTFExtensionImport meshExtras) { foreach (var kv in meshExtras.ObjectItems()) { if (kv.Key.GetUtf8String() == ExtraNameUtf8) { targetNames = Deserialize(kv.Value); return(true); } } } // use first primitive if (mesh.primitives.Count > 0 && mesh.primitives[0].extras is glTFExtensionImport primExtras) { foreach (var kv in primExtras.ObjectItems()) { if (kv.Key.GetUtf8String() == ExtraNameUtf8) { targetNames = Deserialize(kv.Value); return(true); } } } targetNames = default; return(false); }
public static string ToJson(this glTFMesh self) { var f = new JsonFormatter(); GltfSerializer.Serialize_gltf_meshes_ITEM(f, self); return(f.ToString()); }
/// <summary> /// バッファ共有方式(vrm-0.x)の判定。 /// import の後方互換性のためで、vrm-1.0 export では使いません。 /// /// * バッファ共用方式は VertexBuffer が同じでSubMeshの index buffer がスライドしていく方式 /// * バッファがひとつのとき /// * すべての primitive の attribute が 同一の accessor を使用している時 /// /// </summary> private static bool HasSharedVertexBuffer(glTFMesh gltfMesh) { glTFAttributes lastAttributes = null; foreach (var prim in gltfMesh.primitives) { if (lastAttributes != null && !prim.attributes.Equals(lastAttributes)) { return(false); } lastAttributes = prim.attributes; } return(true); }
public void RenameBlendShape(glTFMesh gltfMesh) { if (gltf_mesh_extras_targetNames.TryGet(gltfMesh, out List <string> targetNames)) { for (var i = 0; i < BlendShapes.Count; i++) { if (i >= targetNames.Count) { Debug.LogWarning($"invalid primitive.extras.targetNames length"); break; } BlendShapes[i].Name = targetNames[i]; } } }
/// <summary> /// /// </summary> /// <param name="mesh"></param> /// <param name="gltf"></param> /// <param name="bufferIndex"></param> /// <param name="unityMesh"></param> /// <param name="unityMaterials"></param> /// <param name="settings"></param> /// <param name="axisInverter"></param> /// <returns></returns> public static (glTFMesh mesh, Dictionary <int, int> blendShapeIndexMap) ExportMesh(glTF gltf, int bufferIndex, MeshWithRenderer unityMesh, List <Material> unityMaterials, MeshExportSettings settings, IAxisInverter axisInverter) { glTFMesh gltfMesh = default; var blendShapeIndexMap = new Dictionary <int, int>(); if (settings.DivideVertexBuffer) { gltfMesh = MeshExporterDivided.Export(gltf, bufferIndex, unityMesh, unityMaterials, axisInverter, settings); } else { gltfMesh = ExportSharedVertexBuffer(gltf, bufferIndex, unityMesh, unityMaterials, axisInverter, settings); var targetNames = new List <string>(); int exportBlendShapes = 0; for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(gltf, bufferIndex, unityMesh.Mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition, axisInverter); if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0) { continue; } // maybe skip var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j); blendShapeIndexMap.Add(j, exportBlendShapes++); targetNames.Add(blendShapeName); // // all primitive has same blendShape // for (int k = 0; k < gltfMesh.primitives.Count; ++k) { gltfMesh.primitives[k].targets.Add(morphTarget); } } gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); } return(gltfMesh, blendShapeIndexMap); }
static bool HasSharedVertexBuffer(glTFMesh gltfMesh) { glTFAttributes lastAttributes = null; var sharedAttributes = true; foreach (var prim in gltfMesh.primitives) { if (lastAttributes != null && !prim.attributes.Equals(lastAttributes)) { sharedAttributes = false; break; } lastAttributes = prim.attributes; } return(sharedAttributes); }
private void RenameBlendShape(glTFMesh gltfMesh) { if (!gltf_mesh_extras_targetNames.TryGet(gltfMesh, out var targetNames)) { return; } for (var i = 0; i < _blendShapes.Count; i++) { if (i >= targetNames.Count) { Debug.LogWarning($"invalid primitive.extras.targetNames length"); break; } _blendShapes[i].Name = targetNames[i]; } }
public static bool TryGet(glTFMesh mesh, out List <string> targetNames) { if (mesh.extras is glTFExtensionImport meshExtras) { foreach (var kv in meshExtras.ObjectItems()) { if (kv.Key.GetUtf8String() == ExtraNameUtf8) { targetNames = Deserialize(kv.Value); return(true); } } } // use first primitive if (mesh.primitives.Count > 0 && mesh.primitives[0].extras is glTFExtensionImport primExtras) { foreach (var kv in primExtras.ObjectItems()) { if (kv.Key.GetUtf8String() == ExtraNameUtf8) { targetNames = Deserialize(kv.Value); return(true); } } } if (mesh.primitives.Count > 0) { var prim = mesh.primitives[0]; if (prim.targets.Count > 0) { // 名無しには連番を付ける targetNames = new List <string>(); for (int i = 0; i < prim.targets.Count; ++i) { targetNames.Add($"{i}"); } return(true); } } targetNames = default; return(false); }
public void RenameBlendShape(glTFMesh gltfMesh) { if (gltfMesh.extras != null && gltfMesh.extras.targetNames != null) { 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++) { BlendShapes[i].Name = targetNames[i]; } return; } var prim = gltfMesh.primitives[0]; { if (prim.extras != null && prim.extras.targetNames != null) { var targetNames = prim.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++) { BlendShapes[i].Name = targetNames[i]; } } } }
public void MeshTest() { var model = new glTFMesh("mesh") { primitives = new List <glTFPrimitives> { new glTFPrimitives { indices = 0, attributes = new glTFAttributes { POSITION = 1, } } }, }; var json = model.ToJson(); Assert.AreEqual(@"{""name"":""mesh"",""primitives"":[{""mode"":0,""indices"":0,""attributes"":{""POSITION"":1},""material"":0}]}", json); Debug.Log(json); }
public static void Serialize(glTFMesh gltfMesh, IEnumerable <string> targetNames) { // targetNames var f = new JsonFormatter(); f.BeginList(); foreach (var n in targetNames) { f.Value(n); } f.EndList(); var targetNamesJson = f.GetStore().Bytes; var meshExtras = glTFExtensionExport.GetOrCreate(ref gltfMesh.extras); meshExtras.Add(ExtraName, targetNamesJson); foreach (var prim in gltfMesh.primitives) { var primExtras = glTFExtensionExport.GetOrCreate(ref prim.extras); primExtras.Add(ExtraName, targetNamesJson); } }
public static void Serialize_gltf_meshes_ITEM(JsonFormatter f, glTFMesh value) { f.BeginMap(); if (!string.IsNullOrEmpty(value.name)) { f.Key("name"); f.Value(value.name); } if (value.primitives != null && value.primitives.Count >= 1) { f.Key("primitives"); Serialize_gltf_meshes__primitives(f, value.primitives); } if (value.weights != null && value.weights.Length >= 1) { f.Key("weights"); Serialize_gltf_meshes__weights(f, value.weights); } if (value.extras != null) { f.Key("extras"); value.extras.Serialize(f); } if (value.extensions != null) { f.Key("extensions"); value.extensions.Serialize(f); } f.EndMap(); }
public void MeshTest() { var mesh = new glTFMesh("mesh") { primitives = new List <glTFPrimitives> { new glTFPrimitives { attributes = new glTFAttributes { POSITION = 0, } } } }; var f = new JsonFormatter(); f.Serialize(mesh); var json = new Utf8String(f.GetStoreBytes()).ToString(); Debug.Log(json); }
public static (int VertexCapacity, int IndexCapacity) GetCapacity(GltfData data, glTFMesh gltfMesh) { var vertexCount = 0; var indexCount = 0; foreach (var primitive in gltfMesh.primitives) { var positions = data.GLTF.accessors[primitive.attributes.POSITION]; vertexCount += positions.count; if (primitive.indices == -1) { indexCount += positions.count; } else { var accessor = data.GLTF.accessors[primitive.indices]; indexCount += accessor.count; } } return(vertexCount, indexCount); }
// multiple submesh sharing same VertexBuffer private static MeshContext _ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh) { var context = new MeshContext(); { var prim = gltfMesh.primitives.First(); context.positions = ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).SelectInplace(x => x.ReverseZ()); // normal if (prim.attributes.NORMAL != -1) { context.normals = ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).SelectInplace(x => x.ReverseZ()); } // tangent if (prim.attributes.TANGENT != -1) { context.tangents = ctx.GLTF.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).SelectInplace(x => x.ReverseZ()); } // uv if (prim.attributes.TEXCOORD_0 != -1) { context.uv = ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).SelectInplace(x => x.ReverseY()); } else { // for inconsistent attributes in primitives context.uv = new Vector2[context.positions.Length]; } // color if (prim.attributes.COLOR_0 != -1) { context.colors = ctx.GLTF.GetArrayFromAccessor <Color>(prim.attributes.COLOR_0); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = ctx.GLTF.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = ctx.GLTF.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0); for (int i = 0; i < weights0.Length; ++i) { weights0[i] = weights0[i].One(); } 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; context.boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { context.blendShapes.AddRange(prim.targets.Select((x, i) => new BlendShape( i < prim.extras.targetNames.Count && !string.IsNullOrEmpty(prim.extras.targetNames[i]) ? prim.extras.targetNames[i] : i.ToString()))); for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = context.blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.POSITION), x => x.ReverseZ()); } if (primTarget.NORMAL != -1) { blendShape.Normals.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.NORMAL), x => x.ReverseZ()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.TANGENT), x => x.ReverseZ()); } } } } foreach (var prim in gltfMesh.primitives) { if (prim.indices == -1) { context.subMeshes.Add(TriangleUtil.FlipTriangle(Enumerable.Range(0, context.positions.Length)).ToArray()); } else { var indices = ctx.GLTF.GetIndices(prim.indices); context.subMeshes.Add(indices); } // material context.materialIndices.Add(prim.material); } return(context); }
// multiple submMesh is not sharing a VertexBuffer. // each subMesh use a independent VertexBuffer. private static MeshContext _ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh) { //Debug.LogWarning("_ImportMeshIndependentVertexBuffer"); var targets = gltfMesh.primitives[0].targets; for (int i = 1; i < gltfMesh.primitives.Count; ++i) { if (!gltfMesh.primitives[i].targets.SequenceEqual(targets)) { throw new NotImplementedException(string.Format("diffirent targets: {0} with {1}", gltfMesh.primitives[i], targets)); } } var positions = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var uv = new List <Vector2>(); var colors = new List <Color>(); var meshContext = new MeshContext(); foreach (var prim in gltfMesh.primitives) { var indexOffset = positions.Count; var indexBuffer = prim.indices; var positionCount = positions.Count; positions.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); positionCount = positions.Count - positionCount; // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } if (prim.attributes.TANGENT != -1) { tangents.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } // uv if (prim.attributes.TEXCOORD_0 != -1) { uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); } else { // for inconsistent attributes in primitives uv.AddRange(new Vector2[positionCount]); } // color if (prim.attributes.COLOR_0 != -1) { colors.AddRange(ctx.GLTF.GetArrayFromAccessor <Color>(prim.attributes.COLOR_0)); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = ctx.GLTF.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = ctx.GLTF.GetArrayFromAccessor <Float4>(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 name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = new BlendShape(!string.IsNullOrEmpty(prim.extras.targetNames[i]) ? prim.extras.targetNames[i] : i.ToString()) ; if (primTarget.POSITION != -1) { blendShape.Positions.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShape.Normals.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } meshContext.blendShapes.Add(blendShape); } } 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(); return(meshContext); }
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex, MeshWithRenderer unityMesh, List <Material> unityMaterials) { var mesh = unityMesh.Mesh; var materials = unityMesh.Renderer.sharedMaterials; var positions = mesh.vertices.Select(y => y.ReverseZ()).ToArray(); var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, positions, glBufferTarget.ARRAY_BUFFER); gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => y.normalized.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER); #if GLTF_EXPORT_TANGENTS var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER); #endif var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var uvAccessorIndex1 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var colorAccessorIndex = -1; var vColorState = MeshExportInfo.DetectVertexColor(mesh, materials); if (vColorState == MeshExportInfo.VertexColorState.ExistsAndIsUsed || // VColor使っている vColorState == MeshExportInfo.VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする) ) { // UniUnlit で Multiply 設定になっている colorAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.colors, glBufferTarget.ARRAY_BUFFER); } var boneweights = mesh.boneWeights; var weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); var jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new UShort4( (ushort)unityMesh.GetJointIndex(y.boneIndex0), (ushort)unityMesh.GetJointIndex(y.boneIndex1), (ushort)unityMesh.GetJointIndex(y.boneIndex2), (ushort)unityMesh.GetJointIndex(y.boneIndex3)) ).ToArray(), glBufferTarget.ARRAY_BUFFER); var attributes = new glTFAttributes { POSITION = positionAccessorIndex, }; if (normalAccessorIndex != -1) { attributes.NORMAL = normalAccessorIndex; } #if GLTF_EXPORT_TANGENTS if (tangentAccessorIndex != -1) { attributes.TANGENT = tangentAccessorIndex; } #endif if (uvAccessorIndex0 != -1) { attributes.TEXCOORD_0 = uvAccessorIndex0; } if (uvAccessorIndex1 != -1) { attributes.TEXCOORD_1 = uvAccessorIndex1; } if (colorAccessorIndex != -1) { attributes.COLOR_0 = colorAccessorIndex; } if (weightAccessorIndex != -1) { attributes.WEIGHTS_0 = weightAccessorIndex; } if (jointsAccessorIndex != -1) { attributes.JOINTS_0 = jointsAccessorIndex; } var gltfMesh = new glTFMesh(mesh.name); var indices = new List <uint>(); for (int j = 0; j < mesh.subMeshCount; ++j) { indices.Clear(); var triangles = mesh.GetIndices(j); if (triangles.Length == 0) { // https://github.com/vrm-c/UniVRM/issues/664 continue; } for (int i = 0; i < triangles.Length; i += 3) { var i0 = triangles[i]; var i1 = triangles[i + 1]; var i2 = triangles[i + 2]; // flip triangle indices.Add((uint)i2); indices.Add((uint)i1); indices.Add((uint)i0); } var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); if (indicesAccessorIndex < 0) { // https://github.com/vrm-c/UniVRM/issues/664 throw new Exception(); } if (j >= materials.Length) { Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Renderer.name); break; } gltfMesh.primitives.Add(new glTFPrimitives { attributes = attributes, indices = indicesAccessorIndex, mode = 4, // triangles ? material = unityMaterials.IndexOf(materials[j]) }); } return(gltfMesh); }
// mesh is sharing morph targets. private static MeshContext _ImportMeshSharingMorphTarget(ImporterContext ctx, glTFMesh gltfMesh) { var positions = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var uv = new List <Vector2>(); var colors = new List <Color>(); var blendShapes = new List <BlendShape>(); 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 <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); positionCount = positions.Count - positionCount; // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } if (prim.attributes.TANGENT != -1) { tangents.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector4>(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 <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); #pragma warning restore 0612 } else { uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(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 <Color>(prim.attributes.COLOR_0)); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = ctx.GLTF.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = ctx.GLTF.GetArrayFromAccessor <Float4>(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 <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShapes[i].Normals.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShapes[i].Tangents.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(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); }
/// <summary> /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど /// glTF 的にはありうる。 /// /// primitive を独立した(Independent) Mesh として扱いこれを連結する。 /// </summary> /// <param name="ctx"></param> /// <param name="gltfMesh"></param> /// <returns></returns> private void ImportMeshIndependentVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter) { bool isOldVersion = data.GLTF.IsGeneratedUniGLTFAndOlder(1, 16); foreach (var primitives in gltfMesh.primitives) { var vertexOffset = _currentVertexCount; var indexBufferCount = primitives.indices; // position は必ずある var positions = primitives.GetPositions(data); var normals = primitives.GetNormals(data, positions.Length); var texCoords0 = primitives.GetTexCoords0(data, positions.Length); var texCoords1 = primitives.GetTexCoords1(data, positions.Length); var colors = primitives.GetColors(data, positions.Length); var skinning = SkinningInfo.Create(data, gltfMesh, primitives); AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone; CheckAttributeUsages(primitives); for (var i = 0; i < positions.Length; ++i) { var position = inverter.InvertVector3(positions[i]); var normal = normals != null?inverter.InvertVector3(normals.Value[i]) : Vector3.zero; var texCoord0 = Vector2.zero; if (texCoords0 != null) { if (isOldVersion) { #pragma warning disable 0612 // backward compatibility texCoord0 = texCoords0.Value[i].ReverseY(); #pragma warning restore 0612 } else { texCoord0 = texCoords0.Value[i].ReverseUV(); } } var texCoord1 = texCoords1 != null ? texCoords1.Value[i].ReverseUV() : Vector2.zero; var color = colors != null ? colors.Value[i] : Color.white; AddVertex( new MeshVertex( position, normal, texCoord0, texCoord1, color )); var skin = skinning.GetSkinnedVertex(i); if (skin.HasValue) { AddSkin(skin.Value); } } // blendshape if (primitives.targets != null && primitives.targets.Count > 0) { for (var i = 0; i < primitives.targets.Count; ++i) { var primTarget = primitives.targets[i]; var blendShape = GetOrCreateBlendShape(i); if (primTarget.POSITION != -1) { var array = data.GetArrayFromAccessor <Vector3>(primTarget.POSITION); if (array.Length != positions.Length) { throw new Exception("different length"); } blendShape.Positions.AddRange(array.Select(inverter.InvertVector3).ToArray()); } if (primTarget.NORMAL != -1) { var array = data.GetArrayFromAccessor <Vector3>(primTarget.NORMAL); if (array.Length != positions.Length) { throw new Exception("different length"); } blendShape.Normals.AddRange(array.Select(inverter.InvertVector3).ToArray()); } if (primTarget.TANGENT != -1) { var array = data.GetArrayFromAccessor <Vector3>(primTarget.TANGENT); if (array.Length != positions.Length) { throw new Exception("different length"); } blendShape.Tangents.AddRange(array.Select(inverter.InvertVector3).ToArray()); } } } if (indexBufferCount >= 0) { var indexOffset = _currentIndexCount; var dataIndices = data.GetIndicesFromAccessorIndex(indexBufferCount); PushIndices(dataIndices, vertexOffset); _subMeshes.Add(new SubMeshDescriptor(indexOffset, dataIndices.Count)); } else { var indexOffset = _currentIndexCount; for (int i = 0; i < positions.Count(); i += 3) { // flip triangle AddIndex(i + vertexOffset + 2); AddIndex(i + vertexOffset + 1); AddIndex(i + vertexOffset); } _subMeshes.Add(new SubMeshDescriptor(indexOffset, positions.Count())); } // material _materialIndices.Add(primitives.material); } }
public static glTFMesh Export(glTF gltf, int bufferIndex, MeshWithRenderer unityMesh, List<Material> unityMaterials, IAxisInverter axisInverter, MeshExportSettings settings) { var mesh = unityMesh.Mesh; var gltfMesh = new glTFMesh(mesh.name); if (settings.ExportTangents) { // support しない throw new NotImplementedException(); } var positions = mesh.vertices; var normals = mesh.normals; var uv = mesh.uv; var boneWeights = mesh.boneWeights; Func<int, int> getJointIndex = null; if (boneWeights != null && boneWeights.Length == positions.Length) { getJointIndex = unityMesh.GetJointIndex; } Vector3[] blendShapePositions = new Vector3[mesh.vertexCount]; Vector3[] blendShapeNormals = new Vector3[mesh.vertexCount]; var usedIndices = new List<int>(); for (int i = 0; i < mesh.subMeshCount; ++i) { var indices = mesh.GetIndices(i); // mesh // index の順に attributes を蓄える var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (indices.Contains(k)) { // indices から参照される頂点だけを蓄える usedIndices.Add(k); buffer.Push(k, axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV()); if (getJointIndex != null) { buffer.Push(boneWeights[k]); } } } var material = unityMesh.Renderer.sharedMaterials[i]; var materialIndex = -1; if (material != null) { materialIndex = unityMaterials.IndexOf(material); } var flipped = new List<int>(); for (int j = 0; j < indices.Length; j += 3) { var t0 = indices[j]; var t1 = indices[j + 1]; var t2 = indices[j + 2]; flipped.Add(t2); flipped.Add(t1); flipped.Add(t0); } var gltfPrimitive = buffer.ToGltfPrimitive(gltf, bufferIndex, materialIndex, flipped); // blendShape for (int j = 0; j < mesh.blendShapeCount; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(indices.Length); // index の順に attributes を蓄える mesh.GetBlendShapeFrameVertices(j, 0, blendShapePositions, blendShapeNormals, null); foreach (var k in usedIndices) { blendShape.Push( axisInverter.InvertVector3(blendShapePositions[k]), axisInverter.InvertVector3(blendShapeNormals[k])); } gltfPrimitive.targets.Add(blendShape.ToGltf(gltf, bufferIndex, !settings.ExportOnlyBlendShapePosition)); } gltfMesh.primitives.Add(gltfPrimitive); } var targetNames = Enumerable.Range(0, mesh.blendShapeCount).Select(x => mesh.GetBlendShapeName(x)).ToArray(); gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); return gltfMesh; }
/// <summary> /// primitive 間で vertex を共有する形で Export する。 /// UniVRM-0.71.0 以降は、MeshExporterDivided.Export もある。 /// /// * GLB/GLTF は shared(default) と divided を選択可能 /// * VRM0 は shared 仕様 /// * VRM1 は divided 仕様 /// /// /// </summary> /// <param name="gltf"></param> /// <param name="bufferIndex"></param> /// <param name="unityMesh"></param> /// <param name="unityMaterials"></param> /// <param name="axisInverter"></param> /// <param name="settings"></param> /// <returns></returns> public static (glTFMesh, Dictionary <int, int> blendShapeIndexMap) Export(ExportingGltfData data, MeshExportInfo unityMesh, List <Material> unityMaterials, IAxisInverter axisInverter, GltfExportSettings settings) { var mesh = unityMesh.Mesh; var materials = unityMesh.Materials; var positions = mesh.vertices.Select(axisInverter.InvertVector3).ToArray(); var positionAccessorIndex = data.ExtendBufferAndGetAccessorIndex(positions, glBufferTarget.ARRAY_BUFFER); data.GLTF.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); data.GLTF.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); var normalAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.normals.Select(y => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER); int?tangentAccessorIndex = default; if (settings.ExportTangents) { tangentAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.tangents.Select(axisInverter.InvertVector4).ToArray(), glBufferTarget.ARRAY_BUFFER); } var uvAccessorIndex0 = data.ExtendBufferAndGetAccessorIndex(mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var uvAccessorIndex1 = data.ExtendBufferAndGetAccessorIndex(mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var colorAccessorIndex = -1; var vColorState = VertexColorUtility.DetectVertexColor(mesh, materials); if (vColorState == VertexColorState.ExistsAndIsUsed || // VColor使っている vColorState == VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする) ) { // UniUnlit で Multiply 設定になっている colorAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.colors, glBufferTarget.ARRAY_BUFFER); } var boneweights = mesh.boneWeights; var weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); var jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new UShort4( (ushort)unityMesh.GetJointIndex(y.boneIndex0), (ushort)unityMesh.GetJointIndex(y.boneIndex1), (ushort)unityMesh.GetJointIndex(y.boneIndex2), (ushort)unityMesh.GetJointIndex(y.boneIndex3)) ).ToArray(), glBufferTarget.ARRAY_BUFFER); var attributes = new glTFAttributes { POSITION = positionAccessorIndex, }; if (normalAccessorIndex != -1) { attributes.NORMAL = normalAccessorIndex; } if (tangentAccessorIndex.HasValue) { attributes.TANGENT = tangentAccessorIndex.Value; } if (uvAccessorIndex0 != -1) { attributes.TEXCOORD_0 = uvAccessorIndex0; } if (uvAccessorIndex1 != -1) { attributes.TEXCOORD_1 = uvAccessorIndex1; } if (colorAccessorIndex != -1) { attributes.COLOR_0 = colorAccessorIndex; } if (weightAccessorIndex != -1) { attributes.WEIGHTS_0 = weightAccessorIndex; } if (jointsAccessorIndex != -1) { attributes.JOINTS_0 = jointsAccessorIndex; } var gltfMesh = new glTFMesh(mesh.name); var indices = new List <uint>(); for (int j = 0; j < mesh.subMeshCount; ++j) { indices.Clear(); var triangles = mesh.GetIndices(j); if (triangles.Length == 0) { // https://github.com/vrm-c/UniVRM/issues/664 continue; } for (int i = 0; i < triangles.Length; i += 3) { var i0 = triangles[i]; var i1 = triangles[i + 1]; var i2 = triangles[i + 2]; // flip triangle indices.Add((uint)i2); indices.Add((uint)i1); indices.Add((uint)i0); } var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); if (indicesAccessorIndex < 0) { // https://github.com/vrm-c/UniVRM/issues/664 throw new Exception(); } if (j >= materials.Length) { Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Mesh.name); break; } gltfMesh.primitives.Add(new glTFPrimitives { attributes = attributes, indices = indicesAccessorIndex, mode = 4, // triangles ? material = unityMaterials.IndexOf(materials[j]) }); } var blendShapeIndexMap = new Dictionary <int, int>(); { var targetNames = new List <string>(); int exportBlendShapes = 0; for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(data, unityMesh.Mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition, axisInverter); if (morphTarget.POSITION < 0) { // Skip empty blendShape. // Shift blendShape's index. continue; } var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j); blendShapeIndexMap.Add(j, exportBlendShapes++); targetNames.Add(blendShapeName); // // all primitive has same blendShape // for (int k = 0; k < gltfMesh.primitives.Count; ++k) { gltfMesh.primitives[k].targets.Add(morphTarget); } } gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); } return(gltfMesh, blendShapeIndexMap); }
/// <summary> /// /// 各primitiveが同じ attribute を共有している場合専用のローダー。 /// /// </summary> /// <param name="ctx"></param> /// <param name="gltfMesh"></param> /// <returns></returns> private void ImportMeshSharingVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter) { var isOldVersion = data.GLTF.IsGeneratedUniGLTFAndOlder(1, 16); { // 同じVertexBufferを共有しているので先頭のモノを使う var primitives = gltfMesh.primitives.First(); var positions = primitives.GetPositions(data); var normals = primitives.GetNormals(data, positions.Length); var texCoords0 = primitives.GetTexCoords0(data, positions.Length); var texCoords1 = primitives.GetTexCoords1(data, positions.Length); var colors = primitives.GetColors(data, positions.Length); var skinning = SkinningInfo.Create(data, gltfMesh, primitives); AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone; CheckAttributeUsages(primitives); for (var i = 0; i < positions.Length; ++i) { var position = inverter.InvertVector3(positions[i]); var normal = normals != null?inverter.InvertVector3(normals.Value[i]) : Vector3.zero; var texCoord0 = Vector2.zero; if (texCoords0 != null) { if (isOldVersion) { #pragma warning disable 0612 texCoord0 = texCoords0.Value[i].ReverseY(); #pragma warning restore 0612 } else { texCoord0 = texCoords0.Value[i].ReverseUV(); } } var texCoord1 = texCoords1 != null ? texCoords1.Value[i].ReverseUV() : Vector2.zero; var color = colors != null ? colors.Value[i] : Color.white; AddVertex( new MeshVertex( position, normal, texCoord0, texCoord1, color)); var skin = skinning.GetSkinnedVertex(i); if (skin.HasValue) { AddSkin(skin.Value); } } // blendshape if (primitives.targets != null && primitives.targets.Count > 0) { for (int i = 0; i < primitives.targets.Count; ++i) { var primTarget = primitives.targets[i]; var hasPosition = primTarget.POSITION != -1 && data.GLTF.accessors[primTarget.POSITION].count == positions.Length; var hasNormal = primTarget.NORMAL != -1 && data.GLTF.accessors[primTarget.NORMAL].count == positions.Length; var hasTangent = primTarget.TANGENT != -1 && data.GLTF.accessors[primTarget.TANGENT].count == positions.Length; var blendShape = new BlendShape(i.ToString(), positions.Length, hasPosition, hasNormal, hasTangent); _blendShapes.Add(blendShape); if (hasPosition) { var morphPositions = data.GetArrayFromAccessor <Vector3>(primTarget.POSITION); blendShape.Positions.Capacity = morphPositions.Length; for (var j = 0; j < positions.Length; ++j) { blendShape.Positions.Add(inverter.InvertVector3(morphPositions[j])); } } if (hasNormal) { var morphNormals = data.GetArrayFromAccessor <Vector3>(primTarget.NORMAL); blendShape.Normals.Capacity = morphNormals.Length; for (var j = 0; j < positions.Length; ++j) { blendShape.Normals.Add(inverter.InvertVector3(morphNormals[j])); } } if (hasTangent) { var morphTangents = data.GetArrayFromAccessor <Vector3>(primTarget.TANGENT); blendShape.Tangents.Capacity = morphTangents.Length; for (var j = 0; j < positions.Length; ++j) { blendShape.Tangents.Add(inverter.InvertVector3(morphTangents[j])); } } } } } foreach (var primitive in gltfMesh.primitives) { if (primitive.indices >= 0) { var indexOffset = _currentIndexCount; var indices = data.GetIndicesFromAccessorIndex(primitive.indices); PushIndices(indices, 0); _subMeshes.Add(new SubMeshDescriptor(indexOffset, indices.Count)); } else { var indexOffset = _currentIndexCount; var positions = data.GLTF.accessors[primitive.attributes.POSITION]; for (int i = 0; i < positions.count; i += 3) { // flip triangle AddIndex(i + 2); AddIndex(i + 1); AddIndex(i); } _subMeshes.Add(new SubMeshDescriptor(indexOffset, positions.count)); } // material _materialIndices.Add(primitive.material); } }
/// <summary> /// Divide vertex buffer(Position, Normal, UV, VertexColor, Skinning and BlendShapes) by submesh usage, then export /// </summary> /// <param name="gltf"></param> /// <param name="gltfBuffer"></param> /// <param name="unityMesh"></param> /// <param name="unityMaterials"></param> /// <param name="axisInverter"></param> /// <param name="settings"></param> /// <returns></returns> public static (glTFMesh, Dictionary <int, int>) Export(glTF gltf, int gltfBuffer, MeshExportInfo unityMesh, List <Material> unityMaterials, IAxisInverter axisInverter, GltfExportSettings settings) { var mesh = unityMesh.Mesh; var gltfMesh = new glTFMesh(mesh.name); if (settings.ExportTangents) { // no support throw new NotImplementedException(); } var positions = mesh.vertices; var normals = mesh.normals; var uv = mesh.uv; var boneWeights = mesh.boneWeights; Func <int, int> getJointIndex = null; if (boneWeights != null && boneWeights.Length == positions.Length) { getJointIndex = unityMesh.GetJointIndex; } Vector3[] blendShapePositions = new Vector3[mesh.vertexCount]; Vector3[] blendShapeNormals = new Vector3[mesh.vertexCount]; var usedIndices = new List <int>(); for (int i = 0; i < mesh.subMeshCount; ++i) { var indices = mesh.GetIndices(i); var hash = new HashSet <int>(indices); // aggrigate vertex attributes var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (hash.Contains(k)) { // aggrigate indices usedIndices.Add(k); buffer.Push(k, axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV()); if (getJointIndex != null) { buffer.Push(boneWeights[k]); } } } var material = unityMesh.Materials[i]; var materialIndex = -1; if (material != null) { materialIndex = unityMaterials.IndexOf(material); } var flipped = new List <int>(); for (int j = 0; j < indices.Length; j += 3) { var t0 = indices[j]; var t1 = indices[j + 1]; var t2 = indices[j + 2]; flipped.Add(t2); flipped.Add(t1); flipped.Add(t0); } var gltfPrimitive = buffer.ToGltfPrimitive(gltf, gltfBuffer, materialIndex, flipped); // blendShape(morph target) for (int j = 0; j < mesh.blendShapeCount; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(usedIndices.Count); // aggriage morph target mesh.GetBlendShapeFrameVertices(j, 0, blendShapePositions, blendShapeNormals, null); int l = 0; foreach (var k in usedIndices) { blendShape.Set(l++, axisInverter.InvertVector3(blendShapePositions[k]), axisInverter.InvertVector3(blendShapeNormals[k])); } gltfPrimitive.targets.Add(blendShape.ToGltf(gltf, gltfBuffer, !settings.ExportOnlyBlendShapePosition, settings.UseSparseAccessorForMorphTarget)); } gltfMesh.primitives.Add(gltfPrimitive); } var targetNames = Enumerable.Range(0, mesh.blendShapeCount).Select(x => mesh.GetBlendShapeName(x)).ToArray(); gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); return(gltfMesh, Enumerable.Range(0, mesh.blendShapeCount).ToDictionary(x => x, x => x)); }
/// <summary> /// Divide vertex buffer(Position, Normal, UV, VertexColor, Skinning and BlendShapes) by submesh usage, then export /// </summary> /// <param name="gltf"></param> /// <param name="gltfBuffer"></param> /// <param name="unityMesh"></param> /// <param name="unityMaterials"></param> /// <param name="axisInverter"></param> /// <param name="settings"></param> /// <returns></returns> public static (glTFMesh, Dictionary <int, int>) Export(ExportingGltfData data, MeshExportInfo unityMesh, List <Material> unityMaterials, IAxisInverter axisInverter, GltfExportSettings settings) { var mesh = unityMesh.Mesh; var gltfMesh = new glTFMesh(mesh.name); if (settings.ExportTangents) { // no support throw new NotImplementedException(); } var positions = mesh.vertices; var normals = mesh.normals; var uv = mesh.uv; var boneWeights = mesh.boneWeights; if (boneWeights.All(x => x.weight0 == 0 && x.weight1 == 0 && x.weight2 == 0 && x.weight3 == 0)) { boneWeights = null; } var colors = mesh.colors; Func <int, int> getJointIndex = null; if (boneWeights != null && boneWeights.Length == positions.Length) { getJointIndex = unityMesh.GetJointIndex; } Vector3[] blendShapePositions = new Vector3[mesh.vertexCount]; Vector3[] blendShapeNormals = new Vector3[mesh.vertexCount]; var vColorState = VertexColorUtility.DetectVertexColor(mesh, unityMaterials); var exportVertexColor = ( (settings.KeepVertexColor && mesh.colors != null && mesh.colors.Length == mesh.vertexCount) || // vertex color を残す設定 vColorState == VertexColorState.ExistsAndIsUsed || // VColor使っている vColorState == VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする) ); var usedIndices = new List <int>(); for (int i = 0; i < mesh.subMeshCount; ++i) { var indices = mesh.GetIndices(i); var hash = new HashSet <int>(indices); // aggregate vertex attributes var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (hash.Contains(k)) { // aggregate indices usedIndices.Add(k); buffer.PushVertex(k, axisInverter.InvertVector3(positions[k]), // POSITION axisInverter.InvertVector3(normals[k]), // NORMAL uv[k].ReverseUV() // UV ); if (getJointIndex != null) { buffer.PushBoneWeight(boneWeights[k]); } if (exportVertexColor) { buffer.PushColor(colors[k]); } } } var material = unityMesh.Materials[i]; var materialIndex = -1; if (material != null) { materialIndex = unityMaterials.IndexOf(material); } var flipped = new List <int>(); for (int j = 0; j < indices.Length; j += 3) { var t0 = indices[j]; var t1 = indices[j + 1]; var t2 = indices[j + 2]; flipped.Add(t2); flipped.Add(t1); flipped.Add(t0); } var gltfPrimitive = buffer.ToGltfPrimitive(data, materialIndex, flipped); // blendShape(morph target) for (int j = 0; j < mesh.blendShapeCount; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(usedIndices.Count); // aggriage morph target mesh.GetBlendShapeFrameVertices(j, 0, blendShapePositions, blendShapeNormals, null); int l = 0; foreach (var k in usedIndices) { blendShape.Set(l++, axisInverter.InvertVector3(blendShapePositions[k]), axisInverter.InvertVector3(blendShapeNormals[k])); } gltfPrimitive.targets.Add(blendShape.ToGltf(data, !settings.ExportOnlyBlendShapePosition, settings.UseSparseAccessorForMorphTarget)); } gltfMesh.primitives.Add(gltfPrimitive); } var targetNames = Enumerable.Range(0, mesh.blendShapeCount).Select(x => mesh.GetBlendShapeName(x)).ToArray(); gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); return(gltfMesh, Enumerable.Range(0, mesh.blendShapeCount).ToDictionary(x => x, x => x)); }
/// <summary> /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど /// glTF 的にはありうる。 /// /// primitive を独立した(Independent) Mesh として扱いこれを連結する。 /// </summary> /// <param name="ctx"></param> /// <param name="gltfMesh"></param> /// <returns></returns> public void ImportMeshIndependentVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh) { foreach (var prim in gltfMesh.primitives) { var indexOffset = m_positions.Count; var indexBuffer = prim.indices; // position は必ずある var positionCount = m_positions.Count; m_positions.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); positionCount = m_positions.Count - positionCount; // normal if (prim.attributes.NORMAL != -1) { FillZero(m_normals); m_normals.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } #if false if (prim.attributes.TANGENT != -1) { FillZero(tangetns); tangents.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } #endif // uv if (prim.attributes.TEXCOORD_0 != -1) { FillZero(m_uv); if (ctx.IsGeneratedUniGLTFAndOlder(1, 16)) { #pragma warning disable 0612 // backward compatibility m_uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); #pragma warning restore 0612 } else { m_uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseUV())); } } // uv2 if (prim.attributes.TEXCOORD_1 != -1) { FillZero(m_uv2); m_uv2.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_1).Select(x => x.ReverseUV())); } // color if (prim.attributes.COLOR_0 != -1) { FillZero(m_colors); m_colors.AddRange(ctx.GLTF.GetArrayFromAccessor <Color>(prim.attributes.COLOR_0)); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { FillZero(m_boneWeights); var joints0 = ctx.GLTF.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = ctx.GLTF.GetArrayFromAccessor <Float4>(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; m_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]; var blendShape = new BlendShape(i.ToString()); if (primTarget.POSITION != -1) { FillZero(blendShape.Positions); blendShape.Positions.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { FillZero(blendShape.Normals); blendShape.Normals.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { FillZero(blendShape.Tangents); blendShape.Tangents.AddRange( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } m_blendShapes.Add(blendShape); } } var indices = (indexBuffer >= 0) ? ctx.GLTF.GetIndices(indexBuffer) : TriangleUtil.FlipTriangle(Enumerable.Range(0, m_positions.Count)).ToArray() // without index array ; for (int i = 0; i < indices.Length; ++i) { indices[i] += indexOffset; } m_subMeshes.Add(indices); // material m_materialIndices.Add(prim.material); } }
/// <summary> /// /// 各primitiveが同じ attribute を共有している場合専用のローダー。 /// /// </summary> /// <param name="ctx"></param> /// <param name="gltfMesh"></param> /// <returns></returns> public void ImportMeshSharingVertexBuffer(ImporterContext ctx, glTFMesh gltfMesh) { { // 同じVertexBufferを共有しているので先頭のモノを使う var prim = gltfMesh.primitives.First(); m_positions.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).SelectInplace(x => x.ReverseZ())); // normal if (prim.attributes.NORMAL != -1) { m_normals.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).SelectInplace(x => x.ReverseZ())); } #if false // tangent if (prim.attributes.TANGENT != -1) { tangents.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).SelectInplace(x => x.ReverseZ())); } #endif // uv if (prim.attributes.TEXCOORD_0 != -1) { if (ctx.IsGeneratedUniGLTFAndOlder(1, 16)) { #pragma warning disable 0612 // backward compatibility m_uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).SelectInplace(x => x.ReverseY())); #pragma warning restore 0612 } else { m_uv.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).SelectInplace(x => x.ReverseUV())); } } // uv2 if (prim.attributes.TEXCOORD_1 != -1) { m_uv2.AddRange(ctx.GLTF.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_1).SelectInplace(x => x.ReverseUV())); } // color if (prim.attributes.COLOR_0 != -1) { if (ctx.GLTF.accessors[prim.attributes.COLOR_0].TypeCount == 3) { var vec3Color = ctx.GLTF.GetArrayFromAccessor <Vector3>(prim.attributes.COLOR_0); m_colors.AddRange(new Color[vec3Color.Length]); for (int i = 0; i < vec3Color.Length; i++) { Vector3 color = vec3Color[i]; m_colors[i] = new Color(color.x, color.y, color.z); } } else if (ctx.GLTF.accessors[prim.attributes.COLOR_0].TypeCount == 4) { m_colors.AddRange(ctx.GLTF.GetArrayFromAccessor <Color>(prim.attributes.COLOR_0)); } else { throw new NotImplementedException(string.Format("unknown color type {0}", ctx.GLTF.accessors[prim.attributes.COLOR_0].type)); } } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = ctx.GLTF.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = ctx.GLTF.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0); for (int i = 0; i < weights0.Length; ++i) { weights0[i] = weights0[i].One(); } 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; m_boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { m_blendShapes.AddRange(prim.targets.Select((x, i) => new BlendShape(i.ToString()))); for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = m_blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.POSITION), x => x.ReverseZ()); } if (primTarget.NORMAL != -1) { blendShape.Normals.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.NORMAL), x => x.ReverseZ()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.Assign( ctx.GLTF.GetArrayFromAccessor <Vector3>(primTarget.TANGENT), x => x.ReverseZ()); } } } } foreach (var prim in gltfMesh.primitives) { if (prim.indices == -1) { m_subMeshes.Add(TriangleUtil.FlipTriangle(Enumerable.Range(0, m_positions.Count)).ToArray()); } else { var indices = ctx.GLTF.GetIndices(prim.indices); m_subMeshes.Add(indices); } // material m_materialIndices.Add(prim.material); } }
static glTFMesh ExportPrimitives(glTF gltf, int bufferIndex, string rendererName, Mesh mesh, Material[] materials, List <Material> unityMaterials) { var positions = mesh.vertices.Select(y => y.ReverseZ()).ToArray(); var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, positions, glBufferTarget.ARRAY_BUFFER); gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.normals.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER); #if GLTF_EXPORT_TANGENTS var tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(y => y.ReverseZ()).ToArray(), glBufferTarget.ARRAY_BUFFER); #endif var uvAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var colorAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.colors, glBufferTarget.ARRAY_BUFFER); var boneweights = mesh.boneWeights; var weightAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); var jointsAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, boneweights.Select(y => new UShort4((ushort)y.boneIndex0, (ushort)y.boneIndex1, (ushort)y.boneIndex2, (ushort)y.boneIndex3)).ToArray(), glBufferTarget.ARRAY_BUFFER); var attributes = new glTFAttributes { POSITION = positionAccessorIndex, }; if (normalAccessorIndex != -1) { attributes.NORMAL = normalAccessorIndex; } #if GLTF_EXPORT_TANGENTS if (tangentAccessorIndex != -1) { attributes.TANGENT = tangentAccessorIndex; } #endif if (uvAccessorIndex != -1) { attributes.TEXCOORD_0 = uvAccessorIndex; } if (colorAccessorIndex != -1) { attributes.COLOR_0 = colorAccessorIndex; } if (weightAccessorIndex != -1) { attributes.WEIGHTS_0 = weightAccessorIndex; } if (jointsAccessorIndex != -1) { attributes.JOINTS_0 = jointsAccessorIndex; } var gltfMesh = new glTFMesh(mesh.name); for (int j = 0; j < mesh.subMeshCount; ++j) { var indices = TriangleUtil.FlipTriangle(mesh.GetIndices(j)).Select(y => (uint)y).ToArray(); var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices, glBufferTarget.ELEMENT_ARRAY_BUFFER); if (j >= materials.Length) { Debug.LogWarningFormat("{0}.materials is not enough", rendererName); break; } gltfMesh.primitives.Add(new glTFPrimitives { attributes = attributes, indices = indicesAccessorIndex, mode = 4, // triangels ? material = unityMaterials.IndexOf(materials[j]) }); } return(gltfMesh); }
static MeshWithMaterials ImportMesh(glTF gltf, int meshIndex, glTFMesh gltfMesh, List <Material> materials, GetBlendShapeName getBlendShapeName ) { glTFAttributes lastAttributes = null; var sharedAttributes = true; foreach (var prim in gltfMesh.primitives) { if (lastAttributes != null && !prim.attributes.Equals(lastAttributes)) { sharedAttributes = false; break; } lastAttributes = prim.attributes; } var positions = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var colors = new List <Vector4>(); var uv = new List <Vector2>(); var boneWeights = new List <BoneWeight>(); var subMeshes = new List <int[]>(); var materialIndices = new List <int>(); BlendShape[] blendShapes = null; if (sharedAttributes) { // multiple submesh sharing same VertexBuffer { var prim = gltfMesh.primitives.First(); positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } // tangent if (prim.attributes.TANGENT != -1) { tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } // vertex color if (prim.attributes.COLOR_0 != -1) { colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0)); } // uv if (prim.attributes.TEXCOORD_0 != -1) { uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); } else { // for inconsistent attributes in primitives uv.AddRange(new Vector2[positions.Count]); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = gltf.GetArrayFromAccessor <Float4>(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; boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { if (blendShapes == null) { blendShapes = prim.targets.Select((x, i) => new BlendShape(getBlendShapeName(meshIndex, i))).ToArray(); } for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShape.Normals.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } } } } foreach (var prim in gltfMesh.primitives) { var indices = gltf.GetIndices(prim.indices).Select(x => x).ToArray(); subMeshes.Add(indices); // material materialIndices.Add(prim.material); } } else { // multiple submMesh is not sharing a VertexBuffer. // each subMesh use a independent VertexBuffer. var targets = gltfMesh.primitives[0].targets; for (int i = 1; i < gltfMesh.primitives.Count; ++i) { if (!gltfMesh.primitives[i].targets.SequenceEqual(targets)) { throw new NotImplementedException(string.Format("diffirent targets: {0} with {1}", gltfMesh.primitives[i], targets)); } } foreach (var prim in gltfMesh.primitives) { var indexOffset = positions.Count; var indexBuffer = prim.indices; var positionCount = positions.Count; positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); positionCount = positions.Count - positionCount; // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } if (prim.attributes.TANGENT != -1) { tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } // vertex color if (prim.attributes.COLOR_0 != -1) { colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0)); } // uv if (prim.attributes.TEXCOORD_0 != -1) { uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); } else { // for inconsistent attributes in primitives uv.AddRange(new Vector2[positionCount]); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = gltf.GetArrayFromAccessor <Float4>(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; boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { if (blendShapes == null) { blendShapes = prim.targets.Select((x, i) => new BlendShape(i.ToString())).ToArray(); } for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShape.Normals.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } } } var indices = (indexBuffer >= 0) ? gltf.GetIndices(indexBuffer).Select(x => x + indexOffset).ToArray() : TriangleUtil.FlipTriangle(Enumerable.Range(0, positions.Count)).ToArray() // without index array ; subMeshes.Add(indices); // material materialIndices.Add(prim.material); } } if (!materialIndices.Any()) { materialIndices.Add(0); } //Debug.Log(prims.ToJson()); var mesh = new Mesh(); mesh.name = gltfMesh.name; if (positions.Count > UInt16.MaxValue) { #if UNITY_2017_3_OR_NEWER mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #else Debug.LogWarningFormat("vertices {0} exceed 65535. not implemented. Unity2017.3 supports large mesh", positions.Count); #endif } mesh.vertices = positions.ToArray(); if (normals.Any()) { mesh.normals = normals.ToArray(); } else { mesh.RecalculateNormals(); } if (tangents.Any()) { mesh.tangents = tangents.ToArray(); } else { mesh.RecalculateTangents(); } if (colors.Any()) { mesh.colors = colors.ConvertAll(x => new Color(x.x, x.y, x.z, 1f)).ToArray(); } if (uv.Any()) { mesh.uv = uv.ToArray(); } if (boneWeights.Any()) { mesh.boneWeights = boneWeights.ToArray(); } mesh.subMeshCount = subMeshes.Count; for (int i = 0; i < subMeshes.Count; ++i) { mesh.SetTriangles(subMeshes[i], i); } var result = new MeshWithMaterials { Mesh = mesh, Materials = materialIndices.Select(x => materials[x]).ToArray() }; List <Material> matList = result.Materials.ToList(); matList.RemoveAll(x => x == null); result.Materials = matList.ToArray(); if (blendShapes != null) { foreach (var blendShape in blendShapes) { if (blendShape.Positions.Count > 0) { if (blendShape.Positions.Count == mesh.vertexCount) { mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT, blendShape.Positions.ToArray(), normals.Count == mesh.vertexCount ? blendShape.Normals.ToArray() : null, null ); } else { Debug.LogWarningFormat("May be partial primitive has blendShape. Rquire separete mesh or extend blend shape, but not implemented: {0}", blendShape.Name); } } } } return(result); }