using System.Collections.Generic; using nadena.dev.modular_avatar.core; using nadena.dev.ndmf; #if MA_VRCSDK3_AVATARS using nadena.dev.modular_avatar.core.menu; #endif using UnityEngine; namespace nadena.dev.modular_avatar.editor.ErrorReporting { internal static class ComponentValidation { /// /// Validates the provided tag component. /// /// /// Null if valid, otherwise a list of configuration errors internal static void CheckComponent(this AvatarTagComponent tagComponent) { ErrorReport.WithContextObject(tagComponent, () => { switch (tagComponent) { case ModularAvatarBlendshapeSync bs: CheckInternal(bs); break; case ModularAvatarBoneProxy bp: CheckInternal(bp); break; #if MA_VRCSDK3_AVATARS case ModularAvatarMenuInstaller mi: CheckInternal(mi); break; case ModularAvatarMergeAnimator obj: CheckInternal(obj); break; #endif case ModularAvatarMergeArmature obj: CheckInternal(obj); break; default: return; } }); } internal static void ValidateAll(GameObject root) { foreach (var component in root.GetComponentsInChildren(true)) { component.CheckComponent(); } } private static void CheckInternal(ModularAvatarBlendshapeSync bs) { var localMesh = bs.GetComponent(); if (localMesh == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_local_renderer", bs); } if (localMesh.sharedMesh == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_local_mesh", bs); } if (bs.Bindings == null || bs.Bindings.Count == 0) { BuildReport.Log(ErrorSeverity.Information,"validation.blendshape_sync.no_bindings", bs); } foreach (var binding in bs.Bindings) { var localShape = string.IsNullOrWhiteSpace(binding.LocalBlendshape) ? binding.Blendshape : binding.LocalBlendshape; if (localMesh.sharedMesh.GetBlendShapeIndex(localShape) == -1) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_local_shape", localShape, bs); } var targetObj = binding.ReferenceMesh.Get(bs.transform); if (targetObj == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_target", bs); continue; } var targetRenderer = targetObj.GetComponent(); if (targetRenderer == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_target_renderer", bs, targetRenderer); continue; } var targetMesh = targetRenderer.sharedMesh; if (targetMesh == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_target_mesh", bs, targetRenderer); continue; } if (targetMesh.GetBlendShapeIndex(binding.Blendshape) == -1) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_target_shape", binding.Blendshape, bs, targetRenderer); } } } private static void CheckInternal(ModularAvatarBoneProxy bp) { if (bp.target == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.bone_proxy.no_target", bp); } } #if MA_VRCSDK3_AVATARS private static void CheckInternal(ModularAvatarMenuInstaller mi) { // TODO - check that target menu is in the avatar if (mi.menuToAppend == null && mi.GetComponent() == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.menu_installer.no_menu", mi); } } private static void CheckInternal(ModularAvatarMergeAnimator ma) { if (ma.animator == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.merge_animator.no_animator", ma); } } #endif private static void CheckInternal(ModularAvatarMergeArmature ma) { if (ma.mergeTargetObject == null) { BuildReport.Log(ErrorSeverity.NonFatal, "validation.merge_armature.no_target", ma); return; } if (ma.mergeTargetObject == ma.gameObject || ma.mergeTargetObject.transform.IsChildOf(ma.transform)) { BuildReport.Log(ErrorSeverity.Error, "error.merge_armature.circular_dependency", ma, ma.mergeTargetObject); } } } }