summaryrefslogtreecommitdiff
path: root/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2022-03-10 14:07:40 +0800
committerchai <chaifix@163.com>2022-03-10 14:07:40 +0800
commit22891bf59032ba88262824255a706d652031384b (patch)
tree7595439ba9966c9402d37e37cee5e8cf098757d5 /Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs
parent8b04ea73e540067f83870b61d89db4868fea5e8a (diff)
* move folder
Diffstat (limited to 'Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs')
-rw-r--r--Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs507
1 files changed, 0 insertions, 507 deletions
diff --git a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs b/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs
deleted file mode 100644
index b5f5231c..00000000
--- a/Assets/ThirdParty/VRM/MeshUtility/Runtime/BoneNormalizer.cs
+++ /dev/null
@@ -1,507 +0,0 @@
-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);
- }
- }
-}