From d4326e5f70fcbf99faec52181b109be914a48ffb Mon Sep 17 00:00:00 2001 From: ousttrue Date: Wed, 8 Jul 2020 11:26:06 +0900 Subject: [PATCH] rewrite MapBoneWeight MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Transform[] srcBones から Transform[] dstBones に移し替えるのだけど、src と dst の両方に null が入りうる(その考慮が無かった) * 後で見て分かるようにコメント追加 --- .../SkinnedMeshUtility/BoneNormalizer.cs | 146 ++++++++++-------- 1 file changed, 80 insertions(+), 66 deletions(-) diff --git a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs index a9066f213..609549912 100644 --- a/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs +++ b/Assets/VRM/UniVRM/Scripts/SkinnedMeshUtility/BoneNormalizer.cs @@ -172,98 +172,110 @@ namespace VRM } } + /// + /// index が 有効であれば、setter に weight を渡す。無効であれば setter に 0 を渡す。 + /// + /// + /// + /// + /// + static bool CopyOrDropWeight(int[] indexMap, int srcIndex, float weight, Action setter) + { + var dstIndex = indexMap[srcIndex]; + if (dstIndex != -1) + { + // 有効なindex。weightをコピーする + setter(dstIndex, weight); + return true; + } + else + { + // 無効なindex。0でクリアする + setter(0, 0); + return false; + } + } + + /// + /// BoneWeight[] src から新しいボーンウェイトを作成する。 + /// + /// 変更前のBoneWeight[] + /// 新旧のボーンの対応表。新しい方は無効なボーンが除去されてnullの部分がある + /// 変更前のボーン配列 + /// 変更後のボーン配列。除去されたボーンがある場合、変更前より短い + /// static BoneWeight[] MapBoneWeight(BoneWeight[] src, Dictionary boneMap, Transform[] srcBones, Transform[] dstBones ) { - var indexMap = - srcBones - .Select((x, i) => new { i, x }) - .Select(x => - { - Transform dstBone; - if (x.x != null && boneMap.TryGetValue(x.x, out dstBone)) - { - return dstBones.IndexOf(dstBone); - } - else - { - return -1; - } - }) - .ToArray(); - + // 処理前後の index の対応表を作成する + var indexMap = new int[srcBones.Length]; for (int i = 0; i < srcBones.Length; ++i) { - if (indexMap[i] < 0) + var srcBone = srcBones[i]; + if (srcBone == null) { - 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)) { - Debug.LogWarningFormat("bones[{0}] is null", i); + // 対応するボーンが存在する + var dstIndex = dstBones.IndexOf(dstBone); + if (dstIndex == -1) + { + // ありえない。バグ + throw new Exception(); + } + indexMap[i] = dstIndex; } else { + // 先のボーンが無い + indexMap[i] = -1; Debug.LogWarningFormat("{0} is removed", srcBone.name); } } } - var dst = new BoneWeight[src.Length]; - Array.Copy(src, dst, src.Length); - + // 新しいBoneWeightを作成する + var newBoneWeights = new BoneWeight[src.Length]; for (int i = 0; i < src.Length; ++i) { - var x = src[i]; + BoneWeight srcBoneWeight = src[i]; - if (indexMap[x.boneIndex0] != -1) + // 0 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex0, srcBoneWeight.weight0, (newIndex, newWeight) => { - dst[i].boneIndex0 = indexMap[x.boneIndex0]; - dst[i].weight0 = x.weight0; - } - else if (x.weight0 > 0) + newBoneWeights[i].boneIndex0 = newIndex; + newBoneWeights[i].weight0 = newWeight; + }); + // 1 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex1, srcBoneWeight.weight1, (newIndex, newWeight) => { - Debug.LogWarningFormat("{0} weight0 to {1} is lost", i, srcBones[x.boneIndex0].name); - dst[i].weight0 = 0; - } - - if (indexMap[x.boneIndex1] != -1) + newBoneWeights[i].boneIndex1 = newIndex; + newBoneWeights[i].weight1 = newWeight; + }); + // 2 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex2, srcBoneWeight.weight2, (newIndex, newWeight) => { - dst[i].boneIndex1 = indexMap[x.boneIndex1]; - dst[i].weight1 = x.weight1; - } - else if (x.weight1 > 0) + newBoneWeights[i].boneIndex2 = newIndex; + newBoneWeights[i].weight2 = newWeight; + }); + // 3 + CopyOrDropWeight(indexMap, srcBoneWeight.boneIndex3, srcBoneWeight.weight3, (newIndex, newWeight) => { - Debug.LogWarningFormat("{0} weight0 to {1} is lost", i, srcBones[x.boneIndex1].name); - dst[i].weight1 = 0; - } - - if (indexMap[x.boneIndex2] != -1) - { - dst[i].boneIndex2 = indexMap[x.boneIndex2]; - dst[i].weight2 = x.weight2; - } - else if (x.weight2 > 0) - { - Debug.LogWarningFormat("{0} weight0 to {1} is lost", i, srcBones[x.boneIndex2].name); - dst[i].weight2 = 0; - } - - if (indexMap[x.boneIndex3] != -1) - { - dst[i].boneIndex3 = indexMap[x.boneIndex3]; - dst[i].weight3 = x.weight3; - } - else if (x.weight3 > 0) - { - Debug.LogWarningFormat("{0} weight0 to {1} is lost", i, srcBones[x.boneIndex3].name); - dst[i].weight3 = 0; - } + newBoneWeights[i].boneIndex3 = newIndex; + newBoneWeights[i].weight3 = newWeight; + }); } - return dst; + return newBoneWeights; } /// @@ -296,6 +308,7 @@ namespace VRM } } + // 元の Transform[] bones から、無効なboneを取り除いて前に詰めた配列を作る var dstBones = srcRenderer.bones .Where(x => x != null && boneMap.ContainsKey(x)) .Select(x => boneMap[x]) @@ -340,7 +353,8 @@ namespace VRM if (val > 0) blendShapeValues.Add(i, val); } - mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, srcRenderer.bones, dstBones); // restore weights. clear when BakeMesh + // 新しい骨格のボーンウェイトを作成する + mesh.boneWeights = MapBoneWeight(srcMesh.boneWeights, boneMap, srcRenderer.bones, dstBones); // recalc bindposes mesh.bindposes = dstBones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray();