diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs index 0614e9dcf..30969bba7 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExporterWizard.cs @@ -700,6 +700,7 @@ namespace VRM m_validations.Clear(); m_validations.AddRange(Validate()); + m_validations.AddRange(VRMSpringBoneValidator.Validate(ExportRoot)); var hasError = m_validations.Any(x => !x.CanExport); m_IsValid = !hasError && !MetaHasError; diff --git a/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs b/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs new file mode 100644 index 000000000..d4abef440 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +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; + } + + Dictionary> rootMap = new Dictionary>(); + + foreach (var sb in root.GetComponentsInChildren()) + { + foreach (var springRoot in sb.RootBones) + { + if (!rootMap.TryGetValue(springRoot, out List list)) + { + list = new List(); + rootMap.Add(springRoot, list); + } + list.Add(sb); + } + } + + 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}"); + } + } + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta b/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta new file mode 100644 index 000000000..1a9e37b00 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/SpringBone/VRMSpringBoneValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc588ccd280c0e2429d1d3cc3db5d950 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: