public override void OnInspectorGUI()
    {
        if (avatarDescriptor == null)
        {
            avatarDescriptor = (VRCSDK2.VRC_AvatarDescriptor)target;
        }

        if (pipelineManager == null)
        {
            pipelineManager = avatarDescriptor.GetComponent <VRC.Core.PipelineManager>();
            if (pipelineManager == null)
            {
                avatarDescriptor.gameObject.AddComponent <VRC.Core.PipelineManager>();
            }
        }

        // DrawDefaultInspector();

        avatarDescriptor.ViewPosition = EditorGUILayout.Vector3Field("View Position", avatarDescriptor.ViewPosition);
        //avatarDescriptor.Name = EditorGUILayout.TextField("Avatar Name", avatarDescriptor.Name);
        avatarDescriptor.Animations          = (VRCSDK2.VRC_AvatarDescriptor.AnimationSet)EditorGUILayout.EnumPopup("Default Animation Set", avatarDescriptor.Animations);
        avatarDescriptor.CustomStandingAnims = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Standing Anims", avatarDescriptor.CustomStandingAnims, typeof(AnimatorOverrideController), true, null);
        avatarDescriptor.CustomSittingAnims  = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Sitting Anims", avatarDescriptor.CustomSittingAnims, typeof(AnimatorOverrideController), true, null);
        avatarDescriptor.ScaleIPD            = EditorGUILayout.Toggle("Scale IPD", avatarDescriptor.ScaleIPD);

        avatarDescriptor.lipSync = (VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle)EditorGUILayout.EnumPopup("Lip Sync", avatarDescriptor.lipSync);
        switch (avatarDescriptor.lipSync)
        {
        case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.Default:
            if (GUILayout.Button("Auto Detect!"))
            {
                AutoDetectLipSync();
            }
            break;

        case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape:
            avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
            if (avatarDescriptor.VisemeSkinnedMesh != null)
            {
                DetermineBlendShapeNames();

                int current = -1;
                for (int b = 0; b < blendShapeNames.Count; ++b)
                {
                    if (avatarDescriptor.MouthOpenBlendShapeName == blendShapeNames[b])
                    {
                        current = b;
                    }
                }

                string title = "Jaw Flap Blend Shape";
                int    next  = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
                if (next >= 0)
                {
                    avatarDescriptor.MouthOpenBlendShapeName = blendShapeNames[next];
                }
            }
            break;

        case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone:
            avatarDescriptor.lipSyncJawBone = (Transform)EditorGUILayout.ObjectField("Jaw Bone", avatarDescriptor.lipSyncJawBone, typeof(Transform), true);
            break;

        case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape:
            avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
            if (avatarDescriptor.VisemeSkinnedMesh != null)
            {
                DetermineBlendShapeNames();
                FillvisBlendShapes();

                if (avatarDescriptor.VisemeBlendShapes == null || avatarDescriptor.VisemeBlendShapes.Length != (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count)
                {
                    avatarDescriptor.VisemeBlendShapes = new string[(int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count];

                    for (int i = 0; i < visBlendShapes.Count; ++i)
                    {
                        int current = -1;

                        for (int b = 0; b < blendShapeNames.Count; ++b)
                        {
                            if (visBlendShapes[i] == blendShapeNames[b])
                            {
                                current = b;
                            }
                            else if (blendShapeNames[b] == "vrc.v_ee" && visBlendShapes[i] == "vrc.v_e")
                            {
                                current = b;
                            }
                        }

                        string title = "Viseme: " + visBlendShapes[i].Replace("vrc.v_", "");
                        int    next  = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
                        if (next >= 0)
                        {
                            avatarDescriptor.VisemeBlendShapes[i] = blendShapeNames[next];
                        }
                    }
                    break;
                }

                for (int i = 0; i < (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count; ++i)
                {
                    int current = -1;
                    for (int b = 0; b < blendShapeNames.Count; ++b)
                    {
                        if (avatarDescriptor.VisemeBlendShapes[i] == blendShapeNames[b])
                        {
                            current = b;
                        }
                    }

                    string title = "Viseme: " + ((VRCSDK2.VRC_AvatarDescriptor.Viseme)i).ToString();
                    int    next  = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
                    if (next >= 0)
                    {
                        avatarDescriptor.VisemeBlendShapes[i] = blendShapeNames[next];
                    }
                }
            }
            break;
        }
        EditorGUILayout.LabelField("Unity Version", avatarDescriptor.unityVersion);
    }
    void OnGUIAvatarCheck(VRCSDK2.VRC_AvatarDescriptor avatar)
    {
        AvatarPerformanceStats perfStats = AvatarPerformance.CalculatePerformanceStats(avatar.Name, avatar.gameObject);

        OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.Overall);
        OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.PolyCount);
        OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.AABB);

        var eventHandler = avatar.GetComponentInChildren <VRCSDK2.VRC_EventHandler>();

        if (eventHandler != null)
        {
            OnGUIError(avatar, "This avatar contains an EventHandler, which is not currently supported in VRChat.");
        }

        if (avatar.lipSync == VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape && avatar.VisemeSkinnedMesh == null)
        {
            OnGUIError(avatar, "This avatar uses Visemes but the Face Mesh is not specified.");
        }

        var anim = avatar.GetComponent <Animator>();

        if (anim == null)
        {
            OnGUIWarning(avatar, "This avatar does not contain an animator, and will not animate in VRChat.");
        }
        else if (anim.isHuman == false)
        {
            OnGUIWarning(avatar, "This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set.");
        }
        else if (avatar.gameObject.activeInHierarchy == false)
        {
            OnGUIError(avatar, "Your avatar is disabled in the scene hierarchy!");
        }
        else
        {
            Transform foot     = anim.GetBoneTransform(HumanBodyBones.LeftFoot);
            Transform shoulder = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm);
            if (foot == null)
            {
                OnGUIError(avatar, "Your avatar is humanoid, but it's feet aren't specified!");
            }
            if (shoulder == null)
            {
                OnGUIError(avatar, "Your avatar is humanoid, but it's upper arms aren't specified!");
            }

            if (foot != null && shoulder != null)
            {
                Vector3 footPos = foot.position - avatar.transform.position;
                if (footPos.y < 0)
                {
                    OnGUIWarning(avatar, "Avatar feet are beneath the avatar's origin (the floor). That's probably not what you want.");
                }
                Vector3 shoulderPosition = shoulder.position - avatar.transform.position;
                if (shoulderPosition.y < 0.2f)
                {
                    OnGUIError(avatar, "This avatar is too short. The minimum is 20cm shoulder height.");
                }
                else if (shoulderPosition.y < 1.0f)
                {
                    OnGUIWarning(avatar, "This avatar is short. This is probably shorter than you want.");
                }
                else if (shoulderPosition.y > 5.0f)
                {
                    OnGUIWarning(avatar, "This avatar is too tall. The maximum is 5m shoulder height.");
                }
                else if (shoulderPosition.y > 2.5f)
                {
                    OnGUIWarning(avatar, "This avatar is tall. This is probably taller than you want.");
                }
            }

            if (AnalyzeIK(avatar, avatar.gameObject, anim) == false)
            {
                OnGUILink(avatar, "See Avatar Rig Requirements for more information.", kAvatarRigRequirementsURL);
            }
        }

        IEnumerable <Component> componentsToRemove      = VRCSDK2.AvatarValidation.FindIllegalComponents(avatar.gameObject);
        HashSet <string>        componentsToRemoveNames = new HashSet <string>();

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

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

        if (VRCSDK2.AvatarValidation.EnforceAudioSourceLimits(avatar.gameObject).Count > 0)
        {
            OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.");
        }

        if (VRCSDK2.AvatarValidation.EnforceAvatarStationLimits(avatar.gameObject).Count > 0)
        {
            OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary.");
        }

        if (avatar.gameObject.GetComponentInChildren <Camera>() != null)
        {
            OnGUIWarning(avatar, "Cameras are removed from non-local avatars at runtime.");
        }

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

            OnGUIPerformanceInfo(avatar, perfStats, perfCategory);
        }

        OnGUILink(avatar, "Avatar Optimization Tips", kAvatarOptimizationTipsURL);
    }
Exemplo n.º 3
0
    void OnGUIAvatarCheck(VRCSDK2.VRC_AvatarDescriptor avatar)
    {
        int    polycount;
        Bounds bounds;

        AnalyzeGeometry(avatar.gameObject, out bounds, out polycount);
        if (polycount < 10000)
        {
            OnGUIInformation(avatar, "Polygons: " + polycount);
        }
        else if (polycount < 15000)
        {
            OnGUIWarning(avatar, "Polygons: " + polycount + " - Please try to reduce your avatar poly count to less thatn 10k.");
        }
        else if (polycount < 20000)
        {
            OnGUIWarning(avatar, "Polygons: " + polycount + " - This avatar will not perform well on many systems.");
        }
        else
        {
            OnGUIError(avatar, "Polygons: " + polycount + " - This avatar has too many polygons. It must have less than 20k and should have less than 10k.");
        }

        if (bounds.size.x > 5f || bounds.size.y > 6.0f || bounds.size.z > 5f)
        {
            OnGUIError(avatar, "This avatar measures too large on at least one axis. It must be <5m on a side but it's bounds are " + bounds.size.ToString());
        }

        var eventHandler = avatar.GetComponentInChildren <VRCSDK2.VRC_EventHandler>();

        if (eventHandler != null)
        {
            OnGUIError(avatar, "This avatar contains an EventHandler, which is not currently supported in VRChat.");
        }

        if (avatar.lipSync == VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape && avatar.VisemeSkinnedMesh == null)
        {
            OnGUIError(avatar, "This avatar uses Visemes but the Face Mesh is not specified.");
        }

        var anim = avatar.GetComponent <Animator>();

        if (anim == null)
        {
            OnGUIWarning(avatar, "This avatar does not contain an animator, and will not animate in VRChat.");
        }
        else if (anim.isHuman == false)
        {
            OnGUIWarning(avatar, "This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set.");
        }
        else if (avatar.gameObject.activeInHierarchy == false)
        {
            OnGUIError(avatar, "Your avatar is disabled in the scene heirarchy!");
        }
        else
        {
            Transform foot     = anim.GetBoneTransform(HumanBodyBones.LeftFoot);
            Transform shoulder = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm);
            if (foot == null)
            {
                OnGUIError(avatar, "Your avatar is humanoid, but it's feet aren't specified!");
            }
            if (shoulder == null)
            {
                OnGUIError(avatar, "Your avatar is humanoid, but it's upper arms aren't specified!");
            }

            if (foot != null && shoulder != null)
            {
                Vector3 footPos = foot.position - avatar.transform.position;
                if (footPos.y < 0)
                {
                    OnGUIWarning(avatar, "Avatar feet are beneath the avatar's origin (the floor). That's probably not what you want.");
                }
                Vector3 shoulderPosition = shoulder.position - avatar.transform.position;
                if (shoulderPosition.y < 0.2f)
                {
                    OnGUIError(avatar, "This avatar is too short. The minimum is 20cm shoulder height.");
                }
                else if (shoulderPosition.y < 1.0f)
                {
                    OnGUIWarning(avatar, "This avatar is short. This is probably shorter than you want.");
                }
                else if (shoulderPosition.y > 5.0f)
                {
                    OnGUIWarning(avatar, "This avatar is too tall. The maximum is 5m shoulder height.");
                }
                else if (shoulderPosition.y > 2.5f)
                {
                    OnGUIWarning(avatar, "This avatar is tall. This is probably taller than you want.");
                }
            }

            if (AnalyzeIK(avatar, avatar.gameObject, anim) == false)
            {
                OnGUILink(avatar, "See https://docs.vrchat.com/docs/rig-requirements for details.");
            }
        }

        {
            IEnumerable <Component> componentsToRemove      = VRCSDK2.AvatarValidation.FindIllegalComponents(avatar.Name, avatar.gameObject);
            HashSet <string>        componentsToRemoveNames = new HashSet <string>();
            foreach (Component c in componentsToRemove)
            {
                if (componentsToRemoveNames.Contains(c.GetType().Name) == false)
                {
                    componentsToRemoveNames.Add(c.GetType().Name);
                }
            }

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

        if (VRCSDK2.AvatarValidation.EnforceAudioSourceLimits(avatar.gameObject).Count > 0)
        {
            OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.");
        }

        if (avatar.gameObject.GetComponentInChildren <Camera>() != null)
        {
            OnGUIWarning(avatar, "Cameras are removed from non-local avatars at runtime.");
        }
    }