using System.Collections.Generic; using System.Linq; using UniGLTF; using UnityEngine; namespace VRM { static class VRMSpringBoneValidator { // https://github.com/vrm-c/UniVRM/issues/474 public static IEnumerable Validate(GameObject root) { if (root == null) { yield break; } var hierarchy = root.GetComponentsInChildren(true); Dictionary> rootMap = new Dictionary>(); foreach (var sb in root.GetComponentsInChildren()) { for (int i = 0; i < sb.RootBones.Count; ++i) { var springRoot = sb.RootBones[i]; if (springRoot == null) { yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is null"); continue; } if (!hierarchy.Contains(springRoot)) { yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is out of hierarchy"); continue; } if (!springRoot.transform.EnableForExport()) { yield return Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is not active"); continue; } if (!rootMap.TryGetValue(springRoot, out List list)) { list = new List(); rootMap.Add(springRoot, list); } list.Add(sb); } if (sb.ColliderGroups != null) { for (int i = 0; i < sb.ColliderGroups.Length; ++i) { var c = sb.ColliderGroups[i]; if (c == null) { yield return Validation.Error($"{sb.name}.ColliderGroups[{i}] is null"); continue; } if (!hierarchy.Contains(c.transform)) { yield return Validation.Error($"{sb.name}.ColliderGroups[{i}] is out of hierarchy"); continue; } foreach (var x in c.Validate()) { yield return x; } } } } foreach (var kv in rootMap) { if (kv.Value.Count > 1) { // * GameObjectが複数回ルートとして指定されてる(SpringBoneが同じでも別でも) var list = string.Join(", ", kv.Value.Select(x => string.IsNullOrEmpty(x.m_comment) ? x.name : x.m_comment)); yield return Validation.Warning($"{kv.Key} found multiple. {list}"); } var rootInRootMap = new Dictionary>(); foreach (var child in kv.Key.GetComponentsInChildren()) { // * Rootから子をだどって、別のRootが現れる if (child == kv.Key) { continue; } if (!rootMap.ContainsKey(child)) { continue; } if (!rootInRootMap.TryGetValue(kv.Key, out List rootInRoot)) { rootInRoot = new List(); rootInRootMap.Add(kv.Key, rootInRoot); } rootInRoot.Add(child); } foreach (var rootList in rootInRootMap) { var list = string.Join(", ", rootList.Value.Select(x => x.name)); yield return Validation.Warning($"{rootList.Key} hierarchy contains other root: {list}"); } } } } }