diff options
Diffstat (limited to 'Assets/ThirdParty/VRM/MeshUtility/Runtime')
24 files changed, 2700 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs new file mode 100644 index 00000000..23e233e5 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs @@ -0,0 +1,158 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +// using UniGLTF; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace MeshUtility +{ + [DisallowMultipleComponent] + public class BindposeGizmo : MonoBehaviour + { + [SerializeField] + Mesh m_target; + + [SerializeField] + float[] m_boneWeights; + + [SerializeField, Range(0.1f, 1.0f)] + float m_gizmoSize = 0.02f; + + [SerializeField] + Color m_meshGizmoColor = Color.yellow; + + [SerializeField] + Color m_bindGizmoColor = Color.red; + + private void Reset() + { + var renderer = GetComponent<SkinnedMeshRenderer>(); + if (renderer == null) return; + m_target = renderer.sharedMesh; + } + +#if UNITY_EDITOR + #region ToBindpose + [ContextMenu("ToBindpose")] + void ToBindpose() + { + var renderer = GetComponent<SkinnedMeshRenderer>(); + + var root = + renderer.bones + .Select(x => Ancestors(x).Reverse().ToArray()) + .Aggregate((a, b) => + { + int i = 0; + for (; i < a.Length && i < b.Length; ++i) + { + if (a[i] != b[i]) + { + break; + } + } + return a.Take(i).ToArray(); + }) + .Last() + ; + + var map = new Dictionary<Transform, Matrix4x4>(); + for (int i = 0; i < renderer.bones.Length; ++i) + { + map[renderer.bones[i]] = m_target.bindposes[i]; + } + + { + var bones = Traverse(root); + Undo.RecordObjects(bones.ToArray(), "toBindpose"); + + foreach (var x in bones) + { + var bind = default(Matrix4x4); + if (map.TryGetValue(x, out bind)) + { + var toWorld = renderer.transform.localToWorldMatrix * bind.inverse; + x.position = toWorld.GetColumn(3); + x.rotation = toWorld.ExtractRotation(); + } + } + + //EditorUtility.SetDirty(transform); + } + } + + IEnumerable<Transform> Traverse(Transform self) + { + yield return self; + + foreach (Transform child in self) + { + foreach (var x in Traverse(child)) + { + yield return x; + } + } + } + + IEnumerable<Transform> Ancestors(Transform self) + { + yield return self; + + if (self.parent != null) + { + foreach (var x in Ancestors(self.parent)) + { + yield return x; + } + } + } + #endregion +#endif + + private void OnDrawGizmos() + { + if (m_target == null) + { + return; + } + + Gizmos.matrix = transform.localToWorldMatrix; + + if (m_target.bindposes != null && m_target.bindposes.Length > 0) + { + if (m_boneWeights == null || m_boneWeights.Length != m_target.bindposes.Length) + { + m_boneWeights = new float[m_target.bindposes.Length]; + foreach (var bw in m_target.boneWeights) + { + if (bw.weight0 > 0) m_boneWeights[bw.boneIndex0] += bw.weight0; + if (bw.weight1 > 0) m_boneWeights[bw.boneIndex1] += bw.weight1; + if (bw.weight2 > 0) m_boneWeights[bw.boneIndex2] += bw.weight2; + if (bw.weight3 > 0) m_boneWeights[bw.boneIndex3] += bw.weight3; + } + } + + Gizmos.color = m_meshGizmoColor; + Gizmos.DrawWireMesh(m_target); + + for (var i = 0; i < m_target.bindposes.Length; ++i) + { + var color = m_bindGizmoColor * m_boneWeights[i]; + color.a = 1.0f; + Gizmos.color = color; + + Gizmos.matrix = transform.localToWorldMatrix * m_target.bindposes[i].inverse; + Gizmos.DrawWireCube(Vector3.zero, Vector3.one * m_gizmoSize); + } + } + else + { + Gizmos.color = Color.gray; + Gizmos.DrawWireMesh(m_target); + } + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs.meta new file mode 100644 index 00000000..1567b1e4 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BindposeGizmo.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 3ada4f7077a352343aed57e71a58885d +timeCreated: 1518245944 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs new file mode 100644 index 00000000..64ee850d --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + + +namespace MeshUtility +{ + public static class BoneMeshEraser + { + private struct ExcludeBoneIndex + { + public readonly bool Bone0; + public readonly bool Bone1; + public readonly bool Bone2; + public readonly bool Bone3; + + public ExcludeBoneIndex(bool bone0, bool bone1, bool bone2, bool bone3) + { + Bone0 = bone0; + Bone1 = bone1; + Bone2 = bone2; + Bone3 = bone3; + } + } + + [Serializable] + public struct EraseBone + { + public Transform Bone; + public bool Erase; + + public override string ToString() + { + return Bone.name + ":" + Erase; + } + } + + static int ExcludeTriangles(int[] triangles, BoneWeight[] bws, int[] exclude) + { + int count = 0; + if (bws != null && bws.Length > 0) + { + for (int i = 0; i < triangles.Length; i += 3) + { + var a = triangles[i]; + var b = triangles[i + 1]; + var c = triangles[i + 2]; + + { + var bw = bws[a]; + var eb = AreBoneContains(ref exclude, bw.boneIndex0, bw.boneIndex1, bw.boneIndex2, bw.boneIndex3); + if (bw.weight0 > 0 && eb.Bone0) continue; + if (bw.weight1 > 0 && eb.Bone1) continue; + if (bw.weight2 > 0 && eb.Bone2) continue; + if (bw.weight3 > 0 && eb.Bone3) continue; + } + { + var bw = bws[b]; + var eb = AreBoneContains(ref exclude, bw.boneIndex0, bw.boneIndex1, bw.boneIndex2, bw.boneIndex3); + if (bw.weight0 > 0 && eb.Bone0) continue; + if (bw.weight1 > 0 && eb.Bone1) continue; + if (bw.weight2 > 0 && eb.Bone2) continue; + if (bw.weight3 > 0 && eb.Bone3) continue; + } + { + var bw = bws[c]; + var eb = AreBoneContains(ref exclude, bw.boneIndex0, bw.boneIndex1, bw.boneIndex2, bw.boneIndex3); + if (bw.weight0 > 0 && eb.Bone0) continue; + if (bw.weight1 > 0 && eb.Bone1) continue; + if (bw.weight2 > 0 && eb.Bone2) continue; + if (bw.weight3 > 0 && eb.Bone3) continue; + } + + triangles[count++] = a; + triangles[count++] = b; + triangles[count++] = c; + } + } + + return count; + } + + private static ExcludeBoneIndex AreBoneContains(ref int[] exclude, int boneIndex0, int boneIndex1, + int boneIndex2, int boneIndex3) + { + var b0 = false; + var b1 = false; + var b2 = false; + var b3 = false; + for (int i = 0; i < exclude.Length; i++) + { + if (exclude[i] == boneIndex0) + { + b0 = true; + continue; + } + + if (exclude[i] == boneIndex1) + { + b1 = true; + continue; + } + + if (exclude[i] == boneIndex2) + { + b2 = true; + continue; + } + + if (exclude[i] == boneIndex3) + { + b3 = true; + } + } + + return new ExcludeBoneIndex(b0, b1, b2, b3); + } + + public static Mesh CreateErasedMesh(Mesh src, int[] eraseBoneIndices) + { + /* + Debug.LogFormat("{0} exclude: {1}", + src.name, + String.Join(", ", eraseBoneIndices.Select(x => x.ToString()).ToArray()) + ); + */ + var mesh = new Mesh(); + mesh.name = src.name + "(erased)"; + +#if UNITY_2017_3_OR_NEWER + mesh.indexFormat = src.indexFormat; +#endif + + mesh.vertices = src.vertices; + mesh.normals = src.normals; + mesh.uv = src.uv; + mesh.tangents = src.tangents; + mesh.boneWeights = src.boneWeights; + mesh.bindposes = src.bindposes; + mesh.subMeshCount = src.subMeshCount; + for (int i = 0; i < src.subMeshCount; ++i) + { + var indices = src.GetIndices(i); + var count = ExcludeTriangles(indices, mesh.boneWeights, eraseBoneIndices); + var dst = new int[count]; + Array.Copy(indices, 0, dst, 0, count); + mesh.SetIndices(dst, MeshTopology.Triangles, i); + } + + return mesh; + } + + public static IEnumerable<Transform> Ancestor(this Transform t) + { + yield return t; + + if (t.parent != null) + { + foreach (var x in Ancestor(t.parent)) + { + yield return x; + } + } + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs.meta new file mode 100644 index 00000000..db633e87 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneMeshEraser.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 270bf7461f6f8d546ae540cb7d5fc6f3 +timeCreated: 1519018975 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs new file mode 100644 index 00000000..b5f5231c --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs @@ -0,0 +1,507 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace MeshUtility +{ + public static class BoneNormalizer + { + public delegate Avatar CreateAvatarFunc(GameObject original, GameObject normalized, Dictionary<Transform, Transform> boneMap); + + static (GameObject, Dictionary<Transform, Transform>) NormalizeHierarchy(GameObject go, CreateAvatarFunc createAvatar) + { + var boneMap = new Dictionary<Transform, Transform>(); + + // + // 回転・スケールの無いヒエラルキーをコピーする + // + var normalized = new GameObject(go.name + "(normalized)"); + normalized.transform.position = go.transform.position; + CopyAndBuild(go.transform, normalized.transform, boneMap); + + // + // 新しいヒエラルキーからAvatarを作る + // + { + var animator = normalized.AddComponent<Animator>(); + var avatar = createAvatar(go, normalized, boneMap); + avatar.name = go.name + ".normalized"; + animator.avatar = avatar; + } + + return (normalized, boneMap); + } + + /// <summary> + /// 回転とスケールを除去したヒエラルキーをコピーする。 + /// </summary> + /// <param name="src"></param> + /// <param name="dst"></param> + static void CopyAndBuild(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap) + { + boneMap[src] = dst; + + foreach (Transform child in src) + { + if (child.gameObject.activeSelf) + { + var dstChild = new GameObject(child.name); + dstChild.transform.SetParent(dst); + dstChild.transform.position = child.position; // copy position only + + CopyAndBuild(child, dstChild.transform, boneMap); + } + } + } + + class BlendShapeReport + { + string m_name; + int m_count; + struct BlendShapeStat + { + public int Index; + public string Name; + public int VertexCount; + public int NormalCount; + public int TangentCount; + + public override string ToString() + { + return string.Format("[{0}]{1}: {2}, {3}, {4}\n", Index, Name, VertexCount, NormalCount, TangentCount); + } + } + List<BlendShapeStat> m_stats = new List<BlendShapeStat>(); + public int Count + { + get { return m_stats.Count; } + } + public BlendShapeReport(Mesh mesh) + { + m_name = mesh.name; + m_count = mesh.vertexCount; + } + public void SetCount(int index, string name, int v, int n, int t) + { + m_stats.Add(new BlendShapeStat + { + Index = index, + Name = name, + VertexCount = v, + NormalCount = n, + TangentCount = t, + }); + } + public override string ToString() + { + return String.Format("NormalizeSkinnedMesh: {0}({1}verts)\n{2}", + m_name, + m_count, + String.Join("", m_stats.Select(x => x.ToString()).ToArray())); + } + } + + /// <summary> + /// index が 有効であれば、setter に weight を渡す。無効であれば setter に 0 を渡す。 + /// </summary> + /// <param name="indexMap"></param> + /// <param name="srcIndex"></param> + /// <param name="weight"></param> + /// <param name="setter"></param> + static bool CopyOrDropWeight(int[] indexMap, int srcIndex, float weight, Action<int, float> setter) + { + if (srcIndex < 0 || srcIndex >= indexMap.Length) + { + // ありえるかどうかわからないが BoneWeight.boneIndexN に変な値が入っている. + setter(0, 0); + return false; + } + + var dstIndex = indexMap[srcIndex]; + if (dstIndex != -1) + { + // 有効なindex。weightをコピーする + setter(dstIndex, weight); + return true; + } + else + { + // 無効なindex。0でクリアする + setter(0, 0); + return false; + } + } + + /// <summary> + /// BoneWeight[] src から新しいボーンウェイトを作成する。 + /// </summary> + /// <param name="src">変更前のBoneWeight[]</param> + /// <param name="boneMap">新旧のボーンの対応表。新しい方は無効なボーンが除去されてnullの部分がある</param> + /// <param name="srcBones">変更前のボーン配列</param> + /// <param name="dstBones">変更後のボーン配列。除去されたボーンがある場合、変更前より短い</param> + /// <returns></returns> + public static BoneWeight[] MapBoneWeight(BoneWeight[] src, + Dictionary<Transform, Transform> boneMap, + Transform[] srcBones, + Transform[] dstBones + ) + { + // 処理前後の index の対応表を作成する + var indexMap = new int[srcBones.Length]; + for (int i = 0; i < srcBones.Length; ++i) + { + var srcBone = srcBones[i]; + if (srcBone == null) + { + // 元のボーンが無い + indexMap[i] = -1; + Debug.LogWarningFormat("bones[{0}] is null", i); + } + else + { + if (boneMap.TryGetValue(srcBone, out Transform dstBone)) + { + // 対応するボーンが存在する + var dstIndex = Array.IndexOf(dstBones, dstBone); + if (dstIndex == -1) + { + // ありえない。バグ + throw new Exception(); + } + indexMap[i] = dstIndex; + } + else + { + // 先のボーンが無い + indexMap[i] = -1; + Debug.LogWarningFormat("{0} is removed", srcBone.name); + } + } + } + + // 新しいBoneWeightを作成する + var newBoneWeights = new BoneWeight[src.Length]; + for (int i = 0; i < src.Length; ++i) + { + BoneWeight srcBoneWeight = src[i]; + + // 0 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex0, srcBoneWeight.weight0, (newIndex, newWeight) => + { + newBoneWeights[i].boneIndex0 = newIndex; + newBoneWeights[i].weight0 = newWeight; + }); + // 1 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex1, srcBoneWeight.weight1, (newIndex, newWeight) => + { + newBoneWeights[i].boneIndex1 = newIndex; + newBoneWeights[i].weight1 = newWeight; + }); + // 2 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex2, srcBoneWeight.weight2, (newIndex, newWeight) => + { + newBoneWeights[i].boneIndex2 = newIndex; + newBoneWeights[i].weight2 = newWeight; + }); + // 3 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex3, srcBoneWeight.weight3, (newIndex, newWeight) => + { + newBoneWeights[i].boneIndex3 = newIndex; + newBoneWeights[i].weight3 = newWeight; + }); + } + + return newBoneWeights; + } + + /// <summary> + /// srcのSkinnedMeshRendererを正規化して、dstにアタッチする + /// </summary> + /// <param name="src">正規化前のSkinnedMeshRendererのTransform</param> + /// <param name="dst">正規化後のSkinnedMeshRendererのTransform</param> + /// <param name="boneMap">正規化前のボーンから正規化後のボーンを得る</param> + static void NormalizeSkinnedMesh(Transform src, Transform dst, Dictionary<Transform, Transform> boneMap, bool clearBlendShape) + { + var srcRenderer = src.GetComponent<SkinnedMeshRenderer>(); + if (srcRenderer == null + || !srcRenderer.enabled + || srcRenderer.sharedMesh == null + || srcRenderer.sharedMesh.vertexCount == 0) + { + // 有効なSkinnedMeshRendererが無かった + return; + } + + var srcMesh = srcRenderer.sharedMesh; + var originalSrcMesh = srcMesh; + + // clear blendShape + if (clearBlendShape) + { + for (int i = 0; i < srcMesh.blendShapeCount; ++i) + { + srcRenderer.SetBlendShapeWeight(i, 0); + } + } + + // 元の Transform[] bones から、無効なboneを取り除いて前に詰めた配列を作る + var dstBones = srcRenderer.bones + .Where(x => x != null && boneMap.ContainsKey(x)) + .Select(x => boneMap[x]) + .ToArray(); + + var hasBoneWeight = srcRenderer.bones != null && srcRenderer.bones.Length > 0; + if (!hasBoneWeight) + { + // Before bake, bind no weight bones + //Debug.LogFormat("no weight: {0}", srcMesh.name); + + srcMesh = srcMesh.Copy(true); + var bw = new BoneWeight + { + boneIndex0 = 0, + boneIndex1 = 0, + boneIndex2 = 0, + boneIndex3 = 0, + weight0 = 1.0f, + weight1 = 0.0f, + weight2 = 0.0f, + weight3 = 0.0f, + }; + srcMesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray(); + srcMesh.bindposes = new Matrix4x4[] { Matrix4x4.identity }; + + srcRenderer.rootBone = srcRenderer.transform; + dstBones = new[] { boneMap[srcRenderer.transform] }; + srcRenderer.bones = new[] { srcRenderer.transform }; + srcRenderer.sharedMesh = srcMesh; + } + + // BakeMesh + var mesh = srcMesh.Copy(false); + mesh.name = srcMesh.name + ".baked"; + srcRenderer.BakeMesh(mesh); + + var blendShapeValues = new Dictionary<int, float>(); + for (int i = 0; i < srcMesh.blendShapeCount; i++) + { + var val = srcRenderer.GetBlendShapeWeight(i); + if (val > 0) blendShapeValues.Add(i, val); + } + + // 新しい骨格のボーンウェイトを作成する + mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, srcRenderer.bones, dstBones); + + // recalc bindposes + mesh.bindposes = dstBones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray(); + + //var m = src.localToWorldMatrix; // include scaling + var m = default(Matrix4x4); + m.SetTRS(Vector3.zero, src.rotation, Vector3.one); // rotation only + mesh.ApplyMatrix(m); + + // + // BlendShapes + // + var meshVertices = mesh.vertices; + var meshNormals = mesh.normals; +#if VRM_NORMALIZE_BLENDSHAPE_TANGENT + var meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray(); +#endif + + var originalBlendShapePositions = new Vector3[meshVertices.Length]; + var originalBlendShapeNormals = new Vector3[meshVertices.Length]; + var originalBlendShapeTangents = new Vector3[meshVertices.Length]; + + var report = new BlendShapeReport(srcMesh); + var blendShapeMesh = new Mesh(); + for (int i = 0; i < srcMesh.blendShapeCount; ++i) + { + // check blendShape + srcRenderer.sharedMesh.GetBlendShapeFrameVertices(i, 0, originalBlendShapePositions, originalBlendShapeNormals, originalBlendShapeTangents); + var hasVertices = originalBlendShapePositions.Count(x => x != Vector3.zero); + var hasNormals = originalBlendShapeNormals.Count(x => x != Vector3.zero); +#if VRM_NORMALIZE_BLENDSHAPE_TANGENT + var hasTangents = originalBlendShapeTangents.Count(x => x != Vector3.zero); +#else + var hasTangents = 0; +#endif + var name = srcMesh.GetBlendShapeName(i); + if (string.IsNullOrEmpty(name)) + { + name = String.Format("{0}", i); + } + + report.SetCount(i, name, hasVertices, hasNormals, hasTangents); + + srcRenderer.SetBlendShapeWeight(i, 100.0f); + srcRenderer.BakeMesh(blendShapeMesh); + if (blendShapeMesh.vertices.Length != mesh.vertices.Length) + { + throw new Exception("different vertex count"); + } + + var value = blendShapeValues.ContainsKey(i) ? blendShapeValues[i] : 0; + srcRenderer.SetBlendShapeWeight(i, value); + + Vector3[] vertices = blendShapeMesh.vertices; + + for (int j = 0; j < vertices.Length; ++j) + { + if (originalBlendShapePositions[j] == Vector3.zero) + { + vertices[j] = Vector3.zero; + } + else + { + vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j]; + } + } + + Vector3[] normals = blendShapeMesh.normals; + for (int j = 0; j < normals.Length; ++j) + { + if (originalBlendShapeNormals[j] == Vector3.zero) + { + normals[j] = Vector3.zero; + + } + else + { + normals[j] = m.MultiplyVector(normals[j]) - meshNormals[j]; + } + } + + Vector3[] tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray(); +#if VRM_NORMALIZE_BLENDSHAPE_TANGENT + for (int j = 0; j < tangents.Length; ++j) + { + if (originalBlendShapeTangents[j] == Vector3.zero) + { + tangents[j] = Vector3.zero; + } + else + { + tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j]; + } + } +#endif + + var frameCount = srcMesh.GetBlendShapeFrameCount(i); + for (int f = 0; f < frameCount; f++) + { + + var weight = srcMesh.GetBlendShapeFrameWeight(i, f); + + try + { + mesh.AddBlendShapeFrame(name, + weight, + vertices, + hasNormals > 0 ? normals : null, + hasTangents > 0 ? tangents : null + ); + } + catch (Exception) + { + Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}", + mesh.name, + srcMesh.GetBlendShapeName(i) + ); + throw; + } + } + } + + if (report.Count > 0) + { + Debug.LogFormat("{0}", report.ToString()); + } + + var dstRenderer = dst.gameObject.AddComponent<SkinnedMeshRenderer>(); + dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; + if (srcRenderer.rootBone != null) + { + dstRenderer.rootBone = boneMap[srcRenderer.rootBone]; + } + dstRenderer.bones = dstBones; + dstRenderer.sharedMesh = mesh; + + if (!hasBoneWeight) + { + // restore bones + srcRenderer.bones = new Transform[] { }; + srcRenderer.sharedMesh = originalSrcMesh; + } + } + + /// <summary> + /// + /// </summary> + /// <param name="src"></param> + /// <param name="dst"></param> + static void NormalizeNoneSkinnedMesh(Transform src, Transform dst) + { + var srcFilter = src.GetComponent<MeshFilter>(); + if (srcFilter == null + || srcFilter.sharedMesh == null + || srcFilter.sharedMesh.vertexCount == 0) + { + return; + } + + var srcRenderer = src.GetComponent<MeshRenderer>(); + if (srcRenderer == null || !srcRenderer.enabled) + { + return; + } + + // Meshに乗っているボーンの姿勢を適用する + var dstFilter = dst.gameObject.AddComponent<MeshFilter>(); + + var dstMesh = srcFilter.sharedMesh.Copy(false); + dstMesh.ApplyRotationAndScale(src.localToWorldMatrix); + dstFilter.sharedMesh = dstMesh; + + // Materialをコピー + var dstRenderer = dst.gameObject.AddComponent<MeshRenderer>(); + dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; + } + + /// <summary> + /// 回転とスケールを除去したヒエラルキーのコピーを作成する(MeshをBakeする) + /// </summary> + /// <param name="go">対象のヒエラルキーのルート</param> + /// <param name="clearBlendShapeBeforeNormalize">BlendShapeを0クリアするか否か。false の場合 BlendShape の現状を Bake する</param> + /// <param name="createAvatar">Avatarを作る関数</param> + /// <returns></returns> + public static (GameObject, Dictionary<Transform, Transform>) Execute(GameObject go, + bool clearBlendShapeBeforeNormalize, CreateAvatarFunc createAvatar) + { + // + // 正規化されたヒエラルキーを作る + // + var (normalized, boneMap) = NormalizeHierarchy(go, createAvatar); + + // + // 各メッシュから回転・スケールを取り除いてBinding行列を再計算する + // + foreach (var src in go.transform.Traverse()) + { + Transform dst; + if (!boneMap.TryGetValue(src, out dst)) + { + continue; + } + + NormalizeSkinnedMesh(src, dst, boneMap, clearBlendShapeBeforeNormalize); + + NormalizeNoneSkinnedMesh(src, dst); + } + + return (normalized, boneMap); + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs.meta new file mode 100644 index 00000000..4663d268 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b330ec419f98af14687c302638922ab0 +timeCreated: 1519379418 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs new file mode 100644 index 00000000..c6d61948 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs @@ -0,0 +1,441 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace MeshUtility +{ + /// <summary> + /// Bone割り当てを保持する。 + /// ヒエラルキーのルート(おそらくHipsの親)にアタッチする + /// </summary> + [DisallowMultipleComponent] + public class Humanoid : MonoBehaviour + { + [SerializeField] private Transform m_Hips; public Transform Hips => m_Hips; + + #region leg + [SerializeField] private Transform m_LeftUpperLeg; public Transform LeftUpperLeg => m_LeftUpperLeg; + [SerializeField] private Transform m_RightUpperLeg; public Transform RightUpperLeg => m_RightUpperLeg; + [SerializeField] private Transform m_LeftLowerLeg; public Transform LeftLowerLeg => m_LeftLowerLeg; + [SerializeField] private Transform m_RightLowerLeg; public Transform RightLowerLeg => m_RightLowerLeg; + [SerializeField] private Transform m_LeftFoot; public Transform LeftFoot => m_LeftFoot; + [SerializeField] private Transform m_RightFoot; public Transform RightFoot => m_RightFoot; + [SerializeField] private Transform m_LeftToes; public Transform LeftToes => m_LeftToes; + [SerializeField] private Transform m_RightToes; public Transform RightToes => m_RightToes; + #endregion + + #region spine + [SerializeField] private Transform m_Spine; public Transform Spine => m_Spine; + [SerializeField] private Transform m_Chest; public Transform Chest => m_Chest; + [SerializeField] private Transform m_UpperChest; public Transform UpperChest => m_UpperChest; + [SerializeField] private Transform m_Neck; public Transform Neck => m_Neck; + [SerializeField] private Transform m_Head; public Transform Head => m_Head; + [SerializeField] private Transform m_LeftEye; public Transform LeftEye => m_LeftEye; + [SerializeField] private Transform m_RightEye; public Transform RightEye => m_RightEye; + [SerializeField] private Transform m_Jaw; public Transform Jaw => m_Jaw; + #endregion + + #region arm + [SerializeField] private Transform m_LeftShoulder; public Transform LeftShoulder => m_LeftShoulder; + [SerializeField] private Transform m_RightShoulder; public Transform RightShoulder => m_RightShoulder; + [SerializeField] private Transform m_LeftUpperArm; public Transform LeftUpperArm => m_LeftUpperArm; + [SerializeField] private Transform m_RightUpperArm; public Transform RightUpperArm => m_RightUpperArm; + [SerializeField] private Transform m_LeftLowerArm; public Transform LeftLowerArm => m_LeftLowerArm; + [SerializeField] private Transform m_RightLowerArm; public Transform RightLowerArm => m_RightLowerArm; + [SerializeField] private Transform m_LeftHand; public Transform LeftHand => m_LeftHand; + [SerializeField] private Transform m_RightHand; public Transform RightHand => m_RightHand; + #endregion + + #region fingers + [SerializeField] private Transform m_LeftThumbProximal; public Transform LeftThumbProximal => m_LeftThumbProximal; + [SerializeField] private Transform m_LeftThumbIntermediate; public Transform LeftThumbIntermediate => m_LeftThumbIntermediate; + [SerializeField] private Transform m_LeftThumbDistal; public Transform LeftThumbDistal => m_LeftThumbDistal; + [SerializeField] private Transform m_LeftIndexProximal; public Transform LeftIndexProximal => m_LeftIndexProximal; + [SerializeField] private Transform m_LeftIndexIntermediate; public Transform LeftIndexIntermediate => m_LeftIndexIntermediate; + [SerializeField] private Transform m_LeftIndexDistal; public Transform LeftIndexDistal => m_LeftIndexDistal; + [SerializeField] private Transform m_LeftMiddleProximal; public Transform LeftMiddleProximal => m_LeftMiddleProximal; + [SerializeField] private Transform m_LeftMiddleIntermediate; public Transform LeftMiddleIntermediate => m_LeftMiddleIntermediate; + [SerializeField] private Transform m_LeftMiddleDistal; public Transform LeftMiddleDistal => m_LeftMiddleDistal; + [SerializeField] private Transform m_LeftRingProximal; public Transform LeftRingProximal => m_LeftRingProximal; + [SerializeField] private Transform m_LeftRingIntermediate; public Transform LeftRingIntermediate => m_LeftRingIntermediate; + [SerializeField] private Transform m_LeftRingDistal; public Transform LeftRingDistal => m_LeftRingDistal; + [SerializeField] private Transform m_LeftLittleProximal; public Transform LeftLittleProximal => m_LeftLittleProximal; + [SerializeField] private Transform m_LeftLittleIntermediate; public Transform LeftLittleIntermediate => m_LeftLittleIntermediate; + [SerializeField] private Transform m_LeftLittleDistal; public Transform LeftLittleDistal => m_LeftLittleDistal; + [SerializeField] private Transform m_RightThumbProximal; public Transform RightThumbProximal => m_RightThumbProximal; + [SerializeField] private Transform m_RightThumbIntermediate; public Transform RightThumbIntermediate => m_RightThumbIntermediate; + [SerializeField] private Transform m_RightThumbDistal; public Transform RightThumbDistal => m_RightThumbDistal; + [SerializeField] private Transform m_RightIndexProximal; public Transform RightIndexProximal => m_RightIndexProximal; + [SerializeField] private Transform m_RightIndexIntermediate; public Transform RightIndexIntermediate => m_RightIndexIntermediate; + [SerializeField] private Transform m_RightIndexDistal; public Transform RightIndexDistal => m_RightIndexDistal; + [SerializeField] private Transform m_RightMiddleProximal; public Transform RightMiddleProximal => m_RightMiddleProximal; + [SerializeField] private Transform m_RightMiddleIntermediate; public Transform RightMiddleIntermediate => m_RightMiddleIntermediate; + [SerializeField] private Transform m_RightMiddleDistal; public Transform RightMiddleDistal => m_RightMiddleDistal; + [SerializeField] private Transform m_RightRingProximal; public Transform RightRingProximal => m_RightRingProximal; + [SerializeField] private Transform m_RightRingIntermediate; public Transform RightRingIntermediate => m_RightRingIntermediate; + [SerializeField] private Transform m_RightRingDistal; public Transform RightRingDistal => m_RightRingDistal; + [SerializeField] private Transform m_RightLittleProximal; public Transform RightLittleProximal => m_RightLittleProximal; + [SerializeField] private Transform m_RightLittleIntermediate; public Transform RightLittleIntermediate => m_RightLittleIntermediate; + [SerializeField] private Transform m_RightLittleDistal; public Transform RightLittleDistal => m_RightLittleDistal; + #endregion + + void Reset() + { + AssignBonesFromAnimator(); + } + + public struct Validation + { + public readonly string Message; + public readonly bool IsError; + + public Validation(string message, bool isError) + { + Message = message; + IsError = isError; + } + } + + IEnumerable<Validation> Required(params (string, Transform)[] props) + { + foreach (var prop in props) + { + if (prop.Item2 == null) + { + var name = prop.Item1; + if (name.StartsWith("m_")) + { + name = name.Substring(2); + } + yield return new Validation($"{name} is Required", true); + } + } + } + + static Vector3 GetForward(Transform l, Transform r) + { + if (l == null || r == null) + { + return Vector3.zero; + } + var lr = (r.position - l.position).normalized; + return Vector3.Cross(lr, Vector3.up); + } + + public Vector3 GetForward() + { + return GetForward(m_LeftUpperLeg, m_RightUpperLeg); + } + + public IEnumerable<Validation> Validate() + { + foreach (var validation in Required( + (nameof(m_Hips), m_Hips), (nameof(m_Spine), m_Spine), (nameof(m_Head), m_Head), + (nameof(m_LeftUpperLeg), m_LeftUpperLeg), (nameof(m_LeftLowerLeg), m_LeftLowerLeg), (nameof(m_LeftFoot), m_LeftFoot), + (nameof(m_RightUpperLeg), m_RightUpperLeg), (nameof(m_RightLowerLeg), m_RightLowerLeg), (nameof(m_RightFoot), m_RightFoot), + (nameof(m_LeftUpperArm), m_LeftUpperArm), (nameof(m_LeftLowerArm), m_LeftLowerArm), (nameof(m_LeftHand), m_LeftHand), + (nameof(m_RightUpperArm), m_RightUpperArm), (nameof(m_RightLowerArm), m_RightLowerArm), (nameof(m_RightHand), m_RightHand) + )) + { + yield return validation; + } + + // var forward = GetForward(); + // if (Vector3.Dot(Vector3.forward, forward) < 0.5f) + // { + // yield return new Validation("Not facing the Z-axis positive direction", true); + // } + } + + /// <summary> + /// ボーン割り当てから UnityEngine.Avatar を生成する + /// </summary> + /// <returns></returns> + public Avatar CreateAvatar() + { + return HumanoidLoader.LoadHumanoidAvatar(transform, BoneMap); + } + + public Transform GetBoneTransform(HumanBodyBones bone) + { + switch (bone) + { + case HumanBodyBones.Hips: return Hips; + + #region leg + case HumanBodyBones.LeftUpperLeg: return LeftUpperLeg; + case HumanBodyBones.RightUpperLeg: return RightUpperLeg; + case HumanBodyBones.LeftLowerLeg: return LeftLowerLeg; + case HumanBodyBones.RightLowerLeg: return RightLowerLeg; + case HumanBodyBones.LeftFoot: return LeftFoot; + case HumanBodyBones.RightFoot: return RightFoot; + case HumanBodyBones.LeftToes: return LeftToes; + case HumanBodyBones.RightToes: return RightToes; + #endregion + + #region spine + case HumanBodyBones.Spine: return Spine; + case HumanBodyBones.Chest: return Chest; + case HumanBodyBones.UpperChest: return UpperChest; + case HumanBodyBones.Neck: return Neck; + case HumanBodyBones.Head: return Head; + case HumanBodyBones.LeftEye: return LeftEye; + case HumanBodyBones.RightEye: return RightEye; + case HumanBodyBones.Jaw: return Jaw; + #endregion + + #region arm + case HumanBodyBones.LeftShoulder: return LeftShoulder; + case HumanBodyBones.RightShoulder: return RightShoulder; + case HumanBodyBones.LeftUpperArm: return LeftUpperArm; + case HumanBodyBones.RightUpperArm: return RightUpperArm; + case HumanBodyBones.LeftLowerArm: return LeftLowerArm; + case HumanBodyBones.RightLowerArm: return RightLowerArm; + case HumanBodyBones.LeftHand: return LeftHand; + case HumanBodyBones.RightHand: return RightHand; + #endregion + + #region fingers + case HumanBodyBones.LeftThumbProximal: return LeftThumbProximal; + case HumanBodyBones.LeftThumbIntermediate: return LeftThumbIntermediate; + case HumanBodyBones.LeftThumbDistal: return LeftThumbDistal; + case HumanBodyBones.LeftIndexProximal: return LeftIndexProximal; + case HumanBodyBones.LeftIndexIntermediate: return LeftIndexIntermediate; + case HumanBodyBones.LeftIndexDistal: return LeftIndexDistal; + case HumanBodyBones.LeftMiddleProximal: return LeftMiddleProximal; + case HumanBodyBones.LeftMiddleIntermediate: return LeftMiddleIntermediate; + case HumanBodyBones.LeftMiddleDistal: return LeftMiddleDistal; + case HumanBodyBones.LeftRingProximal: return LeftRingProximal; + case HumanBodyBones.LeftRingIntermediate: return LeftRingIntermediate; + case HumanBodyBones.LeftRingDistal: return LeftRingDistal; + case HumanBodyBones.LeftLittleProximal: return LeftLittleProximal; + case HumanBodyBones.LeftLittleIntermediate: return LeftLittleIntermediate; + case HumanBodyBones.LeftLittleDistal: return LeftLittleDistal; + case HumanBodyBones.RightThumbProximal: return RightThumbProximal; + case HumanBodyBones.RightThumbIntermediate: return RightThumbIntermediate; + case HumanBodyBones.RightThumbDistal: return RightThumbDistal; + case HumanBodyBones.RightIndexProximal: return RightIndexProximal; + case HumanBodyBones.RightIndexIntermediate: return RightIndexIntermediate; + case HumanBodyBones.RightIndexDistal: return RightIndexDistal; + case HumanBodyBones.RightMiddleProximal: return RightMiddleProximal; + case HumanBodyBones.RightMiddleIntermediate: return RightMiddleIntermediate; + case HumanBodyBones.RightMiddleDistal: return RightMiddleDistal; + case HumanBodyBones.RightRingProximal: return RightRingProximal; + case HumanBodyBones.RightRingIntermediate: return RightRingIntermediate; + case HumanBodyBones.RightRingDistal: return RightRingDistal; + case HumanBodyBones.RightLittleProximal: return RightLittleProximal; + case HumanBodyBones.RightLittleIntermediate: return RightLittleIntermediate; + case HumanBodyBones.RightLittleDistal: return RightLittleDistal; + #endregion + + } + + return null; + } + + IEnumerable<(Transform, HumanBodyBones)> BoneMap + { + get + { + if (Hips != null) { yield return (Hips, HumanBodyBones.Hips); } + + #region leg + if (LeftUpperLeg != null) { yield return (LeftUpperLeg, HumanBodyBones.LeftUpperLeg); } + if (RightUpperLeg != null) { yield return (RightUpperLeg, HumanBodyBones.RightUpperLeg); } + if (LeftLowerLeg != null) { yield return (LeftLowerLeg, HumanBodyBones.LeftLowerLeg); } + if (RightLowerLeg != null) { yield return (RightLowerLeg, HumanBodyBones.RightLowerLeg); } + if (LeftFoot != null) { yield return (LeftFoot, HumanBodyBones.LeftFoot); } + if (RightFoot != null) { yield return (RightFoot, HumanBodyBones.RightFoot); } + if (LeftToes != null) { yield return (LeftToes, HumanBodyBones.LeftToes); } + if (RightToes != null) { yield return (RightToes, HumanBodyBones.RightToes); } + #endregion + + #region spine + if (Spine != null) { yield return (Spine, HumanBodyBones.Spine); } + if (Chest != null) { yield return (Chest, HumanBodyBones.Chest); } + if (UpperChest != null) { yield return (UpperChest, HumanBodyBones.UpperChest); } + if (Neck != null) { yield return (Neck, HumanBodyBones.Neck); } + if (Head != null) { yield return (Head, HumanBodyBones.Head); } + if (LeftEye != null) { yield return (LeftEye, HumanBodyBones.LeftEye); } + if (RightEye != null) { yield return (RightEye, HumanBodyBones.RightEye); } + if (Jaw != null) { yield return (Jaw, HumanBodyBones.Jaw); } + #endregion + + #region arm + if (LeftShoulder != null) { yield return (LeftShoulder, HumanBodyBones.LeftShoulder); } + if (RightShoulder != null) { yield return (RightShoulder, HumanBodyBones.RightShoulder); } + if (LeftUpperArm != null) { yield return (LeftUpperArm, HumanBodyBones.LeftUpperArm); } + if (RightUpperArm != null) { yield return (RightUpperArm, HumanBodyBones.RightUpperArm); } + if (LeftLowerArm != null) { yield return (LeftLowerArm, HumanBodyBones.LeftLowerArm); } + if (RightLowerArm != null) { yield return (RightLowerArm, HumanBodyBones.RightLowerArm); } + if (LeftHand != null) { yield return (LeftHand, HumanBodyBones.LeftHand); } + if (RightHand != null) { yield return (RightHand, HumanBodyBones.RightHand); } + #endregion + + #region fingers + if (LeftThumbProximal != null) { yield return (LeftThumbProximal, HumanBodyBones.LeftThumbProximal); } + if (LeftThumbIntermediate != null) { yield return (LeftThumbIntermediate, HumanBodyBones.LeftThumbIntermediate); } + if (LeftThumbDistal != null) { yield return (LeftThumbDistal, HumanBodyBones.LeftThumbDistal); } + if (LeftIndexProximal != null) { yield return (LeftIndexProximal, HumanBodyBones.LeftIndexProximal); } + if (LeftIndexIntermediate != null) { yield return (LeftIndexIntermediate, HumanBodyBones.LeftIndexIntermediate); } + if (LeftIndexDistal != null) { yield return (LeftIndexDistal, HumanBodyBones.LeftIndexDistal); } + if (LeftMiddleProximal != null) { yield return (LeftMiddleProximal, HumanBodyBones.LeftMiddleProximal); } + if (LeftMiddleIntermediate != null) { yield return (LeftMiddleIntermediate, HumanBodyBones.LeftMiddleIntermediate); } + if (LeftMiddleDistal != null) { yield return (LeftMiddleDistal, HumanBodyBones.LeftMiddleDistal); } + if (LeftRingProximal != null) { yield return (LeftRingProximal, HumanBodyBones.LeftRingProximal); } + if (LeftRingIntermediate != null) { yield return (LeftRingIntermediate, HumanBodyBones.LeftRingIntermediate); } + if (LeftRingDistal != null) { yield return (LeftRingDistal, HumanBodyBones.LeftRingDistal); } + if (LeftLittleProximal != null) { yield return (LeftLittleProximal, HumanBodyBones.LeftLittleProximal); } + if (LeftLittleIntermediate != null) { yield return (LeftLittleIntermediate, HumanBodyBones.LeftLittleIntermediate); } + if (LeftLittleDistal != null) { yield return (LeftLittleDistal, HumanBodyBones.LeftLittleDistal); } + if (RightThumbProximal != null) { yield return (RightThumbProximal, HumanBodyBones.RightThumbProximal); } + if (RightThumbIntermediate != null) { yield return (RightThumbIntermediate, HumanBodyBones.RightThumbIntermediate); } + if (RightThumbDistal != null) { yield return (RightThumbDistal, HumanBodyBones.RightThumbDistal); } + if (RightIndexProximal != null) { yield return (RightIndexProximal, HumanBodyBones.RightIndexProximal); } + if (RightIndexIntermediate != null) { yield return (RightIndexIntermediate, HumanBodyBones.RightIndexIntermediate); } + if (RightIndexDistal != null) { yield return (RightIndexDistal, HumanBodyBones.RightIndexDistal); } + if (RightMiddleProximal != null) { yield return (RightMiddleProximal, HumanBodyBones.RightMiddleProximal); } + if (RightMiddleIntermediate != null) { yield return (RightMiddleIntermediate, HumanBodyBones.RightMiddleIntermediate); } + if (RightMiddleDistal != null) { yield return (RightMiddleDistal, HumanBodyBones.RightMiddleDistal); } + if (RightRingProximal != null) { yield return (RightRingProximal, HumanBodyBones.RightRingProximal); } + if (RightRingIntermediate != null) { yield return (RightRingIntermediate, HumanBodyBones.RightRingIntermediate); } + if (RightRingDistal != null) { yield return (RightRingDistal, HumanBodyBones.RightRingDistal); } + if (RightLittleProximal != null) { yield return (RightLittleProximal, HumanBodyBones.RightLittleProximal); } + if (RightLittleIntermediate != null) { yield return (RightLittleIntermediate, HumanBodyBones.RightLittleIntermediate); } + if (RightLittleDistal != null) { yield return (RightLittleDistal, HumanBodyBones.RightLittleDistal); } + #endregion + } + } + + /// <summary> + /// nodes からボーンを割り当てる + /// </summary> + /// <param name="nodes"></param> + public void AssignBones(IEnumerable<(HumanBodyBones, Transform)> nodes) + { + foreach (var (key, value) in nodes) + { + if (key == HumanBodyBones.LastBone) + { + continue; + } + if (value is null) + { + continue; + } + + switch (key) + { + case HumanBodyBones.Hips: m_Hips = value; break; + + #region leg + case HumanBodyBones.LeftUpperLeg: m_LeftUpperLeg = value; break; + case HumanBodyBones.RightUpperLeg: m_RightUpperLeg = value; break; + case HumanBodyBones.LeftLowerLeg: m_LeftLowerLeg = value; break; + case HumanBodyBones.RightLowerLeg: m_RightLowerLeg = value; break; + case HumanBodyBones.LeftFoot: m_LeftFoot = value; break; + case HumanBodyBones.RightFoot: m_RightFoot = value; break; + case HumanBodyBones.LeftToes: m_LeftToes = value; break; + case HumanBodyBones.RightToes: m_RightToes = value; break; + #endregion + + #region spine + case HumanBodyBones.Spine: m_Spine = value; break; + case HumanBodyBones.Chest: m_Chest = value; break; + case HumanBodyBones.UpperChest: m_UpperChest = value; break; + case HumanBodyBones.Neck: m_Neck = value; break; + case HumanBodyBones.Head: m_Head = value; break; + case HumanBodyBones.LeftEye: m_LeftEye = value; break; + case HumanBodyBones.RightEye: m_RightEye = value; break; + case HumanBodyBones.Jaw: m_Jaw = value; break; + #endregion + + #region arm + case HumanBodyBones.LeftShoulder: m_LeftShoulder = value; break; + case HumanBodyBones.RightShoulder: m_RightShoulder = value; break; + case HumanBodyBones.LeftUpperArm: m_LeftUpperArm = value; break; + case HumanBodyBones.RightUpperArm: m_RightUpperArm = value; break; + case HumanBodyBones.LeftLowerArm: m_LeftLowerArm = value; break; + case HumanBodyBones.RightLowerArm: m_RightLowerArm = value; break; + case HumanBodyBones.LeftHand: m_LeftHand = value; break; + case HumanBodyBones.RightHand: m_RightHand = value; break; + #endregion + + #region fingers + case HumanBodyBones.LeftThumbProximal: m_LeftThumbProximal = value; break; + case HumanBodyBones.LeftThumbIntermediate: m_LeftThumbIntermediate = value; break; + case HumanBodyBones.LeftThumbDistal: m_LeftThumbDistal = value; break; + case HumanBodyBones.LeftIndexProximal: m_LeftIndexProximal = value; break; + case HumanBodyBones.LeftIndexIntermediate: m_LeftIndexIntermediate = value; break; + case HumanBodyBones.LeftIndexDistal: m_LeftIndexDistal = value; break; + case HumanBodyBones.LeftMiddleProximal: m_LeftMiddleProximal = value; break; + case HumanBodyBones.LeftMiddleIntermediate: m_LeftMiddleIntermediate = value; break; + case HumanBodyBones.LeftMiddleDistal: m_LeftMiddleDistal = value; break; + case HumanBodyBones.LeftRingProximal: m_LeftRingProximal = value; break; + case HumanBodyBones.LeftRingIntermediate: m_LeftRingIntermediate = value; break; + case HumanBodyBones.LeftRingDistal: m_LeftRingDistal = value; break; + case HumanBodyBones.LeftLittleProximal: m_LeftLittleProximal = value; break; + case HumanBodyBones.LeftLittleIntermediate: m_LeftLittleIntermediate = value; break; + case HumanBodyBones.LeftLittleDistal: m_LeftLittleDistal = value; break; + case HumanBodyBones.RightThumbProximal: m_RightThumbProximal = value; break; + case HumanBodyBones.RightThumbIntermediate: m_RightThumbIntermediate = value; break; + case HumanBodyBones.RightThumbDistal: m_RightThumbDistal = value; break; + case HumanBodyBones.RightIndexProximal: m_RightIndexProximal = value; break; + case HumanBodyBones.RightIndexIntermediate: m_RightIndexIntermediate = value; break; + case HumanBodyBones.RightIndexDistal: m_RightIndexDistal = value; break; + case HumanBodyBones.RightMiddleProximal: m_RightMiddleProximal = value; break; + case HumanBodyBones.RightMiddleIntermediate: m_RightMiddleIntermediate = value; break; + case HumanBodyBones.RightMiddleDistal: m_RightMiddleDistal = value; break; + case HumanBodyBones.RightRingProximal: m_RightRingProximal = value; break; + case HumanBodyBones.RightRingIntermediate: m_RightRingIntermediate = value; break; + case HumanBodyBones.RightRingDistal: m_RightRingDistal = value; break; + case HumanBodyBones.RightLittleProximal: m_RightLittleProximal = value; break; + case HumanBodyBones.RightLittleIntermediate: m_RightLittleIntermediate = value; break; + case HumanBodyBones.RightLittleDistal: m_RightLittleDistal = value; break; + #endregion + } + } + } + + /// <summary> + /// Animator から Bone を割り当てる + /// </summary> + /// <returns></returns> + public bool AssignBonesFromAnimator() + { + var animator = GetComponent<Animator>(); + if (animator == null) + { + return false; + } + var avatar = animator.avatar; + if (avatar == null) + { + return false; + } + if (!avatar.isValid) + { + return false; + } + if (!avatar.isHuman) + { + return false; + } + + var keys = (UnityEngine.HumanBodyBones[])Enum.GetValues(typeof(UnityEngine.HumanBodyBones)); + + AssignBones(keys.Select(x => + { + if (x == HumanBodyBones.LastBone) + { + return (HumanBodyBones.LastBone, null); + } + return ((HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), x.ToString(), true), animator.GetBoneTransform(x)); + })); + + return true; + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs.meta new file mode 100644 index 00000000..53fc0f6d --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/Humanoid.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 889d98e41c0e8ff48bae50d1a729c2df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs new file mode 100644 index 00000000..2a5d5252 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace MeshUtility +{ + public static class HumanoidLoader + { + public static Avatar LoadHumanoidAvatar(Transform root, IEnumerable<(Transform, HumanBodyBones)> boneMap) + { + var description = new HumanDescription + { + skeleton = root.GetComponentsInChildren<Transform>() + .Select(x => x.ToSkeletonBone()).ToArray(), + human = boneMap + .Select(x => new HumanBone + { + boneName = x.Item1.name, + humanName = s_humanTranitBoneNameMap[x.Item2], + limit = new HumanLimit + { + useDefaultValues = true, + } + }).ToArray(), + + armStretch = 0.05f, + legStretch = 0.05f, + upperArmTwist = 0.5f, + lowerArmTwist = 0.5f, + upperLegTwist = 0.5f, + lowerLegTwist = 0.5f, + feetSpacing = 0, + hasTranslationDoF = false, + }; + + return AvatarBuilder.BuildHumanAvatar(root.gameObject, description); + } + + static SkeletonBone ToSkeletonBone(this Transform t) + { + var sb = new SkeletonBone(); + sb.name = t.name; + sb.position = t.localPosition; + sb.rotation = t.localRotation; + sb.scale = t.localScale; + return sb; + } + + static HumanBodyBones TraitToHumanBone(string x) + { + return (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), x.Replace(" ", ""), true); + } + + static readonly Dictionary<HumanBodyBones, string> s_humanTranitBoneNameMap = + HumanTrait.BoneName.ToDictionary( + x => TraitToHumanBone(x), + x => x); + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs.meta new file mode 100644 index 00000000..02965556 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/HumanoidLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12453a111483e4145852e3b057e065d9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs new file mode 100644 index 00000000..aa458d52 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs @@ -0,0 +1,85 @@ +using UnityEngine; +using System.Linq; + + +namespace MeshUtility +{ + public static class MeshExtensions + { + public static Mesh Copy(this Mesh src, bool copyBlendShape) + { + var dst = new Mesh(); + dst.name = src.name + "(copy)"; +#if UNITY_2017_3_OR_NEWER + dst.indexFormat = src.indexFormat; +#endif + + dst.vertices = src.vertices; + dst.normals = src.normals; + dst.tangents = src.tangents; + dst.colors = src.colors; + dst.uv = src.uv; + dst.uv2 = src.uv2; + dst.uv3 = src.uv3; + dst.uv4 = src.uv4; + dst.boneWeights = src.boneWeights; + dst.bindposes = src.bindposes; + + dst.subMeshCount = src.subMeshCount; + for (int i = 0; i < dst.subMeshCount; ++i) + { + dst.SetIndices(src.GetIndices(i), src.GetTopology(i), i); + } + + dst.RecalculateBounds(); + + if (copyBlendShape) + { + var vertices = src.vertices; + var normals = src.normals; +#if VRM_NORMALIZE_BLENDSHAPE_TANGENT + var tangents = src.tangents.Select(x => (Vector3)x).ToArray(); +#else + Vector3[] tangents = null; +#endif + + for (int i = 0; i < src.blendShapeCount; ++i) + { + src.GetBlendShapeFrameVertices(i, 0, vertices, normals, tangents); + dst.AddBlendShapeFrame( + src.GetBlendShapeName(i), + src.GetBlendShapeFrameWeight(i, 0), + vertices, + normals, + tangents + ); + } + } + + return dst; + } + + public static void ApplyRotationAndScale(this Mesh src, Matrix4x4 m) + { + m.SetColumn(3, new Vector4(0, 0, 0, 1)); // remove translation + src.ApplyMatrix(m); + } + + public static void ApplyMatrix(this Mesh src, Matrix4x4 m) + { + src.vertices = src.vertices.Select(x => m.MultiplyPoint(x)).ToArray(); + if (src.normals != null && src.normals.Length > 0) + { + src.normals = src.normals.Select(x => m.MultiplyVector(x)).ToArray(); + } + if (src.tangents != null && src.tangents.Length > 0) + { + src.tangents = src.tangents.Select(x => + { + var t = m.MultiplyVector((Vector3)x); + return new Vector4(t.x, t.y, t.z, x.w); + }).ToArray(); + } + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs.meta new file mode 100644 index 00000000..7485178c --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshExtensions.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4181f0b5e9a271b45b3e995a38202780 +timeCreated: 1532506262 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs new file mode 100644 index 00000000..91742fc8 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace MeshUtility +{ + [System.Serializable] + public class MeshIntegrationResult + { + public List<SkinnedMeshRenderer> SourceSkinnedMeshRenderers = new List<SkinnedMeshRenderer>(); + public List<MeshRenderer> SourceMeshRenderers = new List<MeshRenderer>(); + public SkinnedMeshRenderer IntegratedRenderer; + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs.meta new file mode 100644 index 00000000..5f32450b --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrationResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1c66a21d479b3e4a92eedd622d27f4f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs new file mode 100644 index 00000000..34ba7005 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs @@ -0,0 +1,253 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace MeshUtility +{ + public class MeshIntegrator + { + public struct SubMesh + { + public List<int> Indices; + public Material Material; + } + + public class BlendShape + { + public int VertexOffset; + public string Name; + public float FrameWeight; + public Vector3[] Positions; + public Vector3[] Normals; + public Vector3[] Tangents; + } + +// public List<SkinnedMeshRenderer> Renderers { get; private set; } + public List<Vector3> Positions { get; private set; } + public List<Vector3> Normals { get; private set; } + public List<Vector2> UV { get; private set; } + public List<Vector4> Tangents { get; private set; } + public List<BoneWeight> BoneWeights { get; private set; } + + public List<SubMesh> SubMeshes + { + get; + private set; + } + + public List<Matrix4x4> BindPoses { get; private set; } + public List<Transform> Bones { get; private set; } + + public List<BlendShape> BlendShapes { get; private set; } + public void AddBlendShapesToMesh(Mesh mesh) + { + Dictionary<string, BlendShape> map = new Dictionary<string, BlendShape>(); + + foreach (var x in BlendShapes) + { + BlendShape bs = null; + if (!map.TryGetValue(x.Name, out bs)) + { + bs = new BlendShape(); + bs.Positions = new Vector3[Positions.Count]; + bs.Normals = new Vector3[Normals.Count]; + bs.Tangents = new Vector3[Tangents.Count]; + bs.Name = x.Name; + bs.FrameWeight = x.FrameWeight; + map.Add(x.Name, bs); + } + + var j = x.VertexOffset; + for (int i = 0; i < x.Positions.Length; ++i, ++j) + { + bs.Positions[j] = x.Positions[i]; + bs.Normals[j] = x.Normals[i]; + bs.Tangents[j] = x.Tangents[i]; + } + } + + foreach (var kv in map) + { + //Debug.LogFormat("AddBlendShapeFrame: {0}", kv.Key); + mesh.AddBlendShapeFrame(kv.Key, kv.Value.FrameWeight, + kv.Value.Positions, kv.Value.Normals, kv.Value.Tangents); + } + } + + public MeshIntegrator() + { +// Renderers = new List<SkinnedMeshRenderer>(); + + Positions = new List<Vector3>(); + Normals = new List<Vector3>(); + UV = new List<Vector2>(); + Tangents = new List<Vector4>(); + BoneWeights = new List<BoneWeight>(); + + SubMeshes = new List<SubMesh>(); + + BindPoses = new List<Matrix4x4>(); + Bones = new List<Transform>(); + + BlendShapes = new List<BlendShape>(); + } + + static BoneWeight AddBoneIndexOffset(BoneWeight bw, int boneIndexOffset) + { + if (bw.weight0 > 0) bw.boneIndex0 += boneIndexOffset; + if (bw.weight1 > 0) bw.boneIndex1 += boneIndexOffset; + if (bw.weight2 > 0) bw.boneIndex2 += boneIndexOffset; + if (bw.weight3 > 0) bw.boneIndex3 += boneIndexOffset; + return bw; + } + + public void Push(MeshRenderer renderer) + { + var meshFilter = renderer.GetComponent<MeshFilter>(); + if (meshFilter == null) + { + Debug.LogWarningFormat("{0} has no mesh filter", renderer.name); + return; + } + var mesh = meshFilter.sharedMesh; + if (mesh == null) + { + Debug.LogWarningFormat("{0} has no mesh", renderer.name); + return; + } + + var indexOffset = Positions.Count; + var boneIndexOffset = Bones.Count; + + Positions.AddRange(mesh.vertices + .Select(x => renderer.transform.TransformPoint(x)) + ); + Normals.AddRange(mesh.normals + .Select(x => renderer.transform.TransformVector(x)) + ); + UV.AddRange(mesh.uv); + Tangents.AddRange(mesh.tangents + .Select(t => + { + var v = renderer.transform.TransformVector(t.x, t.y, t.z); + return new Vector4(v.x, v.y, v.z, t.w); + }) + ); + + var self = renderer.transform; + var bone = self.parent; + if (bone == null) + { + Debug.LogWarningFormat("{0} is root gameobject.", self.name); + return; + } + var bindpose = bone.worldToLocalMatrix; + + BoneWeights.AddRange(Enumerable.Range(0, mesh.vertices.Length) + .Select(x => new BoneWeight() + { + boneIndex0 = Bones.Count, + weight0 = 1, + }) + ); + + BindPoses.Add(bindpose); + Bones.Add(bone); + + for (int i = 0; i < mesh.subMeshCount; ++i) + { + var indices = mesh.GetIndices(i).Select(x => x + indexOffset); + var mat = renderer.sharedMaterials[i]; + var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat)); + if (sameMaterialSubMeshIndex >= 0) + { + SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices); + } + else + { + SubMeshes.Add(new SubMesh + { + Indices = indices.ToList(), + Material = mat, + }); + } + } + } + + public void Push(SkinnedMeshRenderer renderer) + { + var mesh = renderer.sharedMesh; + if (mesh == null) + { + Debug.LogWarningFormat("{0} has no mesh", renderer.name); + return; + } + +// Renderers.Add(renderer); + + var indexOffset = Positions.Count; + var boneIndexOffset = Bones.Count; + + Positions.AddRange(mesh.vertices); + Normals.AddRange(mesh.normals); + UV.AddRange(mesh.uv); + Tangents.AddRange(mesh.tangents); + + if (mesh.vertexCount == mesh.boneWeights.Length) + { + BoneWeights.AddRange(mesh.boneWeights.Select(x => AddBoneIndexOffset(x, boneIndexOffset)).ToArray()); + BindPoses.AddRange(mesh.bindposes); + Bones.AddRange(renderer.bones); + } + else + { + // Bone Count 0 の SkinnedMeshRenderer + var rigidBoneWeight = new BoneWeight + { + boneIndex0 = boneIndexOffset, + weight0 = 1f, + }; + BoneWeights.AddRange(Enumerable.Range(0, mesh.vertexCount).Select(x => rigidBoneWeight).ToArray()); + BindPoses.Add(renderer.transform.localToWorldMatrix); + Bones.Add(renderer.transform); + } + + for (int i = 0; i < mesh.subMeshCount; ++i) + { + var indices = mesh.GetIndices(i).Select(x => x + indexOffset); + var mat = renderer.sharedMaterials[i]; + var sameMaterialSubMeshIndex = SubMeshes.FindIndex(x => ReferenceEquals(x.Material, mat)); + if (sameMaterialSubMeshIndex >= 0) + { + SubMeshes[sameMaterialSubMeshIndex].Indices.AddRange(indices); + } + else + { + SubMeshes.Add(new SubMesh + { + Indices = indices.ToList(), + Material = mat, + }); + } + } + + for (int i = 0; i < mesh.blendShapeCount; ++i) + { + var positions = (Vector3[])mesh.vertices.Clone(); + var normals = (Vector3[])mesh.normals.Clone(); + var tangents = mesh.tangents.Select(x => (Vector3)x).ToArray(); + + mesh.GetBlendShapeFrameVertices(i, 0, positions, normals, tangents); + BlendShapes.Add(new BlendShape + { + VertexOffset = indexOffset, + FrameWeight = mesh.GetBlendShapeFrameWeight(i, 0), + Name = mesh.GetBlendShapeName(i), + Positions = positions, + Normals = normals, + Tangents = tangents, + }); + } + } + } +}
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs.meta new file mode 100644 index 00000000..15921c89 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegrator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 547dd57b50bf4820a570336659345084 +timeCreated: 1560168946
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs new file mode 100644 index 00000000..cfb1742e --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace MeshUtility +{ + public static class MeshIntegratorUtility + { + /// <summary> + /// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する + /// </summary> + /// <param name="go"></param> + /// <param name="onlyBlendShapeRenderers">BlendShapeを保持するSkinnedMeshRendererのみ/BlendShapeを保持しないSkinnedMeshRenderer + MeshRenderer</param> + /// <returns></returns> + public static MeshIntegrationResult Integrate(GameObject go, bool onlyBlendShapeRenderers) + { + var result = new MeshIntegrationResult(); + + var meshNode = new GameObject(); + if (onlyBlendShapeRenderers) + { + meshNode.name = "MeshIntegrator(BlendShape)"; + } + else + { + meshNode.name = "MeshIntegrator"; + } + meshNode.transform.SetParent(go.transform, false); + + // レンダラから情報を集める + var integrator = new MeshUtility.MeshIntegrator(); + + if (onlyBlendShapeRenderers) + { + foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, true)) + { + integrator.Push(x); + result.SourceSkinnedMeshRenderers.Add(x); + } + } + else + { + foreach (var x in EnumerateSkinnedMeshRenderer(go.transform, false)) + { + integrator.Push(x); + result.SourceSkinnedMeshRenderers.Add(x); + } + + foreach (var x in EnumerateMeshRenderer(go.transform)) + { + integrator.Push(x); + result.SourceMeshRenderers.Add(x); + } + } + + var mesh = new Mesh(); + mesh.name = "integrated"; + + if (integrator.Positions.Count > ushort.MaxValue) + { + Debug.LogFormat("exceed 65535 vertices: {0}", integrator.Positions.Count); + mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + } + + mesh.vertices = integrator.Positions.ToArray(); + mesh.normals = integrator.Normals.ToArray(); + mesh.uv = integrator.UV.ToArray(); + mesh.tangents = integrator.Tangents.ToArray(); + mesh.boneWeights = integrator.BoneWeights.ToArray(); + mesh.subMeshCount = integrator.SubMeshes.Count; + for (var i = 0; i < integrator.SubMeshes.Count; ++i) + { + mesh.SetIndices(integrator.SubMeshes[i].Indices.ToArray(), MeshTopology.Triangles, i); + } + mesh.bindposes = integrator.BindPoses.ToArray(); + + if (onlyBlendShapeRenderers) + { + integrator.AddBlendShapesToMesh(mesh); + } + + var integrated = meshNode.AddComponent<SkinnedMeshRenderer>(); + integrated.sharedMesh = mesh; + integrated.sharedMaterials = integrator.SubMeshes.Select(x => x.Material).ToArray(); + integrated.bones = integrator.Bones.ToArray(); + result.IntegratedRenderer = integrated; + + return result; + } + + public static IEnumerable<SkinnedMeshRenderer> EnumerateSkinnedMeshRenderer(Transform root, bool hasBlendShape) + { + foreach (var x in Traverse(root)) + { + var renderer = x.GetComponent<SkinnedMeshRenderer>(); + if (renderer != null && + renderer.gameObject.activeInHierarchy && + renderer.sharedMesh != null && + renderer.enabled && + renderer.sharedMesh.blendShapeCount > 0 == hasBlendShape) + { + yield return renderer; + } + } + } + + public static IEnumerable<MeshRenderer> EnumerateMeshRenderer(Transform root) + { + foreach (var x in Traverse(root)) + { + var renderer = x.GetComponent<MeshRenderer>(); + var filter = x.GetComponent<MeshFilter>(); + + if (renderer != null && + filter != null && + renderer.gameObject.activeInHierarchy && + filter.sharedMesh != null) + { + yield return renderer; + } + } + } + + private static IEnumerable<Transform> Traverse(Transform parent) + { + if (parent.gameObject.activeSelf) + { + yield return parent; + + foreach (Transform child in parent) + { + foreach (var x in Traverse(child)) + { + yield return x; + } + } + } + } + } +}
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs.meta new file mode 100644 index 00000000..b9075e71 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshIntegratorUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a982d9d30c0145038245b0214dc2f2e4 +timeCreated: 1560190306
\ No newline at end of file diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef new file mode 100644 index 00000000..f161fcaf --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef @@ -0,0 +1,3 @@ +{ + "name": "MeshUtility" +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef.meta new file mode 100644 index 00000000..4085faa9 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/MeshUtility.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 71ab1919192903d44971eedbc26b24d1 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs new file mode 100644 index 00000000..a772980d --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace MeshUtility +{ + public struct PosRot + { + public Vector3 Position; + public Quaternion Rotation; + + public static PosRot FromGlobalTransform(Transform t) + { + return new PosRot + { + Position = t.position, + Rotation = t.rotation, + }; + } + } + + public class BlendShape + { + public string Name; + + public BlendShape(string name) + { + Name = name; + } + + public List<Vector3> Positions = new List<Vector3>(); + public List<Vector3> Normals = new List<Vector3>(); + public List<Vector3> Tangents = new List<Vector3>(); + } + + public static class UnityExtensions + { + public static Vector4 ReverseZ(this Vector4 v) + { + return new Vector4(v.x, v.y, -v.z, v.w); + } + + public static Vector3 ReverseZ(this Vector3 v) + { + return new Vector3(v.x, v.y, -v.z); + } + + [Obsolete] + public static Vector2 ReverseY(this Vector2 v) + { + return new Vector2(v.x, -v.y); + } + + public static Vector2 ReverseUV(this Vector2 v) + { + return new Vector2(v.x, 1.0f - v.y); + } + + public static Quaternion ReverseZ(this Quaternion q) + { + float angle; + Vector3 axis; + q.ToAngleAxis(out angle, out axis); + return Quaternion.AngleAxis(-angle, ReverseZ(axis)); + } + + public static Matrix4x4 Matrix4x4FromColumns(Vector4 c0, Vector4 c1, Vector4 c2, Vector4 c3) + { +#if UNITY_2017_1_OR_NEWER + return new Matrix4x4(c0, c1, c2, c3); +#else + var m = default(Matrix4x4); + m.SetColumn(0, c0); + m.SetColumn(1, c1); + m.SetColumn(2, c2); + m.SetColumn(3, c3); + return m; +#endif + } + + public static Matrix4x4 Matrix4x4FromRotation(Quaternion q) + { +#if UNITY_2017_1_OR_NEWER + return Matrix4x4.Rotate(q); +#else + var m = default(Matrix4x4); + m.SetTRS(Vector3.zero, q, Vector3.one); + return m; +#endif + } + + public static Matrix4x4 ReverseZ(this Matrix4x4 m) + { + m.SetTRS(m.ExtractPosition().ReverseZ(), m.ExtractRotation().ReverseZ(), m.ExtractScale()); + return m; + } + + public static Matrix4x4 MatrixFromArray(float[] values) + { + var m = new Matrix4x4(); + m.m00 = values[0]; + m.m10 = values[1]; + m.m20 = values[2]; + m.m30 = values[3]; + m.m01 = values[4]; + m.m11 = values[5]; + m.m21 = values[6]; + m.m31 = values[7]; + m.m02 = values[8]; + m.m12 = values[9]; + m.m22 = values[10]; + m.m32 = values[11]; + m.m03 = values[12]; + m.m13 = values[13]; + m.m23 = values[14]; + m.m33 = values[15]; + return m; + } + + // https://forum.unity.com/threads/how-to-assign-matrix4x4-to-transform.121966/ + public static Quaternion ExtractRotation(this Matrix4x4 matrix) + { + Vector3 forward; + forward.x = matrix.m02; + forward.y = matrix.m12; + forward.z = matrix.m22; + + Vector3 upwards; + upwards.x = matrix.m01; + upwards.y = matrix.m11; + upwards.z = matrix.m21; + + return Quaternion.LookRotation(forward, upwards); + } + + public static Vector3 ExtractPosition(this Matrix4x4 matrix) + { + Vector3 position; + position.x = matrix.m03; + position.y = matrix.m13; + position.z = matrix.m23; + return position; + } + + public static Vector3 ExtractScale(this Matrix4x4 matrix) + { + Vector3 scale; + scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude; + scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude; + scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude; + return scale; + } + + public static string RelativePathFrom(this Transform self, Transform root) + { + var path = new List<String>(); + for (var current = self; current != null; current = current.parent) + { + if (current == root) + { + return String.Join("/", path.ToArray()); + } + + path.Insert(0, current.name); + } + + throw new Exception("no RelativePath"); + } + + public static Transform GetChildByName(this Transform self, string childName) + { + foreach (Transform child in self) + { + if (child.name == childName) + { + return child; + } + } + + throw new KeyNotFoundException(); + } + + public static Transform GetFromPath(this Transform self, string path) + { + var current = self; + + var split = path.Split('/'); + + foreach (var childName in split) + { + current = current.GetChildByName(childName); + } + + return current; + } + + public static IEnumerable<Transform> GetChildren(this Transform self) + { + foreach (Transform child in self) + { + yield return child; + } + } + + public static IEnumerable<Transform> Traverse(this Transform t) + { + yield return t; + foreach (Transform x in t) + { + foreach (Transform y in x.Traverse()) + { + yield return y; + } + } + } + + [Obsolete("Use FindDescendant(name)")] + public static Transform FindDescenedant(this Transform t, string name) + { + return FindDescendant(t, name); + } + + public static Transform FindDescendant(this Transform t, string name) + { + return t.Traverse().First(x => x.name == name); + } + + public static IEnumerable<Transform> Ancestors(this Transform t) + { + yield return t; + if (t.parent != null) + { + foreach (Transform x in t.parent.Ancestors()) + { + yield return x; + } + } + } + + public static float[] ToArray(this Quaternion q) + { + return new float[] { q.x, q.y, q.z, q.w }; + } + + public static float[] ToArray(this Vector3 v) + { + return new float[] { v.x, v.y, v.z }; + } + + public static float[] ToArray(this Vector4 v) + { + return new float[] { v.x, v.y, v.z, v.w }; + } + + public static float[] ToArray(this Color c) + { + return new float[] { c.r, c.g, c.b, c.a }; + } + + public static void ReverseZRecursive(this Transform root) + { + var globalMap = root.Traverse().ToDictionary(x => x, x => PosRot.FromGlobalTransform(x)); + + foreach (var x in root.Traverse()) + { + x.position = globalMap[x].Position.ReverseZ(); + x.rotation = globalMap[x].Rotation.ReverseZ(); + } + } + + public static Mesh GetSharedMesh(this Transform t) + { + var meshFilter = t.GetComponent<MeshFilter>(); + if (meshFilter != null) + { + return meshFilter.sharedMesh; + } + + var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>(); + if (skinnedMeshRenderer != null) + { + return skinnedMeshRenderer.sharedMesh; + } + + return null; + } + + public static Material[] GetSharedMaterials(this Transform t) + { + var renderer = t.GetComponent<Renderer>(); + if (renderer != null) + { + return renderer.sharedMaterials; + } + + return new Material[] { }; + } + + public static bool Has<T>(this Transform transform, T t) where T : Component + { + return transform.GetComponent<T>() == t; + } + + public static T GetOrAddComponent<T>(this GameObject go) where T : Component + { + var c = go.GetComponent<T>(); + if (c != null) + { + return c; + } + return go.AddComponent<T>(); + } + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs.meta new file mode 100644 index 00000000..07785c58 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5294813527b3278458026afc820dd63d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs new file mode 100644 index 00000000..24404741 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs @@ -0,0 +1,435 @@ +using System; +using System.IO; +using UnityEngine; +using System.Collections.Generic; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace MeshUtility +{ + /// <summary> + /// relative path from Unity project root. + /// For AssetDatabase. + /// </summary> + public struct UnityPath + { + #region UnityPath + public string Value + { + get; + private set; + } + + public override string ToString() + { + return string.Format("unity://{0}", Value); + } + + public bool IsNull + { + get { return Value == null; } + } + + public bool IsUnderAssetsFolder + { + get + { + if (IsNull) + { + return false; + } + return Value == "Assets" || Value.StartsWith("Assets/"); + } + } + + public bool IsStreamingAsset + { + get + { + if (IsNull) + { + return false; + } + + return FullPath.StartsWith(Application.streamingAssetsPath + "/"); + } + } + + public string FileName + { + get { return Path.GetFileName(Value); } + } + + public string FileNameWithoutExtension + { + get { return Path.GetFileNameWithoutExtension(Value); } + } + + public string Extension + { + get { return Path.GetExtension(Value); } + } + + public UnityPath Parent + { + get + { + if (IsNull) + { + return default(UnityPath); + } + + return new UnityPath(Path.GetDirectoryName(Value)); + } + } + + public bool HasParent + { + get + { + return !string.IsNullOrEmpty(Value); + } + } + + static readonly char[] EscapeChars = new char[] + { + '\\', + '/', + ':', + '*', + '?', + '"', + '<', + '>', + '|', + }; + + static string EscapeFilePath(string path) + { + foreach (var x in EscapeChars) + { + path = path.Replace(x, '+'); + } + return path; + } + + public UnityPath Child(string name) + { + if (IsNull) + { + throw new NotImplementedException(); + } + else if (Value == "") + { + return new UnityPath(name); + } + else + { + return new UnityPath(Value + "/" + name); + } + } + + public override int GetHashCode() + { + if (IsNull) + { + return 0; + } + return Value.GetHashCode(); + } + + public override bool Equals(object obj) + { + if(obj is UnityPath) + { + var rhs = (UnityPath)obj; + if(Value==null && rhs.Value == null) + { + return true; + } + else if (Value == null) + { + return false; + } + else if (rhs.Value == null) + { + return false; + } + else + { + return Value == rhs.Value; + } + } + else + { + return false; + } + } + + /// <summary> + /// Remove extension and add suffix + /// </summary> + /// <param name="prefabPath"></param> + /// <param name="suffix"></param> + /// <returns></returns> + public UnityPath GetAssetFolder(string suffix) + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + return new UnityPath( + string.Format("{0}/{1}{2}", + Parent.Value, + FileNameWithoutExtension, + suffix + )); + } + + UnityPath(string value) : this() + { + Value = value.Replace("\\", "/"); + } + + /// <summary> + /// + /// </summary> + /// <param name="unityPath">Relative from unity current path. GetParent(Application.dataPath)</param> + /// <returns></returns> + public static UnityPath FromUnityPath(string unityPath) + { + if (String.IsNullOrEmpty(unityPath)) + { + return new UnityPath + { + Value="" + }; + } + return FromFullpath(Path.GetFullPath(unityPath)); + } + #endregion + + #region FullPath + static string s_basePath; + static string BaseFullPath + { + get + { + if (string.IsNullOrEmpty(s_basePath)) + { + s_basePath = Path.GetFullPath(Application.dataPath + "/..").Replace("\\", "/"); + } + return s_basePath; + } + } + + static string AssetFullPath + { + get + { + return BaseFullPath + "/Assets"; + } + } + + public string FullPath + { + get + { + if (IsNull) + { + throw new NotImplementedException(); + } + return Path.Combine(BaseFullPath, Value).Replace("\\", "/"); + } + } + + public bool IsFileExists + { + get { return File.Exists(FullPath); } + } + + public bool IsDirectoryExists + { + get { return Directory.Exists(FullPath); } + } + + /// <summary> + /// + /// </summary> + /// <param name="fullPath">C:/path/to/file</param> + /// <returns></returns> + public static UnityPath FromFullpath(string fullPath) + { + if(fullPath == null) + { + fullPath = ""; + } + fullPath = fullPath.Replace("\\", "/"); + + if (fullPath == BaseFullPath) { + return new UnityPath + { + Value="" + }; + } + else if(fullPath.StartsWith(BaseFullPath + "/")) + { + return new UnityPath(fullPath.Substring(BaseFullPath.Length + 1)); + } + else + { + return default(UnityPath); + } + } + + public static bool IsUnderAssetFolder(string fullPath) + { + return fullPath.Replace("\\", "/").StartsWith(AssetFullPath); + } + #endregion + + [Obsolete("Use TraverseDir()")] + public IEnumerable<UnityPath> TravserseDir() + { + return TraverseDir(); + } + + public IEnumerable<UnityPath> TraverseDir() + { + if (IsDirectoryExists) + { + yield return this; + + foreach(var child in ChildDirs) + { + foreach(var x in child.TraverseDir()) + { + yield return x; + } + } + } + } + + public IEnumerable<UnityPath> ChildDirs + { + get + { + foreach(var x in Directory.GetDirectories(FullPath)) + { + yield return UnityPath.FromFullpath(x); + } + } + } + + public IEnumerable<UnityPath> ChildFiles + { + get + { + foreach (var x in Directory.GetFiles(FullPath)) + { + yield return UnityPath.FromFullpath(x); + } + } + } + +#if UNITY_EDITOR + public T GetImporter<T>() where T : AssetImporter + { + return AssetImporter.GetAtPath(Value) as T; + } + + public static UnityPath FromAsset(UnityEngine.Object asset) + { + return new UnityPath(AssetDatabase.GetAssetPath(asset)); + } + + public void ImportAsset() + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + AssetDatabase.ImportAsset(Value); + } + + public void EnsureFolder() + { + if (IsNull) + { + throw new NotImplementedException(); + } + + if (HasParent) + { + Parent.EnsureFolder(); + } + + if (!IsDirectoryExists) + { + var parent = Parent; + // ensure parent + parent.ImportAsset(); + // create + AssetDatabase.CreateFolder( + parent.Value, + Path.GetFileName(Value) + ); + ImportAsset(); + } + } + + public UnityEngine.Object[] GetSubAssets() + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + return AssetDatabase.LoadAllAssetsAtPath(Value); + } + + public void CreateAsset(UnityEngine.Object o) + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + AssetDatabase.CreateAsset(o, Value); + } + + public void AddObjectToAsset(UnityEngine.Object o) + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + AssetDatabase.AddObjectToAsset(o, Value); + } + + public T LoadAsset<T>() where T : UnityEngine.Object + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + return AssetDatabase.LoadAssetAtPath<T>(Value); + } + + public UnityPath GenerateUniqueAssetPath() + { + if (!IsUnderAssetsFolder) + { + throw new NotImplementedException(); + } + + return new UnityPath(AssetDatabase.GenerateUniqueAssetPath(Value)); + } + #endif + } +} diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs.meta b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs.meta new file mode 100644 index 00000000..f64923d8 --- /dev/null +++ b/Assets/ThirdParty/VRM/MeshUtility/Runtime/UnityPath.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7b7af908694806c469d62ce0b5b2f06a +timeCreated: 1532326996 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |