diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs deleted file mode 100644 index c10c09b87..000000000 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Linq; -using UnityEngine; - -namespace UniGLTF -{ - public static class ExporterExtensions - { - public static bool EnableForExport(this Component mono) - { - if (mono.transform.Ancestors().Any(x => !x.gameObject.activeSelf)) - { - // 自分か祖先に !activeSelf がいる - return false; - } - return true; - } - } -} diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/GltfExportWindow.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/GltfExportWindow.cs index 2ad68337a..6888cd5e3 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/GltfExportWindow.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/GltfExportWindow.cs @@ -73,7 +73,7 @@ namespace UniGLTF protected override void OnLayout() { - m_meshes.SetRoot(State.ExportRoot, m_settings.MeshExportSettings); + m_meshes.SetRoot(State.ExportRoot, m_settings.MeshExportSettings, new DefualtBlendShapeExportFilter()); } protected override bool DoGUI(bool isValid) diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs index a22dcca35..f00644a14 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidator.cs @@ -31,148 +31,12 @@ namespace UniGLTF public int ExpectedExportByteSize => Meshes.Where(x => x.IsRendererActive).Sum(x => x.ExportByteSize); - public MeshExportSettings Settings; - - public virtual bool UseBlendShape(int index, string relativePath) => true; - - public virtual void CalcMeshSize(ref MeshExportInfo info, - string relativePath) + public void SetRoot(GameObject ExportRoot, MeshExportSettings settings, IBlendShapeExportFilter blendShapeFilter) { - var sb = new StringBuilder(); - if (!info.IsRendererActive) + MeshExportInfo.GetInfo(ExportRoot.transform.Traverse().Skip(1), Meshes, settings); + foreach(var info in Meshes) { - sb.Append("[NotActive]"); - } - - info.VertexCount = info.Mesh.vertexCount; - info.ExportVertexSize = 0; - info.TotalBlendShapeCount = 0; - info.ExportBlendShapeCount = 0; - - // float4 x 3 - // vertices - sb.Append($"(Pos"); - if (info.HasNormal) - { - sb.Append("+Nom"); - info.ExportVertexSize += 4 * 3; - } - if (info.HasUV) - { - sb.Append("+UV"); - info.ExportVertexSize += 4 * 2; - } - if (info.HasVertexColor) - { - sb.Append("+Col"); - info.ExportVertexSize += 4 * 4; - } - if (info.HasSkinning) - { - // short, float x 4 weights - sb.Append("+Skin"); - info.ExportVertexSize += (2 + 4) * 4; - } - // indices - info.IndexCount = info.Mesh.triangles.Length; - - // postion + normal ?. always tangent is ignored - info.TotalBlendShapeCount = info.Mesh.blendShapeCount; - info.ExportBlendShapeVertexSize = Settings.ExportOnlyBlendShapePosition ? 4 * 3 : 4 * (3 + 3); - for (var i = 0; i < info.Mesh.blendShapeCount; ++i) - { - if (!UseBlendShape(i, relativePath)) - { - continue; - } - - ++info.ExportBlendShapeCount; - } - - if (info.ExportBlendShapeCount > 0) - { - sb.Append($"+Morph x {info.ExportBlendShapeCount}"); - } - sb.Append($") x {info.Mesh.vertexCount}"); - switch (info.VertexColor) - { - case VertexColorState.ExistsAndIsUsed: - case VertexColorState.ExistsAndMixed: // エクスポートする - sb.Insert(0, "[use vcolor]"); - break; - case VertexColorState.ExistsButNotUsed: - sb.Insert(0, "[remove vcolor]"); - break; - } - if (info.ExportBlendShapeCount > 0 && !info.HasSkinning) - { - sb.Insert(0, "[morph without skin]"); - } - - // total bytes - sb.Insert(0, $"{info.ExportByteSize:#,0} Bytes = "); - info.Summary = sb.ToString(); - } - - bool TryGetMeshInfo(GameObject root, Renderer renderer, out MeshExportInfo info) - { - info = default; - if (root == null) - { - info.Summary = ""; - return false; - } - if (renderer == null) - { - info.Summary = "no Renderer"; - return false; - } - info.Renderer = renderer; - - if (renderer is SkinnedMeshRenderer smr) - { - info.Skinned = true; - info.Mesh = smr.sharedMesh; - info.IsRendererActive = smr.EnableForExport(); - } - else if (renderer is MeshRenderer mr) - { - var filter = mr.GetComponent(); - if (filter != null) - { - info.Mesh = filter.sharedMesh; - } - info.IsRendererActive = mr.EnableForExport(); - } - else - { - info.Summary = "no Mesh"; - return false; - } - - info.VertexColor = VertexColorUtility.DetectVertexColor(info.Mesh, info.Renderer.sharedMaterials); - - var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, root.transform); - CalcMeshSize(ref info, relativePath); - - return true; - } - - public void SetRoot(GameObject ExportRoot, MeshExportSettings settings) - { - Settings = settings; - Meshes.Clear(); - if (ExportRoot == null) - { - return; - } - - foreach (var renderer in ExportRoot.GetComponentsInChildren(true)) - { - if (TryGetMeshInfo(ExportRoot, renderer, out MeshExportInfo info)) - { - Meshes.Add(info); - } + info.CalcMeshSize(ExportRoot, info.Renderers[0].Item1, settings, blendShapeFilter); } } @@ -204,28 +68,28 @@ namespace UniGLTF foreach (var info in Meshes) { // invalid materials.len - if (info.Renderer.sharedMaterials.Length < info.Mesh.subMeshCount) + if (info.Materials.Length < info.Mesh.subMeshCount) { // submesh より material の方が少ない yield return Validation.Error(Messages.MATERIALS_LESS_THAN_SUBMESH_COUNT.Msg()); } else { - if (info.Renderer.sharedMaterials.Length > info.Mesh.subMeshCount) + if (info.Materials.Length > info.Mesh.subMeshCount) { // submesh より material の方が多い yield return Validation.Warning(Messages.MATERIALS_GREATER_THAN_SUBMESH_COUNT.Msg()); } - if (info.Renderer.sharedMaterials.Take(info.Mesh.subMeshCount).Any(x => x == null)) + if (info.Materials.Take(info.Mesh.subMeshCount).Any(x => x == null)) { // material に null が含まれる(unity で magenta になっているはず) - yield return Validation.Error($"{info.Renderer}: {Messages.MATERIALS_CONTAINS_NULL.Msg()}"); + yield return Validation.Error($"{info.Renderers}: {Messages.MATERIALS_CONTAINS_NULL.Msg()}"); } } } - foreach (var m in Meshes.SelectMany(x => x.Renderer.sharedMaterials).Distinct()) + foreach (var m in Meshes.SelectMany(x => x.Materials).Distinct()) { if (m == null) { diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidatorEditor.cs b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidatorEditor.cs index 70edf409c..f5160f291 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidatorEditor.cs +++ b/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/MeshExportValidatorEditor.cs @@ -40,12 +40,15 @@ namespace UniGLTF var (left, right) = LeftRight(r.x, r.y, col0, r.width - col0, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(left, $"{i,3}"); - GUI.enabled = false; - EditorGUI.ObjectField(right, info.Renderer, info.Renderer.GetType(), true); - - right.y += EditorGUIUtility.singleLineHeight; - EditorGUI.ObjectField(right, info.Mesh, info.Renderer.GetType(), true); - GUI.enabled = true; + using (new EditorGUI.DisabledScope(false)) + { + foreach (var (renderer, _) in info.Renderers) + { + EditorGUI.ObjectField(right, renderer, info.Renderers.GetType(), true); + } + right.y += EditorGUIUtility.singleLineHeight; + EditorGUI.ObjectField(right, info.Mesh, info.Renderers.GetType(), true); + } right.y += EditorGUIUtility.singleLineHeight; EditorGUI.LabelField(right, info.Summary); diff --git a/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs index f51247633..698b6898f 100644 --- a/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs +++ b/Assets/UniGLTF/Runtime/Extensions/UnityExtensions.cs @@ -213,7 +213,7 @@ namespace UniGLTF { var current = self; - var split = path.Split(new [] {'/'}, StringSplitOptions.RemoveEmptyEntries); + var split = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); foreach (var childName in split) { @@ -334,5 +334,15 @@ namespace UniGLTF } return go.AddComponent(); } + + public static bool EnableForExport(this Component mono) + { + if (mono.transform.Ancestors().Any(x => !x.gameObject.activeSelf)) + { + // 自分か祖先に !activeSelf がいる + return false; + } + return true; + } } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs new file mode 100644 index 000000000..753e44a67 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs @@ -0,0 +1,18 @@ +namespace UniGLTF +{ + public interface IBlendShapeExportFilter + { + bool UseBlendShape(int blendShapeIndex, string relativePath); + } + + public class DefualtBlendShapeExportFilter : IBlendShapeExportFilter + { + /// + /// Export all blendshape + /// + /// + /// + /// + public bool UseBlendShape(int blendShapeIndex, string relativePath) => true; + } +} diff --git a/Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs.meta similarity index 83% rename from Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs.meta rename to Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs.meta index 827e80db5..9a27d0dcc 100644 --- a/Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs.meta +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeFilter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 18a610aab46d5034cb787ab237e3dea2 +guid: d962c03c08e3d3a4eb5c52f5b4e0c93f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportInfo.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportInfo.cs index 768f27ce7..aa4f58667 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportInfo.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportInfo.cs @@ -1,25 +1,154 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; using UnityEngine; namespace UniGLTF { [Serializable] - public struct MeshExportInfo + public class MeshExportInfo { - public Renderer Renderer; + #region この2つの組が gltf mesh の Unique なキーとなる public Mesh Mesh; - public bool IsRendererActive; - public bool Skinned; + public Material[] Materials; + public bool IsSameMeshAndMaterials(MeshExportInfo other) + { + return IsSameMeshAndMaterials(other.Mesh, other.Materials); + } + + public bool IsSameMeshAndMaterials(Mesh mesh, Material[] materials) + { + if (Mesh != mesh) return false; + if (Materials.Length != materials.Length) return false; + for (var i = 0; i < Materials.Length; i++) + { + if (Materials[i] != materials[i]) return false; + } + return true; + } + + public bool IsSameMeshAndMaterials(Renderer r) + { + if (r is SkinnedMeshRenderer smr) + { + return IsSameMeshAndMaterials(smr.sharedMesh, smr.sharedMaterials); + } + else if (r is MeshRenderer mr) + { + var filter = r.GetComponent(); + if (filter == null) + { + return false; + } + return IsSameMeshAndMaterials(filter.sharedMesh, mr.sharedMaterials); + } + else + { + throw new NotImplementedException(); + } + } + + public static bool TryGetSameMeshIndex(List meshWithRenderers, Mesh mesh, Material[] materials, out int meshIndex) + { + for (var i = 0; i < meshWithRenderers.Count; i++) + { + if (meshWithRenderers[i].IsSameMeshAndMaterials(mesh, materials)) + { + meshIndex = i; + return true; + } + } + + meshIndex = -1; + return false; + } + + public bool CanExport + { + get + { + if (Mesh == null) + { + return false; + } + if (Mesh.vertexCount == 0) + { + return false; + } + if (Materials == null) + { + return false; + } + if (Materials.Length == 0) + { + return false; + } + return true; + } + } + #endregion + + /// + /// ひとつの Mesh を複数の Renderer が共有することがありうる + /// + List<(Renderer, Transform[] UniqueBones)> _renderers; + public IReadOnlyList<(Renderer, Transform[] UniqueBones)> Renderers => _renderers; + + public bool IsRendererActive => Renderers.Any(x => x.Item1.EnableForExport()); + + #region SkinnedMeshRenderer + public bool Skinned; + int[] JointIndexMap; + + /// + /// glTF は skinning の boneList の重複を許可しない + /// (unity は ok) + /// + /// + /// + public int GetJointIndex(int index) + { + if (index < 0) + { + return index; + } + + if (JointIndexMap != null) + { + return JointIndexMap[index]; + } + else + { + return index; + } + } + + public IEnumerable GetBindPoses() + { + var used = new HashSet(); + for (int i = 0; i < JointIndexMap.Length; ++i) + { + var index = JointIndexMap[i]; + if (used.Add(index)) + { + yield return Mesh.bindposes[i]; + } + } + } + #endregion + + #region VertexAttribute public bool HasNormal => Mesh != null && Mesh.normals != null && Mesh.normals.Length == Mesh.vertexCount; public bool HasUV => Mesh != null && Mesh.uv != null && Mesh.uv.Length == Mesh.vertexCount; - public bool HasVertexColor => Mesh.colors != null && Mesh.colors.Length == Mesh.vertexCount + public bool HasVertexColor => Mesh != null && Mesh.colors != null && Mesh.colors.Length == Mesh.vertexCount && VertexColor == VertexColorState.ExistsAndIsUsed || VertexColor == VertexColorState.ExistsAndMixed // Export する ; - public bool HasSkinning => Mesh.boneWeights != null && Mesh.boneWeights.Length == Mesh.vertexCount; + public bool HasSkinning => Mesh != null && Mesh.boneWeights != null && Mesh.boneWeights.Length == Mesh.vertexCount; public VertexColorState VertexColor; @@ -31,20 +160,253 @@ namespace UniGLTF /// [SkinningWeight] /// public int ExportVertexSize; + #endregion + #region Triangles public int IndexCount; // int 決め打ち public int IndicesSize => IndexCount * 4; + #endregion + #region BlendShape public int ExportBlendShapeVertexSize; public int TotalBlendShapeCount; public int ExportBlendShapeCount; + #endregion + #region Summary public int ExportByteSize => ExportVertexSize * VertexCount + IndicesSize + ExportBlendShapeCount * ExportBlendShapeVertexSize * VertexCount; public string Summary; + #endregion + + MeshExportInfo(Renderer renderer, MeshExportSettings settings) + { + if (renderer == null) + { + throw new ArgumentNullException(); + } + + Materials = renderer.sharedMaterials; + _renderers = new List<(Renderer, Transform[])>(); + if (renderer is SkinnedMeshRenderer smr) + { + Skinned = true; + Mesh = smr.sharedMesh; + } + else if (renderer is MeshRenderer mr) + { + var filter = mr.GetComponent(); + if (filter != null) + { + Mesh = filter.sharedMesh; + } + } + else + { + throw new NotImplementedException(); + } + + if (Mesh == null) + { + Summary = "no mesh"; + } + + VertexColor = VertexColorUtility.DetectVertexColor(Mesh, Materials); + + PushRenderer(renderer); + } + + void PushRenderer(Renderer renderer) + { + if (renderer is SkinnedMeshRenderer smr) + { + if (smr.bones != null && smr.bones.Length > 0) + { + var uniqueBones = smr.bones.Distinct().ToArray(); + var jointIndexMap = new int[smr.bones.Length]; + var bones = smr.bones; + for (int i = 0; i < bones.Length; ++i) + { + jointIndexMap[i] = Array.IndexOf(uniqueBones, bones[i]); + } + _renderers.Add((smr, uniqueBones)); + + if (JointIndexMap != null) + { + if (!JointIndexMap.SequenceEqual(jointIndexMap)) + { + // edge case + throw new NotImplementedException("different jointIndexMap is not supported"); + } + } + JointIndexMap = jointIndexMap; + } + else + { + // maybe blendshape only SkinnedMeshRenderer + _renderers.Add((smr, null)); + } + } + else if (renderer is MeshRenderer mr) + { + _renderers.Add((mr, null)); + } + else + { + throw new NotImplementedException(); + } + } + + public static MeshExportInfo Create(GameObject go) + { + var list = new List(); + GetInfo(go.transform.Traverse(), list, MeshExportSettings.Default); + return list[0]; + } + + /// + /// ヒエラルキーからエクスポートする Mesh の情報を収集する + /// + /// + /// + /// + /// blendShape の export を filtering する + public static void GetInfo(IEnumerable nodes, List list, MeshExportSettings settings) + { + list.Clear(); + foreach (var node in nodes) + { + var renderer = node.GetComponent(); + if (renderer == null) + { + continue; + } + + var found = list.FirstOrDefault(x => x.IsSameMeshAndMaterials(renderer)); + if (found != null) + { + found.PushRenderer(renderer); + continue; + } + + var info = new MeshExportInfo(renderer, settings); + if (info.Mesh != null) + { + list.Add(info); + } + } + } + + static bool TryGetMeshInfo() + { + + return true; + } + + + public void CalcMeshSize( + GameObject root, + Renderer renderer, + MeshExportSettings settings, + IBlendShapeExportFilter blendShapeFilter + ) + { + var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(renderer.transform, root.transform); + CalcMeshSize(relativePath, settings, blendShapeFilter); + } + + public void CalcMeshSize( + string relativePath, + MeshExportSettings settings, + IBlendShapeExportFilter blendShapeFilter + ) + { + var sb = new StringBuilder(); + if (!IsRendererActive) + { + sb.Append("[NotActive]"); + } + + if (Mesh == null) + { + sb.Append("[NoMesh]"); + } + else + { + + VertexCount = Mesh.vertexCount; + ExportVertexSize = 0; + TotalBlendShapeCount = 0; + ExportBlendShapeCount = 0; + + // float4 x 3 + // vertices + sb.Append($"(Pos"); + if (HasNormal) + { + sb.Append("+Nom"); + ExportVertexSize += 4 * 3; + } + if (HasUV) + { + sb.Append("+UV"); + ExportVertexSize += 4 * 2; + } + if (HasVertexColor) + { + sb.Append("+Col"); + ExportVertexSize += 4 * 4; + } + if (HasSkinning) + { + // short, float x 4 weights + sb.Append("+Skin"); + ExportVertexSize += (2 + 4) * 4; + } + // indices + IndexCount = Mesh.triangles.Length; + + // postion + normal ?. always tangent is ignored + TotalBlendShapeCount = Mesh.blendShapeCount; + ExportBlendShapeVertexSize = settings.ExportOnlyBlendShapePosition ? 4 * 3 : 4 * (3 + 3); + for (var i = 0; i < Mesh.blendShapeCount; ++i) + { + if (!blendShapeFilter.UseBlendShape(i, relativePath)) + { + continue; + } + + ++ExportBlendShapeCount; + } + + if (ExportBlendShapeCount > 0) + { + sb.Append($"+Morph x {ExportBlendShapeCount}"); + } + sb.Append($") x {Mesh.vertexCount}"); + switch (VertexColor) + { + case VertexColorState.ExistsAndIsUsed: + case VertexColorState.ExistsAndMixed: // エクスポートする + sb.Insert(0, "[use vcolor]"); + break; + case VertexColorState.ExistsButNotUsed: + sb.Insert(0, "[remove vcolor]"); + break; + } + if (ExportBlendShapeCount > 0 && !HasSkinning) + { + sb.Insert(0, "[morph without skin]"); + } + + // total bytes + sb.Insert(0, $"{ExportByteSize:#,0} Bytes = "); + } + Summary = sb.ToString(); + } } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs index 22b72471d..f0a8159cf 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs @@ -8,7 +8,7 @@ namespace UniGLTF public static class MeshExporter_DividedVertexBuffer { public static (glTFMesh, Dictionary) Export(glTF gltf, int bufferIndex, - MeshWithRenderer unityMesh, List unityMaterials, + MeshExportInfo unityMesh, List unityMaterials, IAxisInverter axisInverter, MeshExportSettings settings) { var mesh = unityMesh.Mesh; @@ -58,7 +58,7 @@ namespace UniGLTF } } - var material = unityMesh.Renderer.sharedMaterials[i]; + var material = unityMesh.Materials[i]; var materialIndex = -1; if (material != null) { diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs index fc7c1fa8b..23aefbca8 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs @@ -24,11 +24,11 @@ namespace UniGLTF /// /// public static (glTFMesh, Dictionary blendShapeIndexMap) Export(glTF gltf, int bufferIndex, - MeshWithRenderer unityMesh, List unityMaterials, + MeshExportInfo unityMesh, List unityMaterials, IAxisInverter axisInverter, MeshExportSettings settings) { var mesh = unityMesh.Mesh; - var materials = unityMesh.Renderer.sharedMaterials; + var materials = unityMesh.Materials; 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(); @@ -135,7 +135,7 @@ namespace UniGLTF if (j >= materials.Length) { - Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Renderer.name); + Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Mesh.name); break; } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs deleted file mode 100644 index ad7190766..000000000 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -namespace UniGLTF -{ - public struct MeshWithRenderer - { - public readonly Mesh Mesh; - public readonly Renderer Renderer; - public readonly Transform[] UniqueBones; - readonly int[] JointIndexMap; - - public MeshWithRenderer(Transform x) - { - Mesh = x.GetSharedMesh(); - Renderer = x.GetComponent(); - - if (Renderer is SkinnedMeshRenderer skin && skin.bones != null && skin.bones.Length > 0) - { - // has joints - var uniqueBones = skin.bones.Distinct().ToArray(); - UniqueBones = uniqueBones; - JointIndexMap = new int[skin.bones.Length]; - - var bones = skin.bones; - for (int i = 0; i < bones.Length; ++i) - { - JointIndexMap[i] = Array.IndexOf(uniqueBones, bones[i]); - } - } - else - { - UniqueBones = null; - JointIndexMap = null; - } - } - - /// - /// glTF は skinning の boneList の重複を許可しない - /// (unity は ok) - /// - /// - /// - public int GetJointIndex(int index) - { - if (index < 0) - { - return index; - } - - if (JointIndexMap != null) - { - return JointIndexMap[index]; - } - else - { - return index; - } - } - - public IEnumerable GetBindPoses() - { - var used = new HashSet(); - for (int i = 0; i < JointIndexMap.Length; ++i) - { - var index = JointIndexMap[i]; - if (used.Add(index)) - { - yield return Mesh.bindposes[i]; - } - } - } - - public static IEnumerable FromNodes(IEnumerable nodes) - { - foreach (var node in nodes) - { - var x = new MeshWithRenderer(node); - if (x.Mesh == null) - { - continue; ; - } - if (x.Renderer == null - || x.Renderer.sharedMaterials == null - || x.Renderer.sharedMaterials.Length == 0) - { - continue; - } - - yield return x; - } - } - - public bool IsSameMeshAndMaterials(MeshWithRenderer other) - { - return IsSameMeshAndMaterials(other.Mesh, other.Renderer.sharedMaterials); - } - - public bool IsSameMeshAndMaterials(Mesh mesh, Material[] materials) - { - if (Mesh != mesh) return false; - if (Renderer.sharedMaterials.Length != materials.Length) return false; - for (var i = 0; i < Renderer.sharedMaterials.Length; i++) - { - if (Renderer.sharedMaterials[i] != materials[i]) return false; - } - return true; - } - } -} diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs.meta deleted file mode 100644 index 0472adb35..000000000 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithRenderer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ed51e6c1221d3b744a914e977e875122 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs index 54a9a2707..dc4cb567d 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/gltfExporter.cs @@ -138,7 +138,7 @@ namespace UniGLTF } #region Export - static glTFNode ExportNode(Transform x, List nodes, List meshWithRenderers, List skins) + static glTFNode ExportNode(Transform x, List nodes, List meshWithRenderers, List skins) { var node = new glTFNode { @@ -160,7 +160,7 @@ namespace UniGLTF { var mesh = meshFilter.sharedMesh; var materials = meshRenderer.sharedMaterials; - if (TryGetSameMeshIndex(meshWithRenderers, mesh, materials, out int meshIndex)) + if (MeshExportInfo.TryGetSameMeshIndex(meshWithRenderers, mesh, materials, out int meshIndex)) { node.mesh = meshIndex; } @@ -182,7 +182,7 @@ namespace UniGLTF { var mesh = skinnedMeshRenderer.sharedMesh; var materials = skinnedMeshRenderer.sharedMaterials; - if (TryGetSameMeshIndex(meshWithRenderers, mesh, materials, out int meshIndex)) + if (MeshExportInfo.TryGetSameMeshIndex(meshWithRenderers, mesh, materials, out int meshIndex)) { node.mesh = meshIndex; node.skin = skins.IndexOf(skinnedMeshRenderer); @@ -203,21 +203,6 @@ namespace UniGLTF return node; } - private static bool TryGetSameMeshIndex(List meshWithRenderers, Mesh mesh, Material[] materials, out int meshIndex) - { - for (var i = 0; i < meshWithRenderers.Count; i++) - { - if (meshWithRenderers[i].IsSameMeshAndMaterials(mesh, materials)) - { - meshIndex = i; - return true; - } - } - - meshIndex = -1; - return false; - } - public virtual void ExportExtensions(ITextureSerializer textureSerializer) { // do nothing @@ -232,15 +217,11 @@ namespace UniGLTF .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(); - foreach (var um in unityMeshes) - { - if (!uniqueUnityMeshes.Any(x => x.IsSameMeshAndMaterials(um))) uniqueUnityMeshes.Add(um); - } + var uniqueUnityMeshes = new List(); + MeshExportInfo.GetInfo(Nodes, uniqueUnityMeshes, meshExportSettings); #region Materials and Textures - Materials = uniqueUnityMeshes.SelectMany(x => x.Renderer.sharedMaterials).Where(x => x != null).Distinct().ToList(); + Materials = uniqueUnityMeshes.SelectMany(x => x.Materials).Where(x => x != null).Distinct().ToList(); TextureExporter = new TextureExporter(textureSerializer); @@ -267,10 +248,17 @@ namespace UniGLTF #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(); + var skins = uniqueUnityMeshes + .SelectMany(x => x.Renderers) + .Where(x => x.Item1 is SkinnedMeshRenderer && x.UniqueBones != null) + .Select(x => x.Item1 as SkinnedMeshRenderer) + .ToList() + ; + foreach (var node in Nodes) + { + var gltfNode = ExportNode(node, Nodes, uniqueUnityMeshes, skins); + glTF.nodes.Add(gltfNode); + } glTF.scenes = new List { new gltfScene @@ -279,26 +267,30 @@ namespace UniGLTF } }; - foreach (var x in unitySkins) + foreach (var x in uniqueUnityMeshes) { - 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 + foreach (var (renderer, uniqueBones) in x.Renderers) { - 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); + if (uniqueBones != null && renderer is SkinnedMeshRenderer smr) + { + var matrices = x.GetBindPoses().Select(m_axisInverter.InvertMat4).ToArray(); + var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE); + var skin = new glTFSkin + { + inverseBindMatrices = accessor, + joints = uniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(), + skeleton = Nodes.IndexOf(smr.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; + foreach (var z in Nodes.Where(y => y.Has(renderer))) + { + var nodeIndex = Nodes.IndexOf(z); + var node = glTF.nodes[nodeIndex]; + node.skin = skinIndex; + } + } } } #endregion diff --git a/Assets/UniGLTF/Tests/UniGLTF/MeshExportValidatorTests.cs b/Assets/UniGLTF/Tests/UniGLTF/MeshExportValidatorTests.cs index 803437b4d..aedf44fc7 100644 --- a/Assets/UniGLTF/Tests/UniGLTF/MeshExportValidatorTests.cs +++ b/Assets/UniGLTF/Tests/UniGLTF/MeshExportValidatorTests.cs @@ -26,7 +26,7 @@ namespace UniGLTF try { - validator.SetRoot(root, MeshExportSettings.Default); + validator.SetRoot(root, MeshExportSettings.Default, new DefualtBlendShapeExportFilter()); var vs = validator.Validate(root); Assert.False(vs.All(x => x.CanExport)); } @@ -46,7 +46,7 @@ namespace UniGLTF try { - validator.SetRoot(root, MeshExportSettings.Default); + validator.SetRoot(root, MeshExportSettings.Default, new DefualtBlendShapeExportFilter()); var vs = validator.Validate(root); Assert.False(vs.All(x => x.CanExport)); } @@ -66,7 +66,7 @@ namespace UniGLTF try { - validator.SetRoot(root, MeshExportSettings.Default); + validator.SetRoot(root, MeshExportSettings.Default, new DefualtBlendShapeExportFilter()); var vs = validator.Validate(root); Assert.False(vs.All(x => x.CanExport)); } @@ -90,7 +90,7 @@ namespace UniGLTF // remove MeshFilter Component.DestroyImmediate(child.GetComponent()); - validator.SetRoot(root, MeshExportSettings.Default); + validator.SetRoot(root, MeshExportSettings.Default, new DefualtBlendShapeExportFilter()); var vs = validator.Validate(root); Assert.True(vs.All(x => x.CanExport)); } @@ -114,7 +114,7 @@ namespace UniGLTF // set null child.GetComponent().sharedMesh = null; - validator.SetRoot(root, MeshExportSettings.Default); + validator.SetRoot(root, MeshExportSettings.Default, new DefualtBlendShapeExportFilter()); var vs = validator.Validate(root); Assert.True(vs.All(x => x.CanExport)); } diff --git a/Assets/UniGLTF/Tests/UniGLTF/MeshTests.cs b/Assets/UniGLTF/Tests/UniGLTF/MeshTests.cs index c9a4aa3ea..1ef275bd5 100644 --- a/Assets/UniGLTF/Tests/UniGLTF/MeshTests.cs +++ b/Assets/UniGLTF/Tests/UniGLTF/MeshTests.cs @@ -134,7 +134,7 @@ namespace UniGLTF }; var axisInverter = Axes.X.Create(); - var unityMesh = new MeshWithRenderer(go.transform); + var unityMesh = MeshExportInfo.Create(go); var (gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer ? MeshExporter_DividedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings) : MeshExporter_SharedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials,axisInverter, meshExportSettings) @@ -182,7 +182,7 @@ namespace UniGLTF }; var axisInverter = Axes.X.Create(); - var unityMesh = new MeshWithRenderer(go.transform); + var unityMesh = MeshExportInfo.Create(go); var (gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer ? MeshExporter_DividedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings) : MeshExporter_SharedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials,axisInverter, meshExportSettings) diff --git a/Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs b/Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs similarity index 74% rename from Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs rename to Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs index 9797057a9..d04f087f0 100644 --- a/Assets/VRM/Editor/Format/VRMMeshExportValidaotr.cs +++ b/Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs @@ -5,14 +5,7 @@ using UnityEngine; namespace VRM { - /// - /// Export時にMeshを一覧する。 - /// - /// Mesh関連の Validation する。 - /// Meshのエクスポートサイズを試算する。 - /// - [Serializable] - public class VRMMeshExportValidator : MeshExportValidator + public class VRMBlendShapeExportFilter : IBlendShapeExportFilter { static bool ClipsContainsName(IReadOnlyList clips, bool onlyPreset, BlendShapeBinding binding) { @@ -40,7 +33,24 @@ namespace VRM public VRMExportSettings VRMExportSettings; public List Clips; - public override bool UseBlendShape(int index, string relativePath) + public VRMBlendShapeExportFilter(GameObject exportRoot, VRMExportSettings settings) + { + VRMExportSettings = settings; + Clips = new List(); + if (exportRoot != null) + { + var proxy = exportRoot.GetComponent(); + if (proxy != null) + { + if (proxy.BlendShapeAvatar != null) + { + Clips.AddRange(proxy.BlendShapeAvatar.Clips); + } + } + } + } + + public bool UseBlendShape(int index, string relativePath) { if (VRMExportSettings.ReduceBlendshape) { @@ -57,24 +67,5 @@ namespace VRM return true; } - - public void SetRoot(GameObject ExportRoot, VRMExportSettings settings) - { - VRMExportSettings = settings; - Clips = new List(); - if (ExportRoot != null) - { - var proxy = ExportRoot.GetComponent(); - if (proxy != null) - { - if (proxy.BlendShapeAvatar != null) - { - Clips.AddRange(proxy.BlendShapeAvatar.Clips); - } - } - } - - SetRoot(ExportRoot, settings.MeshExportSettings); - } } } diff --git a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs.meta b/Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs.meta similarity index 83% rename from Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs.meta rename to Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs.meta index 821ce7fec..a30a76ee8 100644 --- a/Assets/UniGLTF/Editor/UniGLTF/ExportDialog/ExporterExtensions.cs.meta +++ b/Assets/VRM/Editor/Format/VRMBlendShapeExportFilter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f76ebfda1e249cb48890de41cc221889 +guid: d634cf68918e2f947b7ea0288d52a720 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/VRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/Editor/Format/VRMExporterWizard.cs index 51cc1fd15..2861af0c6 100644 --- a/Assets/VRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/Editor/Format/VRMExporterWizard.cs @@ -36,7 +36,7 @@ namespace VRM Editor m_settingsInspector; - VRMMeshExportValidator m_meshes; + MeshExportValidator m_meshes; Editor m_meshesInspector; @@ -69,7 +69,7 @@ namespace VRM m_settings = ScriptableObject.CreateInstance(); m_settingsInspector = Editor.CreateEditor(m_settings); - m_meshes = ScriptableObject.CreateInstance(); + m_meshes = ScriptableObject.CreateInstance(); m_meshesInspector = Editor.CreateEditor(m_meshes); State.ExportRootChanged += (root) => @@ -179,7 +179,7 @@ namespace VRM protected override void OnLayout() { - m_meshes.SetRoot(State.ExportRoot, m_settings); + m_meshes.SetRoot(State.ExportRoot, m_settings.MeshExportSettings, new VRMBlendShapeExportFilter(State.ExportRoot, m_settings)); } static bool s_foldT = true; @@ -259,7 +259,7 @@ namespace VRM switch (meshInfo.VertexColor) { case UniGLTF.VertexColorState.ExistsAndMixed: - Validation.Warning($"{meshInfo.Renderer}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI(); + Validation.Warning($"{meshInfo.Renderers}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI(); break; } } diff --git a/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs b/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs deleted file mode 100644 index c96a9711a..000000000 --- a/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UniGLTF; -using UnityEditor; - -namespace VRM -{ - [CustomEditor(typeof(VRMMeshExportValidator))] - public class VRMExportMeshesEditor : MeshExportValidatorEditor - { - } -} diff --git a/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs.meta b/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs.meta deleted file mode 100644 index 7fa901c84..000000000 --- a/Assets/VRM/Editor/Format/VRMMeshExportValidatorEditor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e590565ae8d8e5f45956a20e207d3272 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/VRM/Runtime/VRM.asmdef b/Assets/VRM/Runtime/VRM.asmdef index 828486611..d5bac528b 100644 --- a/Assets/VRM/Runtime/VRM.asmdef +++ b/Assets/VRM/Runtime/VRM.asmdef @@ -2,19 +2,17 @@ "name": "VRM", "references": [ "UniHumanoid", - "MeshUtility", "UniGLTF", "VRMShaders.GLTF.IO.Runtime", "VRMShaders.VRM.IO.Runtime", "MToon" ], + "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false + "defineConstraints": [] } \ No newline at end of file diff --git a/Assets/VRM10/Editor/Vrm10ExportDialog.cs b/Assets/VRM10/Editor/Vrm10ExportDialog.cs index 9818a4a86..37d18b337 100644 --- a/Assets/VRM10/Editor/Vrm10ExportDialog.cs +++ b/Assets/VRM10/Editor/Vrm10ExportDialog.cs @@ -168,7 +168,7 @@ namespace UniVRM10 protected override void OnLayout() { // m_settings, m_meshes.Meshes - m_meshes.SetRoot(State.ExportRoot, m_settings.MeshExportSettings); + m_meshes.SetRoot(State.ExportRoot, m_settings.MeshExportSettings, new DefualtBlendShapeExportFilter()); } protected override bool DoGUI(bool isValid)