Ejemplo n.º 1
0
        private static void ValidateLocationBounds(GameObject gameObject)
        {
            var locationBoundsList = gameObject.GetComponentsInChildren<VCILocationBounds>();

            if (locationBoundsList == null || locationBoundsList.Length == 0)
            {
                return;
            }

            // Check 1: LocationBounds が一つのみ存在する
            if (locationBoundsList.Length >= 2)
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.LocationBoundsCountLimitOver}");
                throw new VCIValidatorException(ValidationErrorType.LocationBoundsCountLimitOver, errorText);
            }

            var locationBounds = locationBoundsList[0];
            var min = locationBounds.Bounds.min;
            var max = locationBounds.Bounds.max;

            // Check 2: x, y, z の制限範囲が ±10000 に収まる
            if (Mathf.Abs(min.x) > 10000f || Mathf.Abs(min.y) > 10000f || Mathf.Abs(min.z) > 10000f ||
                Mathf.Abs(max.x) > 10000f || Mathf.Abs(max.y) > 10000f || Mathf.Abs(max.z) > 10000f)
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.LocationBoundsValueExceeded}");
                throw new VCIValidatorException(ValidationErrorType.LocationBoundsValueExceeded, errorText);
            }

        }
Ejemplo n.º 2
0
        private static void ValidateVCIObjectComponentRestrictions(GameObject gameObject)
        {
            // Check 1: RootのGameObjectにVCIObjectがアタッチされている
            var vciObject = gameObject.GetComponent<VCIObject>();
            if (vciObject == null)
            {
                if (vciObject == null)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.VCIObjectNotAttached}");
                    throw new VCIValidatorException(ValidationErrorType.VCIObjectNotAttached, errorText);
                }
            }

            // Check 2:「VCIObject」コンポーネントがVCIの中で一つのみ存在する
            var vciObjectCount = 0;

            foreach (var transform in vciObject.transform.Traverse())
            {
                if (transform.GetComponent<VCIObject>() == null)
                {
                    continue;
                }

                vciObjectCount++;
                if (vciObjectCount > 1)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.MultipleVCIObject}");
                    throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject, errorText);
                }
            }
        }
Ejemplo n.º 3
0
        private static void ValidatePlayerSpawnPoints(GameObject gameObject)
        {
            var playerSpawnPoints = gameObject.GetComponentsInChildren<VCIPlayerSpawnPoint>();

            if (playerSpawnPoints == null)
            {
                return;
            }

            foreach (var playerSpawnPoint in playerSpawnPoints)
            {
                var spawnPointTransform = playerSpawnPoint.gameObject.transform;

                // Check 1: SpawnPoint の向きが水平である
                if (Math.Abs(spawnPointTransform.rotation.x) > 0.001f ||
                    Math.Abs(spawnPointTransform.rotation.z) > 0.001f)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.SpawnPointNotHorizontal}");
                    throw new VCIValidatorException(ValidationErrorType.SpawnPointNotHorizontal, errorText);
                }

                var spawnPointRestriction = playerSpawnPoint.GetComponent<VCIPlayerSpawnPointRestriction>();
                if (spawnPointRestriction == null) continue;

                // Check 2: SpawnPoint が PlayerSpawnPointRestriction で指定した制限範囲内に存在する
                if (spawnPointRestriction.LimitRectLeft > 0
                    || spawnPointRestriction.LimitRectRight < 0
                    || spawnPointRestriction.LimitRectForward < 0
                    || spawnPointRestriction.LimitRectBackward > 0)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.SpawnPointOriginNotInRange}");
                    throw new VCIValidatorException(ValidationErrorType.SpawnPointOriginNotInRange, errorText);
                }
            }
        }
Ejemplo n.º 4
0
        private static void CheckInvalidComponent<T>(GameObject target)
        {
            var c = target.GetComponentsInChildren<T>(true);
            if (c == null || c.Length == 0) return;

            var errorText =
                string.Format(
                    VCIConfig.GetText($"error{(int) ValidationErrorType.InvalidComponent}"),
                    typeof(T).Name);

            throw new VCIValidatorException(ValidationErrorType.InvalidComponent, errorText);
        }
Ejemplo n.º 5
0
        public static bool Validate()
        {
            // Validation
            try
            {
                var selectedGameObjects = Selection.gameObjects;
                if (selectedGameObjects.Length == 0)
                {
                    throw new VCIValidatorException(ValidationErrorType.GameObjectNotSelected);
                }

                if (2 <= selectedGameObjects.Length)
                {
                    throw new VCIValidatorException(ValidationErrorType.MultipleSelection);
                }

                var vciObject = selectedGameObjects[0].GetComponent <VCIObject>();
                if (vciObject == null)
                {
                    throw new VCIValidatorException(ValidationErrorType.VCIObjectNotAttached);
                }

                VCIValidator.ValidateVCIObject(vciObject);
            }
            catch (VCIValidatorException e)
            {
                var title = $"Error{(int)e.ErrorType}";

                var text = "";

                if (string.IsNullOrEmpty(e.Message))
                {
                    text = VCIConfig.GetText($"error{(int)e.ErrorType}");
                }
                else
                {
                    text = e.Message;
                }

                text = text.Replace("\\n", Environment.NewLine);

                if (e.ErrorType == ValidationErrorType.InvalidCharacter)
                {
                    EditorGUILayout.HelpBox(e.Message, MessageType.Warning);
                }

                EditorUtility.DisplayDialog(title, text, "OK");
                GUIUtility.ExitGUI();
                return(false);
            }
            return(true);
        }
Ejemplo n.º 6
0
        private static string ValidateField(string fieldName, string text)
        {
            var validationRule = ValidationRules[fieldName];

            if (validationRule.IsRequired && string.IsNullOrEmpty(text))
            {
                return(string.Format(VCIConfig.GetText("input"), fieldName));
            }

            if (text != null && validationRule.MaxLength < text.Length)
            {
                return(string.Format(VCIConfig.GetText("input_less_than"), fieldName, validationRule.MaxLength));
            }

            return("");
        }
Ejemplo n.º 7
0
        private static void ValidateVCIScripts(VCIObject vciObject)
        {
            if (!vciObject.Scripts.Any())
            {
                return;
            }

            // Check 1: 一つ目のスクリプトの名前が「main」である
            if (vciObject.Scripts[0].name != "main")
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.FirstScriptNameNotValid}");
                throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid, errorText);
            }

            // Check 2: 名前が空のスクリプトが存在しない
            var empties = vciObject.Scripts.Where(x => string.IsNullOrEmpty(x.name));
            if (empties.Any())
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.NoScriptName}");
                throw new VCIValidatorException(ValidationErrorType.NoScriptName, errorText);
            }

            // Check 3: 同一の名前のスクリプトが複数存在しない
            var duplicates = vciObject.Scripts.GroupBy(script => script.name)
                .Where(name => name.Count() > 1)
                .Select(group => @group.Key).ToList();
            if (duplicates.Any())
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.ScriptNameConfliction}");
                throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction, errorText);
            }

            // Check 4: スクリプト名に無効な文字列が含まれていない
            // - 無効な文字列 : ファイル名に含めることのできない文字 + '.'
            var invalidChars = Path.GetInvalidFileNameChars().Concat(new[] {'.'}).ToArray();
            foreach (var script in vciObject.Scripts)
            {
                if (script.name.IndexOfAny(invalidChars) >= 0)
                {
                    var errorText = string.Format(
                        VCIConfig.GetText($"error{(int) ValidationErrorType.InvalidCharacter}"),
                        script.name);
                    throw new VCIValidatorException(ValidationErrorType.InvalidCharacter, errorText);
                }
            }
        }
Ejemplo n.º 8
0
        // TODO: VCIのValidationに関するエラーではないので、別途エラーを定義して投げる
        // NOTE: VCIExporterMenu.Validateの処理の一部をここに切り出したため、VCIValidatorExceptionを投げる形になっている
        //       切り出したのは、この処理は厳密にはVCIのValidationに関するものではないと思ったため
        /// <summary>
        /// Editor上で選択されている唯一のGameObjectを返す
        /// GameObjectが選択されていない/複数選択されている場合はexceptionをthrowする
        /// </summary>
        /// <returns></returns>
        /// <exception cref="VCIValidatorException"></exception>
        public static GameObject GetSingleSelectedObject()
        {
            var selectedGameObjects = Selection.gameObjects;

            if (selectedGameObjects.Length == 0)
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.GameObjectNotSelected}");
                throw new VCIValidatorException(ValidationErrorType.GameObjectNotSelected, errorText);
            }

            if (2 <= selectedGameObjects.Length)
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.MultipleSelection}");
                throw new VCIValidatorException(ValidationErrorType.MultipleSelection, errorText);
            }

            return(selectedGameObjects[0]);
        }
Ejemplo n.º 9
0
        private static void ValidateAnimation(GameObject gameObject)
        {
            // NOTE: Editorコードを含んでいるため、ランタイムでValidateすることができない
            // TODO: Runtime 時にどうするかを考える
#if UNITY_EDITOR
            // Check 1: root の Animator/Animation で自身を animate していない
            // NOTE: root についてる animation の export は UniGLTF 側で行われる
            //       その時、Animator か Animation どちらか片方が一つのみアタッチされていること前提で export される
            var animationClips = new List<AnimationClip>();
            var animator = gameObject.GetComponent<Animator>();
            var animation = gameObject.GetComponent<Animation>();
            if (animator != null)
            {
                animationClips.AddRange(AnimationExporter.GetAnimationClips(animator));
            }
            else if (animation != null)
            {
                animationClips.AddRange(AnimationExporter.GetAnimationClips(animation));
            }

            if (!animationClips.Any())
            {
                return;
            }

            foreach (var animationClip in animationClips)
            {
                foreach (var animationCurveBindings in AnimationUtility.GetCurveBindings(animationClip))
                {
                    if (string.IsNullOrEmpty(animationCurveBindings.path))
                    {
                        var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootIsAnimated}");
                        throw new VCIValidatorException(ValidationErrorType.RootIsAnimated, errorText);
                    }
                }
            }
#endif
        }
Ejemplo n.º 10
0
        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            EditorGUILayout.PropertyField(_scriptProp);

            // Version
            EditorGUI.BeginDisabledGroup(true);
            if (string.IsNullOrEmpty(_target.Meta.exporterVersion))
            {
                _target.Meta.exporterVersion = VCIVersion.VERSION;
            }
            SetMetaPropertyField(_metaProp, "exporterVersion");
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.Space();

            // Information
            EditorGUILayout.LabelField("Information", EditorStyles.boldLabel);
            SetMetaPropertyField(_metaProp, "title");
            SetMetaPropertyField(_metaProp, "version");
            SetMetaPropertyField(_metaProp, "author");
            SetMetaPropertyField(_metaProp, "contactInformation");
            SetMetaPropertyField(_metaProp, "reference");

            // Thumbnail
            SetMetaPropertyField(_metaProp, "thumbnail");
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            _thumbnailProp.objectReferenceValue = (Texture2D)EditorGUILayout.ObjectField(
                _thumbnailProp.objectReferenceValue, typeof(Texture2D), false, GUILayout.Width(100),
                GUILayout.Height(100));
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.Space();

            // Description
            SetMetaPropertyField(_metaProp, "description");
            EditorGUILayout.Space();

            // License
            SetMetaPropertyField(_metaProp, "modelDataLicenseType");
            SetMetaPropertyField(_metaProp, "modelDataOtherLicenseUrl");
            EditorGUILayout.Space();

            SetMetaPropertyField(_metaProp, "scriptLicenseType");
            SetMetaPropertyField(_metaProp, "scriptOtherLicenseUrl");
            EditorGUILayout.Space();

            // Script settings
            SetMetaPropertyField(_metaProp, "scriptWriteProtected");
            SetMetaPropertyField(_metaProp, "scriptEnableDebugging");

            EditorGUILayout.Space();

            if (_target.Scripts.Any())
            {
                if (_target.Scripts[0].name != "main")
                {
                    EditorGUILayout.HelpBox("The first script must be named \"main\".", MessageType.Warning);
                }

                var empties = _target.Scripts.Where(x => string.IsNullOrEmpty(x.name));
                if (empties.Any())
                {
                    EditorGUILayout.HelpBox("Some have no script name.", MessageType.Warning);
                }

                var duplicates = _target.Scripts.GroupBy(script => script.name)
                                 .Where(n => n.Count() > 1)
                                 .Select(group => group.Key).ToList();
                if (duplicates.Any())
                {
                    EditorGUILayout.HelpBox("Duplicate script name.", MessageType.Warning);
                }

                var invalidChars = Path.GetInvalidFileNameChars().Concat(new[] { '.' }).ToArray();
                foreach (var script in _target.Scripts)
                {
                    if (script.name.IndexOfAny(invalidChars) >= 0)
                    {
                        EditorGUILayout.HelpBox("Contains characters that can not be used as scriptName. " + script.name, MessageType.Warning);
                    }
                }
            }

            // vci scripts
            EditorGUILayout.PropertyField(_vciScriptProp, true);
            serializedObject.ApplyModifiedProperties();

            EditorGUILayout.Space();

            // Export Button
            if (GUILayout.Button(VCIConfig.GetText("validate_button"), GUILayout.MinHeight(32)))
            {
                try
                {
                    var rootGameObject = GameObjectSelectionService.GetSingleSelectedObject();
                    VCIValidator.ValidateVCIRequirements(rootGameObject);
                    EditorUtility.DisplayDialog("Result", VCIConfig.GetText("no_error"), "OK");
                }
                catch (VCIValidatorException e)
                {
                    VCIValidationErrorDialog.ShowErrorDialog(e);
                    GUIUtility.ExitGUI();
                }
            }

            EditorGUILayout.Space();

            // Export Button
            if (GUILayout.Button(VCIConfig.GetText("export_button"), GUILayout.MinHeight(32)))
            {
#if UNITY_EDITOR_WIN
                VCIObjectExporterMenu.ExportObject();
#endif
            }
        }
Ejemplo n.º 11
0
        public static void ValidateVCIObject(VCIObject vo)
        {
            // VCIObject
            var vciObjectCount  = 0;
            var gameObjectCount = 0;

            foreach (var t in vo.transform.Traverse())
            {
                if (t.GetComponent <VCIObject>() != null)
                {
                    vciObjectCount++;
                }
                gameObjectCount++;
            }

            if (vciObjectCount > 1)
            {
                throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject);
            }

            // Scripts
            if (vo.Scripts.Any())
            {
                if (vo.Scripts[0].name != "main")
                {
                    throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid);
                }

                var empties = vo.Scripts.Where(x => string.IsNullOrEmpty(x.name));
                if (empties.Any())
                {
                    throw new VCIValidatorException(ValidationErrorType.NoScriptName);
                }

                var duplicates = vo.Scripts.GroupBy(script => script.name)
                                 .Where(name => name.Count() > 1)
                                 .Select(group => group.Key).ToList();
                if (duplicates.Any())
                {
                    throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction);
                }

                var invalidChars = Path.GetInvalidFileNameChars().Concat(new [] { '.' }).ToArray();
                foreach (var script in vo.Scripts)
                {
                    if (script.name.IndexOfAny(invalidChars) >= 0)
                    {
                        throw new VCIValidatorException(ValidationErrorType.InvalidCharacter,
                                                        string.Format(VCIConfig.GetText($"error{(int)ValidationErrorType.InvalidCharacter}"), script.name));
                    }
                }
                ;
            }

            VCIMetaValidator.Validate(vo);

            // Invalid Components
            CheckInvalidComponent <MeshCollider>(vo.gameObject);

            // Spring Bone
            var springBones = vo.GetComponents <VCISpringBone>();

            if (springBones != null && springBones.Length > 0)
            {
                ValidateSpringBones(springBones);
            }
        }
Ejemplo n.º 12
0
        public static void ValidateVCIObject(VCIObject vo)
        {
            // VCIObject
            var vciObjectCount        = 0;
            var gameObjectCount       = 0;
            var playerSpawnPointsList = new List <VCIPlayerSpawnPoint>();
            var locationBoundsList    = new List <VCILocationBounds>();

            foreach (var t in vo.transform.Traverse())
            {
                if (t.GetComponent <VCIObject>() != null)
                {
                    vciObjectCount++;
                }

                var psp = t.GetComponent <VCIPlayerSpawnPoint>();
                if (psp != null)
                {
                    playerSpawnPointsList.Add(psp);
                }

                var locationBounds = t.GetComponent <VCILocationBounds>();
                if (locationBounds != null)
                {
                    locationBoundsList.Add(locationBounds);
                }

                gameObjectCount++;
            }

            if (vciObjectCount > 1)
            {
                throw new VCIValidatorException(ValidationErrorType.MultipleVCIObject);
            }

            // Scripts
            if (vo.Scripts.Any())
            {
                if (vo.Scripts[0].name != "main")
                {
                    throw new VCIValidatorException(ValidationErrorType.FirstScriptNameNotValid);
                }

                var empties = vo.Scripts.Where(x => string.IsNullOrEmpty(x.name));
                if (empties.Any())
                {
                    throw new VCIValidatorException(ValidationErrorType.NoScriptName);
                }

                var duplicates = vo.Scripts.GroupBy(script => script.name)
                                 .Where(name => name.Count() > 1)
                                 .Select(group => group.Key).ToList();
                if (duplicates.Any())
                {
                    throw new VCIValidatorException(ValidationErrorType.ScriptNameConfliction);
                }

                var invalidChars = Path.GetInvalidFileNameChars().Concat(new [] { '.' }).ToArray();
                foreach (var script in vo.Scripts)
                {
                    if (script.name.IndexOfAny(invalidChars) >= 0)
                    {
                        throw new VCIValidatorException(ValidationErrorType.InvalidCharacter,
                                                        string.Format(VCIConfig.GetText($"error{(int)ValidationErrorType.InvalidCharacter}"), script.name));
                    }
                }
                ;
            }

            VCIMetaValidator.Validate(vo);

            // Invalid Components
            CheckInvalidComponent <MeshCollider>(vo.gameObject);

            // Spring Bone
            var springBones = vo.GetComponents <VCISpringBone>();

            if (springBones != null && springBones.Length > 0)
            {
                ValidateSpringBones(springBones);
            }

            // PlayerSpawnPoint
            foreach (var psp in playerSpawnPointsList)
            {
                var pspT = psp.gameObject.transform;
                if (Math.Abs(pspT.position.y) > SpawnPointAllowedHeightRange)
                {
                    throw new VCIValidatorException(ValidationErrorType.SpawnPointHeightRangeNotAllowed,
                                                    VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointHeightRangeNotAllowed}"));
                }

                if (Math.Abs(pspT.rotation.x) > 0.001f || Math.Abs(pspT.rotation.z) > 0.001f)
                {
                    throw new VCIValidatorException(ValidationErrorType.SpawnPointNotHorizontal,
                                                    VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointNotHorizontal}"));
                }

                var pspR = psp.GetComponent <VCIPlayerSpawnPointRestriction>();
                if (pspR == null)
                {
                    continue;
                }

                if (pspR.LimitRectLeft > 0 ||
                    pspR.LimitRectRight < 0 ||
                    pspR.LimitRectForward < 0 ||
                    pspR.LimitRectBackward > 0)
                {
                    throw new VCIValidatorException(ValidationErrorType.SpawnPointOriginNotInRange,
                                                    VCIConfig.GetText($"error{(int)ValidationErrorType.SpawnPointOriginNotInRange}"));
                }
            }

            // LocationBounds
            if (locationBoundsList.Count >= 2)
            {
                throw new VCIValidatorException(ValidationErrorType.LocationBoundsCountLimitOver,
                                                VCIConfig.GetText($"error{(int)ValidationErrorType.LocationBoundsCountLimitOver}"));
            }
            if (locationBoundsList.Count > 0)
            {
                var locationBounds = locationBoundsList[0];
                var min            = locationBounds.Bounds.min;
                var max            = locationBounds.Bounds.max;

                // 10000fを超えてないかだけチェック
                if (Mathf.Abs(min.x) > 10000f || Mathf.Abs(min.y) > 10000f || Mathf.Abs(min.z) > 10000f ||
                    Mathf.Abs(max.x) > 10000f || Mathf.Abs(max.y) > 10000f || Mathf.Abs(max.z) > 10000f)
                {
                    throw new VCIValidatorException(ValidationErrorType.LocationBoundsValueExceeded,
                                                    VCIConfig.GetText($"error{(int)ValidationErrorType.LocationBoundsValueExceeded}"));
                }
            }
        }
Ejemplo n.º 13
0
        private static void ValidateSpringBones(GameObject gameObject)
        {
            // NOTE: どういう歴史的経緯があったのか分からないけど、現在これらの使われていない
            // const int maxSpringBoneColliderCount = 10;
            // const int maxSphereColliderCount = 10;

            var springBones = gameObject.GetComponents<VCISpringBone>();

            if (springBones == null)
            {
                return;
            }

            // Check 1: アタッチされている SpringBone コンポーネントが maxSpringBoneCount以下である
            const int maxSpringBoneCount = 1;
            if (springBones.Length > maxSpringBoneCount)
            {
                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManySpringBone}");
                throw new VCIValidatorException(ValidationErrorType.TooManySpringBone, errorText);
            }

            const int maxRootBoneCount = 10;
            const int maxChildBoneCount = 10;

            foreach (var springBone in springBones)
            {
                var rootBones = springBone.RootBones;

                // Check 2: RootBone が存在する
                if (rootBones == null || rootBones.Count == 0)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneNotFound}");
                    throw new VCIValidatorException(ValidationErrorType.RootBoneNotFound, errorText);
                }

                // Check 3: Root の Bone の数が MaxRootBoneCount 以下である
                if (rootBones.Count > maxRootBoneCount)
                {
                    var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManyRootBone}");
                    throw new VCIValidatorException(ValidationErrorType.TooManyRootBone, errorText);
                }

                for (var i = 0; i < rootBones.Count; i++)
                {
                    if (rootBones[i] == null)
                    {
                        continue;
                    }

                    var rootBone = rootBones[i];
                    var childCount = 0;
                    foreach (var childBone in rootBone.Traverse())
                    {
                        // Check 4: SpringBone の中に SubItem が存在しない
                        if (childBone.GetComponent<VCISubItem>() != null)
                        {
                            var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneContainsSubItem}");
                            throw new VCIValidatorException(ValidationErrorType.RootBoneContainsSubItem, errorText);
                        }

                        // Check 5: RootBone の持つ ChildBone が MaxChildBoneCount 以下である
                        childCount++;
                        if (childCount > maxChildBoneCount)
                        {
                            var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.TooManyRootBoneChild}");
                            throw new VCIValidatorException(ValidationErrorType.TooManyRootBoneChild, errorText);
                        }

                        // Check 6: RootBone が入れ子になっていない
                        for (var j = 0; j < rootBones.Count; j++)
                        {
                            if (j == i)
                            {
                                continue;
                            }

                            if (rootBones[j] == childBone)
                            {
                                var errorText = VCIConfig.GetText($"error{(int) ValidationErrorType.RootBoneNested}");
                                throw new VCIValidatorException(ValidationErrorType.RootBoneNested, errorText);
                            }
                        }
                    }
                }
            }
        }