private static void ExportFromMenu(bool isGlb, MeshExportSettings settings) { var go = Selection.activeObject as GameObject; var ext = isGlb ? "glb" : "gltf"; if (go.transform.position == Vector3.zero && go.transform.rotation == Quaternion.identity && go.transform.localScale == Vector3.one) { var path = EditorUtility.SaveFilePanel( $"Save {ext}", "", go.name + $".{ext}", $"{ext}"); if (string.IsNullOrEmpty(path)) { return; } var gltf = new glTF(); using (var exporter = new gltfExporter(gltf)) { exporter.Prepare(go); exporter.Export(settings); } if (isGlb) { var bytes = gltf.ToGlbBytes(); File.WriteAllBytes(path, bytes); } else { var(json, buffers) = gltf.ToGltf(path); // without BOM var encoding = new System.Text.UTF8Encoding(false); File.WriteAllText(path, json, encoding); // write to local folder var dir = Path.GetDirectoryName(path); foreach (var b in buffers) { var bufferPath = Path.Combine(dir, b.uri); File.WriteAllBytes(bufferPath, b.GetBytes().ToArray()); } } if (path.StartsWithUnityAssetPath()) { AssetDatabase.ImportAsset(path.ToUnityRelativePath()); AssetDatabase.Refresh(); } } else { EditorUtility.DisplayDialog("Error", "The Root transform should have Default translation, rotation and scale.", "ok"); } }
protected override void ExportPath(string path) { var ext = Path.GetExtension(path).ToLower(); var isGlb = false; switch (ext) { case ".glb": isGlb = true; break; case ".gltf": isGlb = false; break; default: throw new System.Exception(); } var gltf = new glTF(); using (var exporter = new gltfExporter(gltf, m_settings.InverseAxis)) { exporter.Prepare(State.ExportRoot); var settings = new MeshExportSettings { ExportOnlyBlendShapePosition = m_settings.DropNormal, UseSparseAccessorForMorphTarget = m_settings.Sparse, DivideVertexBuffer = m_settings.DivideVertexBuffer, }; exporter.Export(settings, new EditorTextureSerializer()); } if (isGlb) { var bytes = gltf.ToGlbBytes(); File.WriteAllBytes(path, bytes); } else { var(json, buffers) = gltf.ToGltf(path); // without BOM var encoding = new System.Text.UTF8Encoding(false); File.WriteAllText(path, json, encoding); // write to local folder var dir = Path.GetDirectoryName(path); foreach (var b in buffers) { var bufferPath = Path.Combine(dir, b.uri); File.WriteAllBytes(bufferPath, b.GetBytes().ToArray()); } } if (path.StartsWithUnityAssetPath()) { AssetDatabase.ImportAsset(path.ToUnityRelativePath()); AssetDatabase.Refresh(); } }
private static void Export(GameObject go, string path, MeshExportSettings settings, Axises inverseAxis) { var ext = Path.GetExtension(path).ToLower(); var isGlb = false; switch (ext) { case ".glb": isGlb = true; break; case ".gltf": isGlb = false; break; default: throw new System.Exception(); } var gltf = new glTF(); using (var exporter = new gltfExporter(gltf, inverseAxis)) { exporter.Prepare(go); exporter.Export(settings, AssetTextureUtil.IsTextureEditorAsset); } if (isGlb) { var bytes = gltf.ToGlbBytes(); File.WriteAllBytes(path, bytes); } else { var(json, buffers) = gltf.ToGltf(path); // without BOM var encoding = new System.Text.UTF8Encoding(false); File.WriteAllText(path, json, encoding); // write to local folder var dir = Path.GetDirectoryName(path); foreach (var b in buffers) { var bufferPath = Path.Combine(dir, b.uri); File.WriteAllBytes(bufferPath, b.GetBytes().ToArray()); } } if (path.StartsWithUnityAssetPath()) { AssetDatabase.ImportAsset(path.ToUnityRelativePath()); AssetDatabase.Refresh(); } }
public void DividedVertexBufferTest() { var glTF = new glTF(); var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); var Materials = new List <Material> { new Material(Shader.Find("Standard")), // A new Material(Shader.Find("Standard")), // B }; var(go, unityMesh) = CreateMesh(Materials.ToArray()); var meshExportSettings = new MeshExportSettings { DivideVertexBuffer = true }; var axisInverter = Axes.X.Create(); var(gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, new MeshWithRenderer(go.transform), Materials, meshExportSettings, axisInverter); { var indices = glTF.GetIndices(gltfMesh.primitives[0].indices); Assert.AreEqual(0, indices[0]); Assert.AreEqual(1, indices[1]); Assert.AreEqual(3, indices[2]); Assert.AreEqual(3, indices[3]); Assert.AreEqual(1, indices[4]); Assert.AreEqual(2, indices[5]); } { var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[0].attributes.POSITION); Assert.AreEqual(4, positions.Length); } { var indices = glTF.GetIndices(gltfMesh.primitives[1].indices); Assert.AreEqual(0, indices[0]); Assert.AreEqual(1, indices[1]); Assert.AreEqual(3, indices[2]); Assert.AreEqual(3, indices[3]); Assert.AreEqual(1, indices[4]); Assert.AreEqual(2, indices[5]); } { var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[1].attributes.POSITION); Assert.AreEqual(4, positions.Length); } }
public void SetRoot(GameObject ExportRoot, MeshExportSettings settings) { Settings = settings; Meshes.Clear(); if (ExportRoot == null) { return; } foreach (var renderer in ExportRoot.GetComponentsInChildren <Renderer>(true)) { if (TryGetMeshInfo(ExportRoot, renderer, out MeshExportInfo info)) { Meshes.Add(info); } } }
private static Texture2D AssignTextureToMaterialPropertyAndExportAndExtract(Texture2D srcTex, string srcImageName, string propertyName) { // Prepare var root = GameObject.CreatePrimitive(PrimitiveType.Cube); var mat = new Material(Shader.Find("Standard")); mat.SetTexture(propertyName, srcTex); root.GetComponent <MeshRenderer>().sharedMaterial = mat; // Export glTF var gltf = new glTF(); using (var exporter = new gltfExporter(gltf, Axes.X)) { exporter.Prepare(root); var settings = new MeshExportSettings { ExportOnlyBlendShapePosition = false, UseSparseAccessorForMorphTarget = false, DivideVertexBuffer = false, }; exporter.Export(settings, new EditorTextureSerializer()); } Assert.AreEqual(1, gltf.images.Count); var exportedImage = gltf.images[0]; Assert.AreEqual("image/png", exportedImage.mimeType); Assert.AreEqual(srcImageName, exportedImage.name); UnityEngine.Object.DestroyImmediate(mat); UnityEngine.Object.DestroyImmediate(root); // Extract Image to Texture2D var exportedBytes = gltf.GetViewBytes(exportedImage.bufferView).ToArray(); var exportedTexture = new Texture2D(2, 2, TextureFormat.ARGB32, mipChain: false, linear: false); Assert.IsTrue(exportedTexture.LoadImage(exportedBytes)); // Always true ? Assert.AreEqual(srcTex.width, exportedTexture.width); Assert.AreEqual(srcTex.height, exportedTexture.height); return(exportedTexture); }
public virtual void Export(MeshExportSettings meshExportSettings) { var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); GameObject tmpParent = null; if (Copy.transform.childCount == 0) { tmpParent = new GameObject("tmpParent"); Copy.transform.SetParent(tmpParent.transform, true); Copy = tmpParent; } try { Nodes = Copy.transform.Traverse() .Skip(1) // exclude root object for the symmetry with the importer .ToList(); #region Materials and Textures Materials = Nodes.SelectMany(x => x.GetSharedMaterials()).Where(x => x != null).Distinct().ToList(); var unityTextures = Materials.SelectMany(x => TextureIO.GetTextures(x)).Where(x => x.Texture != null).Distinct().ToList(); TextureManager = new TextureExportManager(unityTextures.Select(x => x.Texture)); var materialExporter = CreateMaterialExporter(); glTF.materials = Materials.Select(x => materialExporter.ExportMaterial(x, TextureManager)).ToList(); for (int i = 0; i < unityTextures.Count; ++i) { var unityTexture = unityTextures[i]; TextureIO.ExportTexture(glTF, bufferIndex, TextureManager.GetExportTexture(i), unityTexture.TextureType); } #endregion #region Meshes var unityMeshes = Nodes .Select(x => new MeshExporter.MeshWithRenderer { Mesh = x.GetSharedMesh(), Renderer = x.GetComponent <Renderer>(), }) .Where(x => { if (x.Mesh == null) { return(false); } if (x.Renderer.sharedMaterials == null || x.Renderer.sharedMaterials.Length == 0) { return(false); } return(true); }) .ToList(); MeshBlendShapeIndexMap = new Dictionary <Mesh, Dictionary <int, int> >(); foreach (var(mesh, gltfMesh, blendShapeIndexMap) in MeshExporter.ExportMeshes( glTF, bufferIndex, unityMeshes, Materials, meshExportSettings)) { glTF.meshes.Add(gltfMesh); if (!MeshBlendShapeIndexMap.ContainsKey(mesh)) { // 同じmeshが複数回現れた MeshBlendShapeIndexMap.Add(mesh, blendShapeIndexMap); } } Meshes = unityMeshes.Select(x => x.Mesh).ToList(); #endregion #region Nodes and Skins var unitySkins = Nodes .Select(x => x.GetComponent <SkinnedMeshRenderer>()).Where(x => x != null && x.bones != null && x.bones.Length > 0) .ToList(); glTF.nodes = Nodes.Select(x => ExportNode(x, Nodes, unityMeshes.Select(y => y.Renderer).ToList(), unitySkins)).ToList(); glTF.scenes = new List <gltfScene> { new gltfScene { nodes = Copy.transform.GetChildren().Select(x => Nodes.IndexOf(x)).ToArray(), } }; foreach (var x in unitySkins) { var matrices = x.sharedMesh.bindposes.Select(y => y.ReverseZ()).ToArray(); var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE); var skin = new glTFSkin { inverseBindMatrices = accessor, joints = x.bones.Select(y => Nodes.IndexOf(y)).ToArray(), skeleton = Nodes.IndexOf(x.rootBone), }; var skinIndex = glTF.skins.Count; glTF.skins.Add(skin); foreach (var z in Nodes.Where(y => y.Has(x))) { var nodeIndex = Nodes.IndexOf(z); var node = glTF.nodes[nodeIndex]; node.skin = skinIndex; } } #endregion #if UNITY_EDITOR #region Animations var clips = new List <AnimationClip>(); var animator = Copy.GetComponent <Animator>(); var animation = Copy.GetComponent <Animation>(); if (animator != null) { clips = AnimationExporter.GetAnimationClips(animator); } else if (animation != null) { clips = AnimationExporter.GetAnimationClips(animation); } if (clips.Any()) { foreach (AnimationClip clip in clips) { var animationWithCurve = AnimationExporter.Export(clip, Copy.transform, Nodes); foreach (var kv in animationWithCurve.SamplerMap) { var sampler = animationWithCurve.Animation.samplers[kv.Key]; var inputAccessorIndex = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.Input); sampler.input = inputAccessorIndex; var outputAccessorIndex = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.Output); sampler.output = outputAccessorIndex; // modify accessors var outputAccessor = glTF.accessors[outputAccessorIndex]; var channel = animationWithCurve.Animation.channels.First(x => x.sampler == kv.Key); switch (glTFAnimationTarget.GetElementCount(channel.target.path)) { case 1: outputAccessor.type = "SCALAR"; //outputAccessor.count = ; break; case 3: outputAccessor.type = "VEC3"; outputAccessor.count /= 3; break; case 4: outputAccessor.type = "VEC4"; outputAccessor.count /= 4; break; default: throw new NotImplementedException(); } } animationWithCurve.Animation.name = clip.name; glTF.animations.Add(animationWithCurve.Animation); } } #endregion #endif } finally { if (tmpParent != null) { tmpParent.transform.GetChild(0).SetParent(null); if (Application.isPlaying) { GameObject.Destroy(tmpParent); } else { GameObject.DestroyImmediate(tmpParent); } } } }
public static IEnumerable <(Mesh, glTFMesh, Dictionary <int, int>)> ExportMeshes(glTF gltf, int bufferIndex, List <MeshWithRenderer> unityMeshes, List <Material> unityMaterials, MeshExportSettings settings) { foreach (var unityMesh in unityMeshes) { var gltfMesh = ExportPrimitives(gltf, bufferIndex, unityMesh, unityMaterials); var targetNames = new List <string>(); var blendShapeIndexMap = new Dictionary <int, int>(); int exportBlendShapes = 0; for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(gltf, bufferIndex, unityMesh.Mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition); 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); yield return(unityMesh.Mesh, gltfMesh, blendShapeIndexMap); } }
public virtual void Export(MeshExportSettings meshExportSettings, ITextureSerializer textureSerializer) { var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); Nodes = Copy.transform.Traverse() .Skip(1) // exclude root object for the symmetry with the importer .ToList(); var unityMeshes = MeshWithRenderer.FromNodes(Nodes).Where(x => x.Mesh.vertices.Any()).ToList(); var uniqueUnityMeshes = new List <MeshWithRenderer>(); foreach (var um in unityMeshes) { if (!uniqueUnityMeshes.Any(x => x.IsSameMeshAndMaterials(um))) { uniqueUnityMeshes.Add(um); } } #region Materials and Textures Materials = uniqueUnityMeshes.SelectMany(x => x.Renderer.sharedMaterials).Where(x => x != null).Distinct().ToList(); TextureExporter = new TextureExporter(textureSerializer); var materialExporter = CreateMaterialExporter(); glTF.materials = Materials.Select(x => materialExporter.ExportMaterial(x, TextureExporter)).ToList(); #endregion #region Meshes MeshBlendShapeIndexMap = new Dictionary <Mesh, Dictionary <int, int> >(); foreach (var unityMesh in uniqueUnityMeshes) { var(gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, unityMesh, Materials, meshExportSettings, m_axisInverter); glTF.meshes.Add(gltfMesh); Meshes.Add(unityMesh.Mesh); if (!MeshBlendShapeIndexMap.ContainsKey(unityMesh.Mesh)) { // 同じmeshが複数回現れた MeshBlendShapeIndexMap.Add(unityMesh.Mesh, blendShapeIndexMap); } } #endregion #region Nodes and Skins var unitySkins = uniqueUnityMeshes .Where(x => x.UniqueBones != null) .ToList(); glTF.nodes = Nodes.Select(x => ExportNode(x, Nodes, uniqueUnityMeshes, unitySkins.Select(y => y.Renderer as SkinnedMeshRenderer).ToList())).ToList(); glTF.scenes = new List <gltfScene> { new gltfScene { nodes = Copy.transform.GetChildren().Select(x => Nodes.IndexOf(x)).ToArray(), } }; foreach (var x in unitySkins) { var matrices = x.GetBindPoses().Select(m_axisInverter.InvertMat4).ToArray(); var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE); var renderer = x.Renderer as SkinnedMeshRenderer; var skin = new glTFSkin { inverseBindMatrices = accessor, joints = x.UniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(), skeleton = Nodes.IndexOf(renderer.rootBone), }; var skinIndex = glTF.skins.Count; glTF.skins.Add(skin); foreach (var z in Nodes.Where(y => y.Has(x.Renderer))) { var nodeIndex = Nodes.IndexOf(z); var node = glTF.nodes[nodeIndex]; node.skin = skinIndex; } } #endregion #if UNITY_EDITOR #region Animations var clips = new List <AnimationClip>(); var animator = Copy.GetComponent <Animator>(); var animation = Copy.GetComponent <Animation>(); if (animator != null) { clips = AnimationExporter.GetAnimationClips(animator); } else if (animation != null) { clips = AnimationExporter.GetAnimationClips(animation); } if (clips.Any()) { foreach (AnimationClip clip in clips) { var animationWithCurve = AnimationExporter.Export(clip, Copy.transform, Nodes); foreach (var kv in animationWithCurve.SamplerMap) { var sampler = animationWithCurve.Animation.samplers[kv.Key]; var inputAccessorIndex = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.Input); sampler.input = inputAccessorIndex; var outputAccessorIndex = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.Output); sampler.output = outputAccessorIndex; // modify accessors var outputAccessor = glTF.accessors[outputAccessorIndex]; var channel = animationWithCurve.Animation.channels.First(x => x.sampler == kv.Key); switch (glTFAnimationTarget.GetElementCount(channel.target.path)) { case 1: outputAccessor.type = "SCALAR"; //outputAccessor.count = ; break; case 3: outputAccessor.type = "VEC3"; outputAccessor.count /= 3; break; case 4: outputAccessor.type = "VEC4"; outputAccessor.count /= 4; break; default: throw new NotImplementedException(); } } animationWithCurve.Animation.name = clip.name; glTF.animations.Add(animationWithCurve.Animation); } } #endregion #endif ExportExtensions(textureSerializer); // Extension で Texture が増える場合があるので最後に呼ぶ for (int i = 0; i < TextureExporter.Exported.Count; ++i) { var(unityTexture, colorSpace) = TextureExporter.Exported[i]; glTF.PushGltfTexture(bufferIndex, unityTexture, colorSpace, textureSerializer); } }
public static IEnumerable <(Mesh, glTFMesh, Dictionary <int, int>)> ExportMeshes(glTF gltf, int bufferIndex, List <MeshWithRenderer> unityMeshes, List <Material> unityMaterials, MeshExportSettings settings) { for (int i = 0; i < unityMeshes.Count; ++i) { var x = unityMeshes[i]; var mesh = x.Mesh; var materials = x.Renderer.sharedMaterials; var gltfMesh = ExportPrimitives(gltf, bufferIndex, x.Renderer.name, mesh, materials, unityMaterials); var blendShapeIndexMap = new Dictionary <int, int>(); int exportBlendShapes = 0; for (int j = 0; j < mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(gltf, bufferIndex, mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition); if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0) { continue; } // maybe skip blendShapeIndexMap.Add(j, exportBlendShapes++); // // all primitive has same blendShape // for (int k = 0; k < gltfMesh.primitives.Count; ++k) { gltfMesh.primitives[k].targets.Add(morphTarget); gltfMesh.primitives[k].extras.targetNames.Add(mesh.GetBlendShapeName(j)); } } yield return(mesh, gltfMesh, blendShapeIndexMap); } }
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; }
public static (glTFMesh gltfMesh, Dictionary <int, int> blendShapeIndexMap) ExportMesh(glTF gltf, int bufferIndex, Mesh mesh, Renderer renderer, List <Material> exportedMaterials, MeshExportSettings meshExportSettings, IAxisInverter axisInverter) { var meshMaterials = default(Material[]); if (renderer != null) { meshMaterials = renderer.sharedMaterials; } var boneWeights = default(BoneWeight[]); var jointIndexMap = default(int[]); if (renderer is SkinnedMeshRenderer skin) { var bones = skin.bones; var uniqueBones = bones.Distinct().ToArray(); jointIndexMap = new int[bones.Length]; for (var i = 0; i < bones.Length; i++) { jointIndexMap[i] = Array.IndexOf(uniqueBones, bones[i]); } boneWeights = mesh.boneWeights; } var gltfMesh = ExportPrimitives(gltf, bufferIndex, mesh, meshMaterials, boneWeights, jointIndexMap, exportedMaterials); var targetNames = new List <string>(); var blendShapeIndexMap = new Dictionary <int, int>(); int exportBlendShapes = 0; for (int j = 0; j < mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(gltf, bufferIndex, mesh, j, meshExportSettings.UseSparseAccessorForMorphTarget, meshExportSettings.ExportOnlyBlendShapePosition, axisInverter); if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0) { continue; } // maybe skip var blendShapeName = 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> /// /// </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); }
/// <summary> /// primitive 間で vertex を共有する形で Export する。 /// /// UniVRM-0.71.0 までの挙動 /// /// /// </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> static glTFMesh ExportSharedVertexBuffer(glTF gltf, int bufferIndex, MeshWithRenderer unityMesh, List <Material> unityMaterials, IAxisInverter axisInverter, MeshExportSettings settings) { var mesh = unityMesh.Mesh; var materials = unityMesh.Renderer.sharedMaterials; var positions = mesh.vertices.Select(axisInverter.InvertVector3).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 => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER); int?tangentAccessorIndex = default; if (settings.ExportTangents) { tangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, mesh.tangents.Select(axisInverter.InvertVector4).ToArray(), glBufferTarget.ARRAY_BUFFER); } 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 (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 = 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); }