// Updates a layer value, need to do this cause the CustomAnimLayer is a struct and not a class.
 public void UpdateLayer(int index, VRCAvatarDescriptor.CustomAnimLayer layer)
 {
     if (index >= _avatar.baseAnimationLayers.Length)
     {
         _avatar.specialAnimationLayers[index - _avatar.baseAnimationLayers.Length] = layer;
     }
     else
     {
         _avatar.baseAnimationLayers[index] = layer;
     }
 }
        public AvatarDefinition(VRCAvatarDescriptor descriptor)
        {
            Name                    = descriptor.name;
            AvatarDescriptor        = descriptor;
            VrcExpressionParameters = AvatarDescriptor.expressionParameters;

            if (VrcExpressionParameters != null)
            {
                foreach (var parameter in VrcExpressionParameters.parameters)
                {
                    AddParameter(parameter);
                }
            }

            if (descriptor.expressionsMenu != null)
            {
                AddMenu(descriptor.expressionsMenu);
            }

            var animators = descriptor.baseAnimationLayers;

            for (var i = 0; i < animators.Length; i++)
            {
                VRCAvatarDescriptor.CustomAnimLayer animLayer = animators[i];
                if (animLayer.isDefault)
                {
                    continue;
                }

                AnimatorDefinition.AnimatorType type = AnimatorDefinition.AnimatorType.Action;
                switch (i)
                {
                case 0: type = AnimatorDefinition.AnimatorType.Action; break;

                case 1: type = AnimatorDefinition.AnimatorType.Additive; break;

                case 2: type = AnimatorDefinition.AnimatorType.Base; break;

                case 3: type = AnimatorDefinition.AnimatorType.Gesture; break;

                case 4: type = AnimatorDefinition.AnimatorType.FX; break;
                }

                AddAnimator(animLayer.animatorController as AnimatorController, type);
            }
        }
        public static void SetAnimationLayer(this VRCAvatarDescriptor avatar, VRCAvatarDescriptor.AnimLayerType layer, VRCAvatarDescriptor.CustomAnimLayer animation)
        {
            switch (layer)
            {
            case VRCAvatarDescriptor.AnimLayerType.Base:
            case VRCAvatarDescriptor.AnimLayerType.Additive:
            case VRCAvatarDescriptor.AnimLayerType.Gesture:
            case VRCAvatarDescriptor.AnimLayerType.Action:
            case VRCAvatarDescriptor.AnimLayerType.FX:
                var(_, bIndex) = avatar.baseAnimationLayers.Select((w, i) => (Value: w, Index: i)).First(w => w.Value.type == layer);
                avatar.baseAnimationLayers[bIndex] = animation;
                break;

            case VRCAvatarDescriptor.AnimLayerType.SpecialIK:
            case VRCAvatarDescriptor.AnimLayerType.Sitting:
            case VRCAvatarDescriptor.AnimLayerType.TPose:
            case VRCAvatarDescriptor.AnimLayerType.IKPose:
                var(_, sIndex) = avatar.specialAnimationLayers.Select((w, i) => (Value: w, Index: i)).First(w => w.Value.type == layer);
                avatar.specialAnimationLayers[sIndex] = animation;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(layer), layer, null);
            }
        }
Example #4
0
        public void GenerateInventory()
        {
            string generatedDirPath = $"Assets/Merlin/Inventory/_generated/{descriptorGUID}";

            if (!Directory.Exists(generatedDirPath))
            {
                Directory.CreateDirectory(generatedDirPath);
            }

            // Generate the stage parameters for the inventory toggles
            VRCExpressionParameters inventoryStageParams;
            string stageParameterPath = $"{generatedDirPath}/customStageParams.asset";

            if (basisStageParameters != null)
            {
                AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisStageParameters), stageParameterPath);
                inventoryStageParams = AssetDatabase.LoadAssetAtPath <VRCExpressionParameters>(stageParameterPath);
            }
            else
            {
                inventoryStageParams = ScriptableObject.CreateInstance <VRCExpressionParameters>();
                AssetDatabase.CreateAsset(inventoryStageParams, $"{generatedDirPath}/customStageParams.asset");
            }

            List <VRCExpressionParameters.Parameter> originalParams = new List <VRCExpressionParameters.Parameter>();

            if (inventoryStageParams.parameters != null)
            {
                foreach (VRCExpressionParameters.Parameter param in inventoryStageParams.parameters)
                {
                    if (!string.IsNullOrEmpty(param.name))
                    {
                        originalParams.Add(new VRCExpressionParameters.Parameter()
                        {
                            name = param.name, valueType = param.valueType
                        });
                    }
                }
            }

            if (inventorySlots.Length + originalParams.Count > 128)
            {
                Debug.LogError($"Cannot have more than {128 - originalParams.Count} inventory slots");
                return;
            }

            VRCExpressionParameters.Parameter[] basisParameters = inventoryStageParams.parameters;
            inventoryStageParams.parameters = new VRCExpressionParameters.Parameter[inventorySlots.Length];

            for (int i = 0; i < originalParams.Count; ++i)
            {
                inventoryStageParams.parameters[i] = originalParams[i];
            }

            for (int i = originalParams.Count; i < inventorySlots.Length + originalParams.Count; ++i)
            {
                inventoryStageParams.parameters[i] = new VRCExpressionParameters.Parameter()
                {
                    name = $"GenInventorySlot{i - originalParams.Count}", valueType = VRCExpressionParameters.ValueType.Bool
                }
            }
            ;

            // for (int i = originalParams.Count + inventorySlots.Length; i < 16; ++i) // Clear out empty params
            //     inventoryStageParams.parameters[i] = new VRCExpressionParameters.Parameter() { name = "", valueType = VRCExpressionParameters.ValueType.Float };

            // Generate menu asset
            for (int menuNum = 0; menuNum < 8; ++menuNum)
            {
                VRCExpressionsMenu menuAsset;
                string             menuPath = $"{generatedDirPath}/expressionMenu{menuNum}.asset";
                if (basisMenu)
                {
                    AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisMenu), menuPath);
                    menuAsset = AssetDatabase.LoadAssetAtPath <VRCExpressionsMenu>(menuPath);
                }
                else
                {
                    menuAsset = ScriptableObject.CreateInstance <VRCExpressionsMenu>();
                    AssetDatabase.CreateAsset(menuAsset, menuPath);
                }

                int offset = menuNum * 8;
                if (offset >= inventorySlots.Length)
                {
                    break;
                }

                for (int i = 0; i < 8; ++i)
                {
                    if (i + offset >= inventorySlots.Length)
                    {
                        break;
                    }
                    menuAsset.controls.Add(new VRCExpressionsMenu.Control()
                    {
                        icon      = inventorySlots[i + offset].slotIcon,
                        name      = inventorySlots[i + offset].slotName,
                        parameter = new VRCExpressionsMenu.Control.Parameter()
                        {
                            name = $"GenInventorySlot{i+offset}"
                        },
                        type  = VRCExpressionsMenu.Control.ControlType.Toggle,
                        value = 1,
                    });
                }
            }

            // Generate controller
            AnimatorController controller;
            string             controllerPath = $"{generatedDirPath}/inventoryController.controller";

            if (basisAnimator)
            {
                AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisAnimator), controllerPath);

                controller = AssetDatabase.LoadAssetAtPath <AnimatorController>(controllerPath);
            }
            else
            {
                controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
            }

            AnimationClip[] inventoryClips      = new AnimationClip[inventorySlots.Length];
            AnimationClip[] inventoryResetClips = new AnimationClip[inventorySlots.Length];

            // Generate layer mask
            AvatarMask maskEverything = new AvatarMask();

            for (int i = 0; i < (int)AvatarMaskBodyPart.LastBodyPart; ++i)
            {
                maskEverything.SetHumanoidBodyPartActive((AvatarMaskBodyPart)i, false);
            }

            maskEverything.name = "maskEverythingMask";
            AssetDatabase.AddObjectToAsset(maskEverything, controller);

            // Generate animation clips
            for (int i = 0; i < inventorySlots.Length; ++i)
            {
                InventorySlot slot = inventorySlots[i];

                // Set initial object state
                // foreach (GameObject toggleObject in slot.slotToggleItems)
                //     if (toggleObject)
                //         toggleObject.SetActive(slot.startEnabled);

                string        animationClipPath = $"{generatedDirPath}/Animations/_toggle{i}.anim";
                AnimationClip toggleClip        = GenerateToggleClip(slot.slotToggleItems, !slot.startEnabled);
                AnimationClip resetClip         = GenerateToggleClip(slot.slotToggleItems, slot.startEnabled);

                //AssetDatabase.CreateAsset(toggleClip, animationClipPath);

                inventoryClips[i]      = toggleClip;
                inventoryResetClips[i] = resetClip;

                toggleClip.name = $"toggleAnim{i}";
                AssetDatabase.AddObjectToAsset(toggleClip, controller);

                resetClip.name = $"resetAnim{i}";
                AssetDatabase.AddObjectToAsset(resetClip, controller);
            }

            // Generate controller layers
            for (int i = 0; i < inventorySlots.Length; ++i)
            {
                string paramName = $"GenInventorySlot{i}";
                controller.AddParameter(paramName, AnimatorControllerParameterType.Bool);

                string layerName = $"GenToggleLayer{i}";

                AnimatorControllerLayer toggleLayer = new AnimatorControllerLayer();
                toggleLayer.name                   = layerName;
                toggleLayer.defaultWeight          = 1f;
                toggleLayer.stateMachine           = new AnimatorStateMachine();
                toggleLayer.stateMachine.name      = toggleLayer.name;
                toggleLayer.stateMachine.hideFlags = HideFlags.HideInHierarchy;
                toggleLayer.avatarMask             = maskEverything;

                if (AssetDatabase.GetAssetPath(controller) != "")
                {
                    AssetDatabase.AddObjectToAsset(toggleLayer.stateMachine, AssetDatabase.GetAssetPath(controller));
                }

                AnimatorStateMachine stateMachine = toggleLayer.stateMachine;

                AnimatorState nullState   = stateMachine.AddState("Null State", stateMachine.entryPosition + new Vector3(200f, 0f));
                AnimatorState toggleState = stateMachine.AddState("Toggle Triggered", stateMachine.entryPosition + new Vector3(500f, 0f));
                nullState.motion   = inventoryResetClips[i];
                toggleState.motion = inventoryClips[i];

                AnimatorStateTransition toToggle = nullState.AddTransition(toggleState);
                toToggle.exitTime         = 0f;
                toToggle.hasExitTime      = false;
                toToggle.hasFixedDuration = true;
                toToggle.duration         = 0f;

                AnimatorStateTransition toNull = toggleState.AddTransition(nullState);
                toNull.exitTime         = 0f;
                toNull.hasExitTime      = false;
                toNull.hasFixedDuration = true;
                toNull.duration         = 0f;

                toToggle.AddCondition(AnimatorConditionMode.If, 0f, paramName);
                toNull.AddCondition(AnimatorConditionMode.IfNot, 0f, paramName);

                controller.AddLayer(toggleLayer);
            }

            // Setup layers on the avatar descriptor
            VRCAvatarDescriptor descriptor = GetComponent <VRCAvatarDescriptor>();

            // descriptor.expressionsMenu = menuAsset;
            descriptor.expressionParameters = inventoryStageParams;

            VRCAvatarDescriptor.CustomAnimLayer layer = new VRCAvatarDescriptor.CustomAnimLayer();
            layer.isDefault          = false;
            layer.animatorController = controller;
            layer.type = inventoryAnimLayer;

            for (int i = 0; i < descriptor.baseAnimationLayers.Length; ++i)
            {
                if (descriptor.baseAnimationLayers[i].type == inventoryAnimLayer)
                {
                    descriptor.baseAnimationLayers[i] = layer;
                    break;
                }
            }

            AssetDatabase.SaveAssets();
        }
Example #5
0
 // Updates a layer value, need to do this cause the CustomAnimLayer is a struct and not a class.
 public void UpdateLayer(int index, VRCAvatarDescriptor.CustomAnimLayer layer)
 {
     _avatar.baseAnimationLayers[index] = layer;
 }
        public override void ValidateFeatures(VRC_AvatarDescriptor avatar, Animator anim, AvatarPerformanceStats perfStats)
        {
            //Create avatar debug hashset
            VRCAvatarDescriptor avatarSDK3 = avatar as VRCAvatarDescriptor;

            if (avatarSDK3 != null)
            {
                avatarSDK3.animationHashSet.Clear();

                foreach (VRCAvatarDescriptor.CustomAnimLayer animLayer in avatarSDK3.baseAnimationLayers)
                {
                    AnimatorController controller = animLayer.animatorController as AnimatorController;
                    if (controller != null)
                    {
                        foreach (AnimatorControllerLayer layer in controller.layers)
                        {
                            ProcessStateMachine(layer.stateMachine, "");
                            void ProcessStateMachine(AnimatorStateMachine stateMachine, string prefix)
                            {
                                //Update prefix
                                prefix = prefix + stateMachine.name + ".";

                                //States
                                foreach (var state in stateMachine.states)
                                {
                                    VRCAvatarDescriptor.DebugHash hash = new VRCAvatarDescriptor.DebugHash();
                                    string fullName = prefix + state.state.name;
                                    hash.hash = Animator.StringToHash(fullName);
                                    hash.name = fullName.Remove(0, layer.stateMachine.name.Length + 1);
                                    avatarSDK3.animationHashSet.Add(hash);
                                }

                                //Sub State Machines
                                foreach (var subMachine in stateMachine.stateMachines)
                                {
                                    ProcessStateMachine(subMachine.stateMachine, prefix);
                                }
                            }
                        }
                    }
                }
            }

            //Validate Playable Layers
            if (avatarSDK3 != null && avatarSDK3.customizeAnimationLayers)
            {
                VRCAvatarDescriptor.CustomAnimLayer gestureLayer = avatarSDK3.baseAnimationLayers[2];
                if (anim != null &&
                    anim.isHuman &&
                    gestureLayer.animatorController != null &&
                    gestureLayer.type == VRCAvatarDescriptor.AnimLayerType.Gesture &&
                    !gestureLayer.isDefault)
                {
                    AnimatorController controller = gestureLayer.animatorController as AnimatorController;
                    if (controller != null && controller.layers[0].avatarMask == null)
                    {
                        _builder.OnGUIError(avatar, "Gesture Layer needs valid mask on first animator layer",
                                            delegate { OpenAnimatorControllerWindow(controller); }, null);
                    }
                }
            }

            //Expression menu images
            if (avatarSDK3 != null)
            {
                bool ValidateTexture(Texture2D texture)
                {
                    string          path     = AssetDatabase.GetAssetPath(texture);
                    TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;

                    if (importer == null)
                    {
                        return(true);
                    }
                    TextureImporterPlatformSettings settings = importer.GetDefaultPlatformTextureSettings();

                    //Max texture size
                    if ((texture.width > MAX_ACTION_TEXTURE_SIZE || texture.height > MAX_ACTION_TEXTURE_SIZE) &&
                        settings.maxTextureSize > MAX_ACTION_TEXTURE_SIZE)
                    {
                        return(false);
                    }

                    //Compression
                    if (settings.textureCompression == TextureImporterCompression.Uncompressed)
                    {
                        return(false);
                    }

                    //Success
                    return(true);
                }

                void FixTexture(Texture2D texture)
                {
                    string          path     = AssetDatabase.GetAssetPath(texture);
                    TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;

                    if (importer == null)
                    {
                        return;
                    }
                    TextureImporterPlatformSettings settings = importer.GetDefaultPlatformTextureSettings();

                    //Max texture size
                    if (texture.width > MAX_ACTION_TEXTURE_SIZE || texture.height > MAX_ACTION_TEXTURE_SIZE)
                    {
                        settings.maxTextureSize = Math.Min(settings.maxTextureSize, MAX_ACTION_TEXTURE_SIZE);
                    }

                    //Compression
                    if (settings.textureCompression == TextureImporterCompression.Uncompressed)
                    {
                        settings.textureCompression = TextureImporterCompression.Compressed;
                    }

                    //Set & Reimport
                    importer.SetPlatformTextureSettings(settings);
                    AssetDatabase.ImportAsset(path);
                }

                //Find all textures
                List <Texture2D>          textures  = new List <Texture2D>();
                List <VRCExpressionsMenu> menuStack = new List <VRCExpressionsMenu>();
                FindTextures(avatarSDK3.expressionsMenu);

                void FindTextures(VRCExpressionsMenu menu)
                {
                    if (menu == null || menuStack.Contains(menu)) //Prevent recursive menu searching
                    {
                        return;
                    }
                    menuStack.Add(menu);

                    //Check controls
                    foreach (VRCExpressionsMenu.Control control in menu.controls)
                    {
                        AddTexture(control.icon);
                        if (control.labels != null)
                        {
                            foreach (VRCExpressionsMenu.Control.Label label in control.labels)
                            {
                                AddTexture(label.icon);
                            }
                        }

                        if (control.subMenu != null)
                        {
                            FindTextures(control.subMenu);
                        }
                    }

                    void AddTexture(Texture2D texture)
                    {
                        if (texture != null)
                        {
                            textures.Add(texture);
                        }
                    }
                }

                //Validate
                bool isValid = true;
                foreach (Texture2D texture in textures)
                {
                    if (!ValidateTexture(texture))
                    {
                        isValid = false;
                    }
                }

                if (!isValid)
                {
                    _builder.OnGUIError(avatar, "Images used for Actions & Moods are too large.",
                                        delegate { Selection.activeObject = avatar.gameObject; }, FixTextures);
                }

                //Fix
                void FixTextures()
                {
                    foreach (Texture2D texture in textures)
                    {
                        FixTexture(texture);
                    }
                }
            }

            //Expression menu parameters
            if (avatarSDK3 != null)
            {
                //Check for expression menu/parameters object
                if (avatarSDK3.expressionsMenu != null || avatarSDK3.expressionParameters != null)
                {
                    //Menu
                    if (avatarSDK3.expressionsMenu == null)
                    {
                        _builder.OnGUIError(avatar, "VRCExpressionsMenu object reference is missing.",
                                            delegate { Selection.activeObject = avatarSDK3; }, null);
                    }

                    //Parameters
                    if (avatarSDK3.expressionParameters == null)
                    {
                        _builder.OnGUIError(avatar, "VRCExpressionParameters object reference is missing.",
                                            delegate { Selection.activeObject = avatarSDK3; }, null);
                    }
                }

                //Check if parameters is valid
                if (avatarSDK3.expressionParameters != null && avatarSDK3.expressionParameters.CalcTotalCost() > VRCExpressionParameters.MAX_PARAMETER_COST)
                {
                    _builder.OnGUIError(avatar, "VRCExpressionParameters has too many parameters defined.",
                                        delegate { Selection.activeObject = avatarSDK3.expressionParameters; }, null);
                }

                //Find all existing parameters
                if (avatarSDK3.expressionsMenu != null && avatarSDK3.expressionParameters != null)
                {
                    List <VRCExpressionsMenu> menuStack  = new List <VRCExpressionsMenu>();
                    List <string>             parameters = new List <string>();
                    List <VRCExpressionsMenu> selects    = new List <VRCExpressionsMenu>();
                    FindParameters(avatarSDK3.expressionsMenu);

                    void FindParameters(VRCExpressionsMenu menu)
                    {
                        if (menu == null || menuStack.Contains(menu)) //Prevent recursive menu searching
                        {
                            return;
                        }
                        menuStack.Add(menu);

                        //Check controls
                        foreach (VRCExpressionsMenu.Control control in menu.controls)
                        {
                            AddParameter(control.parameter);
                            if (control.subParameters != null)
                            {
                                foreach (VRCExpressionsMenu.Control.Parameter subParameter in control.subParameters)
                                {
                                    AddParameter(subParameter);
                                }
                            }

                            if (control.subMenu != null)
                            {
                                FindParameters(control.subMenu);
                            }
                        }

                        void AddParameter(VRCExpressionsMenu.Control.Parameter parameter)
                        {
                            if (parameter != null)
                            {
                                parameters.Add(parameter.name);
                                selects.Add(menu);
                            }
                        }
                    }

                    //Validate parameters
                    for (int i = 0; i < parameters.Count; i++)
                    {
                        string             parameter = parameters[i];
                        VRCExpressionsMenu select    = selects[i];

                        //Find
                        bool exists = string.IsNullOrEmpty(parameter) || avatarSDK3.expressionParameters.FindParameter(parameter) != null;
                        if (!exists)
                        {
                            _builder.OnGUIError(avatar,
                                                "VRCExpressionsMenu uses a parameter that is not defined.\nParameter: " + parameter,
                                                delegate { Selection.activeObject = select; }, null);
                        }
                    }

                    //Validate param choices
                    foreach (var menu in menuStack)
                    {
                        foreach (var control in menu.controls)
                        {
                            bool isValid = true;
                            if (control.type == VRCExpressionsMenu.Control.ControlType.FourAxisPuppet)
                            {
                                isValid &= ValidateNonBoolParam(control.subParameters[0].name);
                                isValid &= ValidateNonBoolParam(control.subParameters[1].name);
                                isValid &= ValidateNonBoolParam(control.subParameters[2].name);
                                isValid &= ValidateNonBoolParam(control.subParameters[3].name);
                            }
                            else if (control.type == VRCExpressionsMenu.Control.ControlType.RadialPuppet)
                            {
                                isValid &= ValidateNonBoolParam(control.subParameters[0].name);
                            }
                            else if (control.type == VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet)
                            {
                                isValid &= ValidateNonBoolParam(control.subParameters[0].name);
                                isValid &= ValidateNonBoolParam(control.subParameters[1].name);
                            }
                            if (!isValid)
                            {
                                _builder.OnGUIError(avatar,
                                                    "VRCExpressionsMenu uses an invalid parameter for a control.\nControl: " + control.name,
                                                    delegate { Selection.activeObject = menu; }, null);
                            }
                        }

                        bool ValidateNonBoolParam(string name)
                        {
                            VRCExpressionParameters.Parameter param = string.IsNullOrEmpty(name) ? null : avatarSDK3.expressionParameters.FindParameter(name);
                            if (param != null && param.valueType == VRCExpressionParameters.ValueType.Bool)
                            {
                                return(false);
                            }
                            return(true);
                        }
                    }
                }
            }

            IEnumerable <Component> componentsToRemove      = AvatarValidation.FindIllegalComponents(avatar.gameObject);
            HashSet <string>        componentsToRemoveNames = new HashSet <string>();
            IEnumerable <Component> toRemove = componentsToRemove as Component[] ?? componentsToRemove.ToArray();

            foreach (Component c in toRemove)
            {
                if (componentsToRemoveNames.Contains(c.GetType().Name) == false)
                {
                    componentsToRemoveNames.Add(c.GetType().Name);
                }
            }

            if (componentsToRemoveNames.Count > 0)
            {
                _builder.OnGUIError(avatar,
                                    "The following component types are found on the Avatar and will be removed by the client: " +
                                    string.Join(", ", componentsToRemoveNames.ToArray()),
                                    delegate { ShowRestrictedComponents(toRemove); },
                                    delegate { FixRestrictedComponents(toRemove); });
            }

            List <AudioSource> audioSources =
                avatar.gameObject.GetComponentsInChildren <AudioSource>(true).ToList();

            if (audioSources.Count > 0)
            {
                _builder.OnGUIWarning(avatar,
                                      "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.",
                                      GetAvatarSubSelectAction(avatar, typeof(AudioSource)), null);
            }

            List <VRCStation> stations =
                avatar.gameObject.GetComponentsInChildren <VRCStation>(true).ToList();

            if (stations.Count > 0)
            {
                _builder.OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary.",
                                      GetAvatarSubSelectAction(avatar, typeof(VRCStation)), null);
            }

            if (VRCSdkControlPanel.HasSubstances(avatar.gameObject))
            {
                _builder.OnGUIWarning(avatar,
                                      "This avatar has one or more Substance materials, which is not supported and may break in-game. Please bake your Substances to regular materials.",
                                      () => { Selection.objects = VRCSdkControlPanel.GetSubstanceObjects(avatar.gameObject); },
                                      null);
            }

            CheckAvatarMeshesForLegacyBlendShapesSetting(avatar);

#if UNITY_ANDROID
            IEnumerable <Shader> illegalShaders = AvatarValidation.FindIllegalShaders(avatar.gameObject);
            foreach (Shader s in illegalShaders)
            {
                _builder.OnGUIError(avatar, "Avatar uses unsupported shader '" + s.name + "'. You can only use the shaders provided in 'VRChat/Mobile' for Quest avatars.", delegate() { Selection.activeObject
                                                                                                                                                                                             = avatar.gameObject; }, null);
            }
#endif

            foreach (AvatarPerformanceCategory perfCategory in Enum.GetValues(typeof(AvatarPerformanceCategory)))
            {
                if (perfCategory == AvatarPerformanceCategory.Overall ||
                    perfCategory == AvatarPerformanceCategory.PolyCount ||
                    perfCategory == AvatarPerformanceCategory.AABB ||
                    perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
                {
                    continue;
                }

                Action show = null;

                switch (perfCategory)
                {
                case AvatarPerformanceCategory.AnimatorCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(Animator));
                    break;

                case AvatarPerformanceCategory.AudioSourceCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(AudioSource));
                    break;

                case AvatarPerformanceCategory.BoneCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer));
                    break;

                case AvatarPerformanceCategory.ClothCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(Cloth));
                    break;

                case AvatarPerformanceCategory.ClothMaxVertices:
                    show = GetAvatarSubSelectAction(avatar, typeof(Cloth));
                    break;

                case AvatarPerformanceCategory.LightCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(Light));
                    break;

                case AvatarPerformanceCategory.LineRendererCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(LineRenderer));
                    break;

                case AvatarPerformanceCategory.MaterialCount:
                    show = GetAvatarSubSelectAction(avatar,
                                                    new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) });
                    break;

                case AvatarPerformanceCategory.MeshCount:
                    show = GetAvatarSubSelectAction(avatar,
                                                    new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) });
                    break;

                case AvatarPerformanceCategory.ParticleCollisionEnabled:
                    show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem));
                    break;

                case AvatarPerformanceCategory.ParticleMaxMeshPolyCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem));
                    break;

                case AvatarPerformanceCategory.ParticleSystemCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem));
                    break;

                case AvatarPerformanceCategory.ParticleTotalCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem));
                    break;

                case AvatarPerformanceCategory.ParticleTrailsEnabled:
                    show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem));
                    break;

                case AvatarPerformanceCategory.PhysicsColliderCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(Collider));
                    break;

                case AvatarPerformanceCategory.PhysicsRigidbodyCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(Rigidbody));
                    break;

                case AvatarPerformanceCategory.PolyCount:
                    show = GetAvatarSubSelectAction(avatar,
                                                    new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) });
                    break;

                case AvatarPerformanceCategory.SkinnedMeshCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer));
                    break;

                case AvatarPerformanceCategory.TrailRendererCount:
                    show = GetAvatarSubSelectAction(avatar, typeof(TrailRenderer));
                    break;
                }

                // we can only show these buttons if DynamicBone is installed

                Type dynamicBoneType         = typeof(AvatarValidation).Assembly.GetType("DynamicBone");
                Type dynamicBoneColliderType = typeof(AvatarValidation).Assembly.GetType("DynamicBoneCollider");
                if ((dynamicBoneType != null) && (dynamicBoneColliderType != null))
                {
                    switch (perfCategory)
                    {
                    case AvatarPerformanceCategory.DynamicBoneColliderCount:
                        show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType);
                        break;

                    case AvatarPerformanceCategory.DynamicBoneCollisionCheckCount:
                        show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType);
                        break;

                    case AvatarPerformanceCategory.DynamicBoneComponentCount:
                        show = GetAvatarSubSelectAction(avatar, dynamicBoneType);
                        break;

                    case AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount:
                        show = GetAvatarSubSelectAction(avatar, dynamicBoneType);
                        break;
                    }
                }

                OnGUIPerformanceInfo(avatar, perfStats, perfCategory, show, null);
            }

            _builder.OnGUILink(avatar, "Avatar Optimization Tips", VRCSdkControlPanel.AVATAR_OPTIMIZATION_TIPS_URL);
        }
Example #7
0
 public static int LayerSort(VRCAvatarDescriptor.CustomAnimLayer x, VRCAvatarDescriptor.CustomAnimLayer y) => SortValue[x.type] - SortValue[y.type];
        public void Inspector_Body()
        {
            //Avatar
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUI.indentLevel += 1;
            {
                script.avatarDescriptor = (VRCAvatarDescriptor)EditorGUILayout.ObjectField("Avatar", script.avatarDescriptor, typeof(VRCAvatarDescriptor), true);
            }
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.EndVertical();

            //Menu Actions
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUI.indentLevel += 1;
            {
                EditorGUILayout.BeginHorizontal();
                script.menuActions = (MenuActions)EditorGUILayout.ObjectField("Menu Actions", script.menuActions, typeof(MenuActions), false);
                EditorGUILayout.EndHorizontal();
            }
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.EndVertical();

            //Non-Menu Actions
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUI.indentLevel += 1;
            EditorGUILayout.LabelField("Other Actions");
            {
                if (GUILayout.Button("Add"))
                {
                    script.otherActions.Add(null);
                }
                for (int i = 0; i < script.otherActions.Count; i++)
                {
                    EditorGUILayout.BeginHorizontal();
                    {
                        //Reference
                        script.otherActions[i] = (NonMenuActions)EditorGUILayout.ObjectField("Actions", script.otherActions[i], typeof(NonMenuActions), false);

                        //Delete
                        if (GUILayout.Button("X", GUILayout.Width(32)))
                        {
                            script.otherActions.RemoveAt(i);
                            i -= 1;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                }
            }
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.EndVertical();

            EditorBase.Divider();

            //Build
            EditorGUI.BeginDisabledGroup(script.ReturnAnyScriptableObject() == null || script.avatarDescriptor == null);
            if (GUILayout.Button("Build Avatar Data", GUILayout.Height(32)))
            {
                BaseActions.BuildAvatarData(script.avatarDescriptor, script);
            }
            EditorGUI.EndDisabledGroup();

            //Build Options
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUI.indentLevel += 1;
            {
                script.foldoutBuildOptions = EditorGUILayout.Foldout(script.foldoutBuildOptions, "Built Options");
                if (script.foldoutBuildOptions)
                {
                    //Ignore Lists
                    DrawStringList(ref script.foldoutIgnoreLayers, "Ignore Layers", script.ignoreLayers);
                    DrawStringList(ref script.foldoutIgnoreParameters, "Ignore Parameters", script.ignoreParameters);

                    void DrawStringList(ref bool foldout, string title, List <string> list)
                    {
                        EditorGUI.indentLevel += 1;
                        foldout = EditorGUILayout.Foldout(foldout, BaseActionsEditor.Title(title, list.Count > 0));
                        if (foldout)
                        {
                            //Add
                            GUILayout.BeginHorizontal();
                            GUILayout.Space(EditorGUI.indentLevel * 10);
                            if (GUILayout.Button("Add"))
                            {
                                list.Add(null);
                            }
                            GUILayout.EndHorizontal();

                            //Layers
                            for (int i = 0; i < list.Count; i++)
                            {
                                EditorGUILayout.BeginHorizontal();
                                list[i] = EditorGUILayout.TextField(list[i]);
                                if (GUILayout.Button("X", GUILayout.Width(32)))
                                {
                                    list.RemoveAt(i);
                                    i--;
                                }
                                EditorGUILayout.EndHorizontal();
                            }
                        }
                        EditorGUI.indentLevel -= 1;
                    }

                    //Parameter Defaults
                    {
                        EditorGUI.indentLevel          += 1;
                        script.foldoutParameterDefaults = EditorGUILayout.Foldout(script.foldoutParameterDefaults, BaseActionsEditor.Title("Paramater Defaults", script.parameterDefaults.Count > 0));
                        if (script.foldoutParameterDefaults)
                        {
                            //Add
                            GUILayout.BeginHorizontal();
                            GUILayout.Space(EditorGUI.indentLevel * 10);
                            if (GUILayout.Button("Add"))
                            {
                                script.parameterDefaults.Add(new AvatarActions.ParamDefault());
                            }
                            GUILayout.EndHorizontal();

                            //Layers
                            for (int i = 0; i < script.parameterDefaults.Count; i++)
                            {
                                var value = script.parameterDefaults[i];

                                EditorGUILayout.BeginHorizontal();
                                value.name  = EditorGUILayout.TextField(value.name);
                                value.value = EditorGUILayout.FloatField(value.value);

                                script.parameterDefaults[i] = value;

                                if (GUILayout.Button("X", GUILayout.Width(32)))
                                {
                                    script.parameterDefaults.RemoveAt(i);
                                    i--;
                                }
                                EditorGUILayout.EndHorizontal();
                            }
                        }
                        EditorGUI.indentLevel -= 1;
                    }
                }
            }
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.EndVertical();

            //Build Data
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUI.indentLevel  += 1;
            script.foldoutBuildData = EditorGUILayout.Foldout(script.foldoutBuildData, "Built Data");
            if (script.foldoutBuildData && script.avatarDescriptor != null)
            {
                void AnimationController(VRCAvatarDescriptor.AnimLayerType animLayerType, string name)
                {
                    VRCAvatarDescriptor.CustomAnimLayer descLayer = new VRCAvatarDescriptor.CustomAnimLayer();
                    foreach (var layer in script.avatarDescriptor.baseAnimationLayers)
                    {
                        if (layer.type == animLayerType)
                        {
                            descLayer = layer;
                            break;
                        }
                    }

                    var controller = descLayer.animatorController as UnityEditor.Animations.AnimatorController;

                    EditorGUI.BeginChangeCheck();
                    controller = (UnityEditor.Animations.AnimatorController)EditorGUILayout.ObjectField(name, controller, typeof(UnityEditor.Animations.AnimatorController), false);
                    if (EditorGUI.EndChangeCheck())
                    {
                        descLayer.animatorController = controller;
                        descLayer.isDefault          = false;
                    }
                }

                EditorGUILayout.HelpBox("Objects built and linked on the avatar descriptor. Anything referenced here will be modified and possibly destroyed by the compiling process.", MessageType.Info);

                AnimationController(VRCAvatarDescriptor.AnimLayerType.Action, "Action Controller");
                AnimationController(VRCAvatarDescriptor.AnimLayerType.FX, "FX Controller");
                script.avatarDescriptor.expressionsMenu      = (ExpressionsMenu)EditorGUILayout.ObjectField("Expressions Menu", script.avatarDescriptor.expressionsMenu, typeof(ExpressionsMenu), false);
                script.avatarDescriptor.expressionParameters = (ExpressionParameters)EditorGUILayout.ObjectField("Expression Parameters", script.avatarDescriptor.expressionParameters, typeof(ExpressionParameters), false);
            }
            EditorGUI.indentLevel -= 1;
            EditorGUILayout.EndVertical();
        }