상속: EditorWindow
 private static void DrawContentInfoForWorld(Core.ApiWorld w)
 {
     VRCSdkControlPanel.DrawContentInfo(w.name, w.version.ToString(), w.description, w.capacity.ToString(), w.releaseStatus,
                                        w.tags);
 }
예제 #2
0
    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();

        if (VRCSdkControlPanel.window != null)
        {
            if (GUILayout.Button("Select this avatar in the SDK control panel"))
            {
                VRCSdkControlPanel.SelectAvatar(avatarDescriptor);
            }
        }

        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();

                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 < (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);
    }
        protected virtual void OnGUISceneCheck(VRC_SceneDescriptor scene)
        {
            CheckUploadChanges(scene);

            bool isSdk3Scene = IsSDK3Scene();

            List <VRC_EventHandler> sdkBaseEventHandlers =
                new List <VRC_EventHandler>(Object.FindObjectsOfType <VRC_EventHandler>());

#if VRC_SDK_VRCSDK2
            if (isSdk3Scene == false)
            {
                for (int i = sdkBaseEventHandlers.Count - 1; i >= 0; --i)
                {
                    if (sdkBaseEventHandlers[i] as VRCSDK2.VRC_EventHandler)
                    {
                        sdkBaseEventHandlers.RemoveAt(i);
                    }
                }
            }
#endif
            if (sdkBaseEventHandlers.Count > 0)
            {
                _builder.OnGUIError(scene,
                                    "You have Event Handlers in your scene that are not allowed in this build configuration.",
                                    delegate
                {
                    List <Object> gos = sdkBaseEventHandlers.ConvertAll(item => (Object)item.gameObject);
                    Selection.objects = gos.ToArray();
                },
                                    delegate
                {
                    foreach (VRC_EventHandler eh in sdkBaseEventHandlers)
                    {
#if VRC_SDK_VRCSDK2
                        GameObject go = eh.gameObject;
                        if (isSdk3Scene == false)
                        {
                            if (VRC_SceneDescriptor.Instance as VRCSDK2.VRC_SceneDescriptor != null)
                            {
                                go.AddComponent <VRCSDK2.VRC_EventHandler>();
                            }
                        }
#endif
                        Object.DestroyImmediate(eh);
                    }
                });
            }

            Vector3 g = Physics.gravity;
            if (Math.Abs(g.x) > float.Epsilon || Math.Abs(g.z) > float.Epsilon)
            {
                _builder.OnGUIWarning(scene,
                                      "Gravity vector is not straight down. Though we support different gravity, player orientation is always 'upwards' so things don't always behave as you intend.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }
            if (g.y > 0)
            {
                _builder.OnGUIWarning(scene,
                                      "Gravity vector is not straight down, inverted or zero gravity will make walking extremely difficult.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }
            if (Math.Abs(g.y) < float.Epsilon)
            {
                _builder.OnGUIWarning(scene,
                                      "Zero gravity will make walking extremely difficult, though we support different gravity, player orientation is always 'upwards' so this may not have the effect you're looking for.",
                                      delegate { SettingsService.OpenProjectSettings("Project/Physics"); }, null);
            }

            if (CheckFogSettings())
            {
                _builder.OnGUIWarning(
                    scene,
                    "Fog shader stripping is set to Custom, this may lead to incorrect or unnecessary shader variants being included in the build. You should use Automatic unless you change the fog mode at runtime.",
                    delegate { SettingsService.OpenProjectSettings("Project/Graphics"); },
                    delegate
                {
                    EnvConfig.SetFogSettings(
                        new EnvConfig.FogSettings(EnvConfig.FogSettings.FogStrippingMode.Automatic));
                });
            }

            if (scene.autoSpatializeAudioSources)
            {
                _builder.OnGUIWarning(scene,
                                      "Your scene previously used the 'Auto Spatialize Audio Sources' feature. This has been deprecated, press 'Fix' to disable. Also, please add VRC_SpatialAudioSource to all your audio sources. Make sure Spatial Blend is set to 3D for the sources you want to spatialize.",
                                      null,
                                      delegate { scene.autoSpatializeAudioSources = false; }
                                      );
            }

            AudioSource[] audioSources = Object.FindObjectsOfType <AudioSource>();
            foreach (AudioSource a in audioSources)
            {
                if (a.GetComponent <ONSPAudioSource>() != null)
                {
                    _builder.OnGUIWarning(scene,
                                          "Found audio source(s) using ONSP, this is deprecated. Press 'fix' to convert to VRC_SpatialAudioSource.",
                                          delegate { Selection.activeObject = a.gameObject; },
                                          delegate
                    {
                        Selection.activeObject = a.gameObject;
                        AutoAddSpatialAudioComponents.ConvertONSPAudioSource(a);
                    }
                                          );
                    break;
                }
                else if (a.GetComponent <VRC_SpatialAudioSource>() == null)
                {
                    string msg =
                        "Found 3D audio source with no VRC Spatial Audio component, this is deprecated. Press 'fix' to add a VRC_SpatialAudioSource.";
                    if (IsAudioSource2D(a))
                    {
                        msg =
                            "Found 2D audio source with no VRC Spatial Audio component, this is deprecated. Press 'fix' to add a (disabled) VRC_SpatialAudioSource.";
                    }

                    _builder.OnGUIWarning(scene, msg,
                                          delegate { Selection.activeObject = a.gameObject; },
                                          delegate
                    {
                        Selection.activeObject = a.gameObject;
                        AutoAddSpatialAudioComponents.AddVRCSpatialToBareAudioSource(a);
                    }
                                          );
                    break;
                }
            }

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

            string vrcFilePath = UnityWebRequest.UnEscapeURL(EditorPrefs.GetString("lastVRCPath"));
            if (!string.IsNullOrEmpty(vrcFilePath) &&
                ValidationHelpers.CheckIfAssetBundleFileTooLarge(ContentType.World, vrcFilePath, out int fileSize))
            {
                _builder.OnGUIWarning(scene,
                                      ValidationHelpers.GetAssetBundleOverSizeLimitMessageSDKWarning(ContentType.World, fileSize), null,
                                      null);
            }

            foreach (GameObject go in Object.FindObjectsOfType <GameObject>())
            {
                if (go.transform.parent == null)
                {
                    // check root game objects
#if UNITY_ANDROID
                    IEnumerable <Shader> illegalShaders = VRC.SDKBase.Validation.WorldValidation.FindIllegalShaders(go);
                    foreach (Shader s in illegalShaders)
                    {
                        _builder.OnGUIWarning(scene, "World uses unsupported shader '" + s.name + "'. This could cause low performance or future compatibility issues.", null, null);
                    }
#endif
                }
                else
                {
                    // Check sibling game objects
                    for (int idx = 0; idx < go.transform.parent.childCount; ++idx)
                    {
                        Transform t = go.transform.parent.GetChild(idx);
                        if (t == go.transform)
                        {
                            continue;
                        }

#if VRC_SDK_VRCSDK2
                        bool allowedType = (t.GetComponent <VRCSDK2.VRC_ObjectSync>() ||
                                            t.GetComponent <VRCSDK2.VRC_SyncAnimation>() ||
                                            t.GetComponent <VRCSDK2.VRC_SyncVideoPlayer>() ||
                                            t.GetComponent <VRCSDK2.VRC_SyncVideoStream>());
                        if (t.name != go.transform.name || allowedType)
                        {
                            continue;
                        }
#else
                        if (t.name != go.transform.name)
                        {
                            continue;
                        }
#endif

                        string    path = t.name;
                        Transform p    = t.parent;
                        while (p != null)
                        {
                            path = p.name + "/" + path;
                            p    = p.parent;
                        }

                        _builder.OnGUIWarning(scene,
                                              "Sibling objects share the same path, which may break network events: " + path,
                                              delegate
                        {
                            List <Object> gos = new List <Object>();
                            for (int c = 0; c < go.transform.parent.childCount; ++c)
                            {
                                if (go.transform.parent.GetChild(c).name == go.name)
                                {
                                    gos.Add(go.transform.parent.GetChild(c).gameObject);
                                }
                            }
                            Selection.objects = gos.ToArray();
                        },
                                              delegate
                        {
                            List <Object> gos = new List <Object>();
                            for (int c = 0; c < go.transform.parent.childCount; ++c)
                            {
                                if (go.transform.parent.GetChild(c).name == go.name)
                                {
                                    gos.Add(go.transform.parent.GetChild(c).gameObject);
                                }
                            }
                            Selection.objects = gos.ToArray();
                            for (int i = 0; i < gos.Count; ++i)
                            {
                                gos[i].name = gos[i].name + "-" + i.ToString("00");
                            }
                        });
                        break;
                    }
                }
            }

            // detect dynamic materials and prefabs with identical names (since these could break triggers)
            VRC_Trigger[]     triggers  = Tools.FindSceneObjectsOfTypeAll <VRC_Trigger>();
            List <GameObject> prefabs   = new List <GameObject>();
            List <Material>   materials = new List <Material>();

#if VRC_SDK_VRCSDK2
            AssetExporter.FindDynamicContent(ref prefabs, ref materials);
#elif VRC_SDK_VRCSDK3
            AssetExporter.FindDynamicContent(ref prefabs, ref materials);
#endif

            foreach (VRC_Trigger t in triggers)
            {
                foreach (VRC_Trigger.TriggerEvent triggerEvent in t.Triggers)
                {
                    foreach (VRC_EventHandler.VrcEvent e in triggerEvent.Events.Where(evt =>
                                                                                      evt.EventType == VRC_EventHandler.VrcEventType.SpawnObject))
                    {
                        GameObject go =
                            AssetDatabase.LoadAssetAtPath(e.ParameterString, typeof(GameObject)) as GameObject;
                        if (go == null)
                        {
                            continue;
                        }
                        foreach (GameObject existing in prefabs)
                        {
                            if (go == existing || go.name != existing.name)
                            {
                                continue;
                            }
                            _builder.OnGUIWarning(scene,
                                                  "Trigger prefab '" + AssetDatabase.GetAssetPath(go).Replace(".prefab", "") +
                                                  "' has same name as a prefab in another folder. This may break the trigger.",
                                                  delegate { Selection.objects = new Object[] { go }; },
                                                  delegate
                            {
                                AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(go),
                                                          go.name + "-" + go.GetInstanceID());
                                AssetDatabase.Refresh();
                                e.ParameterString = AssetDatabase.GetAssetPath(go);
                            });
                        }
                    }

                    foreach (VRC_EventHandler.VrcEvent e in triggerEvent.Events.Where(evt =>
                                                                                      evt.EventType == VRC_EventHandler.VrcEventType.SetMaterial))
                    {
                        Material mat = AssetDatabase.LoadAssetAtPath <Material>(e.ParameterString);
                        if (mat == null || mat.name.Contains("(Instance)"))
                        {
                            continue;
                        }
                        foreach (Material existing in materials)
                        {
                            if (mat == existing || mat.name != existing.name)
                            {
                                continue;
                            }
                            _builder.OnGUIWarning(scene,
                                                  "Trigger material '" + AssetDatabase.GetAssetPath(mat).Replace(".mat", "") +
                                                  "' has same name as a material in another folder. This may break the trigger.",
                                                  delegate { Selection.objects = new Object[] { mat }; },
                                                  delegate
                            {
                                AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(mat),
                                                          mat.name + "-" + mat.GetInstanceID());
                                AssetDatabase.Refresh();
                                e.ParameterString = AssetDatabase.GetAssetPath(mat);
                            });
                        }
                    }
                }
            }
        }
        private void OnGUISceneSettings(VRC_SceneDescriptor scene)
        {
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle, GUILayout.Width(VRCSdkControlPanel.SdkWindowWidth));

            string name = "Unpublished VRChat World";

            if (scene.apiWorld != null)
            {
                name = (scene.apiWorld as Core.ApiWorld)?.name;
            }
            EditorGUILayout.Space();
            EditorGUILayout.LabelField(name, VRCSdkControlPanel.titleGuiStyle);

            Core.PipelineManager[] pms = Tools.FindSceneObjectsOfTypeAll <Core.PipelineManager>();
            if (pms.Length == 1)
            {
                if (!string.IsNullOrEmpty(pms[0].blueprintId))
                {
                    if (scene.apiWorld == null)
                    {
                        Core.ApiWorld world = Core.API.FromCacheOrNew <Core.ApiWorld>(pms[0].blueprintId);
                        world.Fetch(null,
                                    (c) => scene.apiWorld = c.Model as Core.ApiWorld,
                                    (c) =>
                        {
                            if (c.Code == 404)
                            {
                                Core.Logger.Log(
                                    $"Could not load world {pms[0].blueprintId} because it didn't exist.", Core.DebugLevel.All);
                                Core.ApiCache.Invalidate <Core.ApiWorld>(pms[0].blueprintId);
                            }
                            else
                            {
                                Debug.LogErrorFormat("Could not load world {0} because {1}", pms[0].blueprintId,
                                                     c.Error);
                            }
                        });
                        scene.apiWorld = world;
                    }
                }
                else
                {
                    // clear scene.apiWorld if blueprint ID has been detached, so world details in builder panel are also cleared
                    scene.apiWorld = null;
                }
            }

            if (scene.apiWorld != null)
            {
                Core.ApiWorld w = (scene.apiWorld as Core.ApiWorld);
                DrawContentInfoForWorld(w);
                VRCSdkControlPanel.DrawContentPlatformSupport(w);
            }

            VRCSdkControlPanel.DrawBuildTargetSwitcher();

            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
        }
예제 #5
0
        public override void OnGUIAvatar(VRC.SDKBase.VRC_AvatarDescriptor avatar)
        {
            EditorGUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle);
            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();

            GUILayout.Label("Online Publishing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "In order for other people to see your avatar in VRChat it must be built and published to our game servers.",
                VRCSdkControlPanel.infoGuiStyle);

            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            GUI.enabled = _builder.NoGuiErrorsOrIssues() ||
                          VRC.Core.APIUser.CurrentUser.developerType == VRC.Core.APIUser.DeveloperType.Internal;
            if (GUILayout.Button(VRCSdkControlPanel.GetBuildAndPublishButtonString()))
            {
                bool buildBlocked = !VRCBuildPipelineCallbacks.OnVRCSDKBuildRequested(VRCSDKRequestedBuildType.Avatar);
                if (!buildBlocked)
                {
                    if (VRC.Core.APIUser.CurrentUser.canPublishAvatars)
                    {
                        EnvConfig.FogSettings originalFogSettings = EnvConfig.GetFogSettings();
                        EnvConfig.SetFogSettings(
                            new EnvConfig.FogSettings(EnvConfig.FogSettings.FogStrippingMode.Custom, true, true, true));

#if UNITY_ANDROID
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", true);
#else
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", false);
#endif

                        VRC_SdkBuilder.shouldBuildUnityPackage = VRCSdkControlPanel.FutureProofPublishEnabled;
                        VRC_SdkBuilder.ExportAndUploadAvatarBlueprint(avatar.gameObject);

                        EnvConfig.SetFogSettings(originalFogSettings);

                        // this seems to workaround a Unity bug that is clearing the formatting of two levels of Layout
                        // when we call the upload functions
                        return;
                    }
                    else
                    {
                        VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                    }
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            GUI.enabled = true;
        }
 public virtual void RegisterBuilder(VRCSdkControlPanel baseBuilder)
 {
     _builder = baseBuilder;
 }
예제 #7
0
        public override void OnGUIScene()
        {
            GUILayout.Label("", VRCSdkControlPanel.scrollViewSeparatorStyle);

            _builderScrollPos = GUILayout.BeginScrollView(_builderScrollPos, false, false, GUIStyle.none,
                                                          GUI.skin.verticalScrollbar, GUILayout.Width(VRCSdkControlPanel.SdkWindowWidth),
                                                          GUILayout.MinHeight(217));

            GUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle, GUILayout.Width(VRCSdkControlPanel.SdkWindowWidth));
            GUILayout.BeginHorizontal();
            GUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();
            GUILayout.Label("Local Testing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "Before uploading your world you may build and test it in the VRChat client. You won't be able to invite anyone from online but you can launch multiple of your own clients.",
                VRCSdkControlPanel.infoGuiStyle);
            GUILayout.EndVertical();
            GUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();
            VRCSettings.NumClients = EditorGUILayout.IntField("Number of Clients", VRCSettings.NumClients, GUILayout.MaxWidth(190));
            EditorGUILayout.Space();
            VRCSettings.ForceNoVR = EditorGUILayout.Toggle("Force Non-VR", VRCSettings.ForceNoVR, GUILayout.MaxWidth(190));
            EditorGUILayout.Space();
            if (VRCSettings.DisplayAdvancedSettings)
            {
                VRCSettings.WatchWorlds =
                    EditorGUILayout.Toggle("Enable World Reload", VRCSettings.WatchWorlds, GUILayout.MaxWidth(190));
                EditorGUILayout.Space();
            }

            GUI.enabled = _builder.NoGuiErrorsOrIssues();

            string lastUrl = VRC_SdkBuilder.GetLastUrl();

            bool doReload = VRCSettings.WatchWorlds && VRCSettings.NumClients == 0;

            bool lastBuildPresent = lastUrl != null;

            if (lastBuildPresent == false)
            {
                GUI.enabled = false;
            }
            if (VRCSettings.DisplayAdvancedSettings)
            {
                string lastBuildLabel = doReload ? "Reload Last Build" : "Last Build";
                if (GUILayout.Button(lastBuildLabel))
                {
                    if (doReload)
                    {
                        // Todo: get this from settings or make key a const
                        string path = EditorPrefs.GetString("lastVRCPath");
                        if (File.Exists(path))
                        {
                            File.SetLastWriteTimeUtc(path, DateTime.Now);
                        }
                        else
                        {
                            Debug.LogWarning($"Cannot find last built scene, please Rebuild.");
                        }
                    }
                    else
                    {
                        VRC_SdkBuilder.shouldBuildUnityPackage = false;
                        VRC_SdkBuilder.RunLastExportedSceneResource();
                    }
                }

                if (Core.APIUser.CurrentUser.hasSuperPowers)
                {
                    if (GUILayout.Button("Copy Test URL"))
                    {
                        TextEditor te = new TextEditor {
                            text = lastUrl
                        };
                        te.SelectAll();
                        te.Copy();
                    }
                }
            }

            GUI.enabled = _builder.NoGuiErrorsOrIssues() ||
                          Core.APIUser.CurrentUser.developerType == Core.APIUser.DeveloperType.Internal;

#if UNITY_ANDROID
            EditorGUI.BeginDisabledGroup(true);
#endif
            string buildLabel = doReload ? "Build & Reload" : "Build & Test";
            if (GUILayout.Button(buildLabel))
            {
                bool buildTestBlocked = !VRCBuildPipelineCallbacks.OnVRCSDKBuildRequested(VRCSDKRequestedBuildType.Scene);
                if (!buildTestBlocked)
                {
                    EnvConfig.ConfigurePlayerSettings();
                    VRC_SdkBuilder.shouldBuildUnityPackage = false;
                    AssetExporter.CleanupUnityPackageExport(); // force unity package rebuild on next publish
                    VRC_SdkBuilder.PreBuildBehaviourPackaging();
                    if (doReload)
                    {
                        VRC_SdkBuilder.ExportSceneResource();
                    }
                    else
                    {
                        VRC_SdkBuilder.ExportSceneResourceAndRun();
                    }
                }
            }
#if UNITY_ANDROID
            EditorGUI.EndDisabledGroup();
#endif

            GUILayout.EndVertical();

            if (Event.current.type != EventType.Used)
            {
                GUILayout.EndHorizontal();
                EditorGUILayout.Space();
                GUILayout.EndVertical();
            }

            EditorGUILayout.Space();

            GUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle, GUILayout.Width(VRCSdkControlPanel.SdkWindowWidth));

            GUILayout.BeginHorizontal();
            GUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();
            GUILayout.Label("Online Publishing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "In order for other people to enter your world in VRChat it must be built and published to our game servers.",
                VRCSdkControlPanel.infoGuiStyle);
            EditorGUILayout.Space();
            GUILayout.EndVertical();
            GUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            if (lastBuildPresent == false)
            {
                GUI.enabled = false;
            }
            if (VRCSettings.DisplayAdvancedSettings)
            {
                if (GUILayout.Button("Last Build"))
                {
                    if (Core.APIUser.CurrentUser.canPublishWorlds)
                    {
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", false);
                        VRC_SdkBuilder.shouldBuildUnityPackage = VRCSdkControlPanel.FutureProofPublishEnabled;
                        VRC_SdkBuilder.UploadLastExportedSceneBlueprint();
                    }
                    else
                    {
                        VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                    }
                }
            }

            GUI.enabled = _builder.NoGuiErrorsOrIssues() ||
                          Core.APIUser.CurrentUser.developerType == Core.APIUser.DeveloperType.Internal;
            if (GUILayout.Button(VRCSdkControlPanel.GetBuildAndPublishButtonString()))
            {
                bool buildBlocked = !VRCBuildPipelineCallbacks.OnVRCSDKBuildRequested(VRCSDKRequestedBuildType.Scene);
                if (!buildBlocked)
                {
                    if (Core.APIUser.CurrentUser.canPublishWorlds)
                    {
                        EnvConfig.ConfigurePlayerSettings();
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", false);

                        VRC_SdkBuilder.shouldBuildUnityPackage = VRCSdkControlPanel.FutureProofPublishEnabled;
                        VRC_SdkBuilder.PreBuildBehaviourPackaging();
                        VRC_SdkBuilder.ExportAndUploadSceneBlueprint();
                    }
                    else
                    {
                        VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                    }
                }
            }

            GUILayout.EndVertical();
            GUI.enabled = true;

            if (Event.current.type == EventType.Used)
            {
                return;
            }
            GUILayout.EndHorizontal();
            GUILayout.EndVertical();
            GUILayout.EndScrollView();
        }
예제 #8
0
        public override void ValidateFeatures(VRC.SDKBase.VRC_AvatarDescriptor avatar, Animator anim, AvatarPerformanceStats perfStats)
        {
            List <Component> componentsToRemove = AvatarValidation.FindIllegalComponents(avatar.gameObject).ToList();

            // create a list of the PipelineSaver component(s)
            List <Component> toRemoveSilently = new List <Component>();

            foreach (Component c in componentsToRemove)
            {
                if (c.GetType().Name == "PipelineSaver")
                {
                    toRemoveSilently.Add(c);
                }
            }

            // delete PipelineSaver(s) from the list of the Components we will destroy now
            foreach (Component c in toRemoveSilently)
            {
                componentsToRemove.Remove(c);
            }

            HashSet <string> componentsToRemoveNames = new HashSet <string>();
            List <Component> toRemove = componentsToRemove as List <Component> ?? componentsToRemove;

            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);
        }
예제 #9
0
        private void OnGUI()
        {
#if GOTOUDON_DEV
            DrawReleaseHelper();
#endif
            UpdaterEditor.Instance.DrawVersionInformation();

            _scroll = GUILayout.BeginScrollView(_scroll, GUIStyle.none);
            if (!Application.isPlaying)
            {
                VRCSdkControlPanel.InitAccount();
            }

            _settings.Init();
            SimpleGUI.DrawFoldout(this, "Help", () =>
            {
                SimpleGUI.InfoBox(true,
                                  "Here you can prepare profiles (vrchat --profile=x option) and launch them at once and connect to given world.\n" +
                                  "Each profile can be logged in to other vrchat account, allowing you for simple testing.\n" +
                                  "You can also disable some profiles, this will just simply ignore them when using button to start all clients.\n" +
                                  "Keeping instance might cause issues on restart with multiple clients, vrchat servers might still think you are trying to join twice.");
            });

            EditorGUI.BeginChangeCheck();

            SimpleGUI.DrawFoldout(this, "Advanced settings", () =>
            {
                SimpleGUI.WarningBox(string.IsNullOrWhiteSpace(_settings.WorldId),
                                     "Missing world. Make sure your world have a vrc world descriptor and you are logged in to SDK.");
                SimpleGUI.InfoBox(string.IsNullOrWhiteSpace(_settings.UserId), "Login to SDK and User ID field will fill up itself");
                _settings.userId = EditorGUILayout.TextField("User ID", _settings.userId);
                SimpleGUI.InfoBox(string.IsNullOrWhiteSpace(_settings.gamePath),
                                  "This should be automatically filled from sdk, but if its not, point it to your vrchat.exe");
                _settings.gamePath           = EditorGUILayout.TextField("Game path", _settings.gamePath);
                _settings.launchOptions      = EditorGUILayout.TextField("Launch options", _settings.launchOptions);
                _settings.localLaunchOptions = EditorGUILayout.TextField("Local launch options", _settings.localLaunchOptions);
                // _settings.sendInvitesOnUpdate = EditorGUILayout.Toggle("Send invites on world update", _settings.sendInvitesOnUpdate);
                _settings.accessType = (ApiWorldInstance.AccessType)EditorGUILayout.EnumPopup("Access Type", _settings.accessType);
                _keepInstanceForce   = EditorGUILayout.Toggle("Force keep instance ID", _keepInstanceForce);
                if (_keepInstanceForce)
                {
                    _keepInstance = true;
                }

                GUILayout.BeginHorizontal();
                GUILayout.Label("Same room restart wait time (s)", GUILayout.Width(200));
                _settings.sameInstanceRestartDelay = EditorGUILayout.IntField(_settings.sameInstanceRestartDelay, GUILayout.Width(30));
                GUILayout.EndHorizontal();
            });

            SimpleGUI.SectionSpacing();
            DrawClientSection();
            SimpleGUI.SectionSpacing();

            if (SimpleGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(_settings);
                EditorUtility.SetDirty(GotoUdonInternalState.Instance);
            }

            SimpleGUI.DrawFooterInformation();
            GUILayout.EndScrollView();
        }
예제 #10
0
    void OnGUI()
    {
        if (window == null)
        {
            window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
            InitializeStyles();
        }

        if (_bannerImage == null)
        {
            _bannerImage = AssetDatabase.LoadAssetAtPath("Assets/VRCSDK/Dependencies/VRChat/Resources/SDK Images/SDK_Panel_Banner.png", typeof(Texture2D)) as Texture2D;
        }


        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        GUILayout.BeginVertical();

        GUILayout.Box(_bannerImage);

        if (Application.isPlaying)
        {
            GUI.enabled = false;
            GUILayout.Space(20);
            EditorGUILayout.LabelField("Unity Application is running ...\nStop it to access the Control Panel", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
            GUI.enabled = true;
            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            return;
        }

        GUI.backgroundColor = new Color(
            UnityEditor.EditorPrefs.GetFloat("SDKColor_R"),
            UnityEditor.EditorPrefs.GetFloat("SDKColor_G"),
            UnityEditor.EditorPrefs.GetFloat("SDKColor_B"),
            UnityEditor.EditorPrefs.GetFloat("SDKColor_A")
            );


        EnvConfig.SetActiveSDKDefines();

#if VRC_SDK_VRCSDK2
        VRCSettings.Get().activeWindowPanel = GUILayout.Toolbar(VRCSettings.Get().activeWindowPanel, new string[] { "Login", "Builder", "Content", "Settings", "Assets", "Changelog" }, GUILayout.Width(SdkWindowWidth));
        int showPanel = VRCSettings.Get().activeWindowPanel;
#elif VRC_SDK_VRCSDK3
        VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel = GUILayout.Toolbar(VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel, new string[] { "Authentication", "Builder", "Content Manager", "Settings" }, GUILayout.Width(SdkWindowWidth));
        int showPanel = VRC.SDK3.Editor.VRCSettings.Get().activeWindowPanel;
#else
        int showPanel = 0;
        #endif

        GUILayout.EndVertical();
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        if (APIUser.IsLoggedInWithCredentials == false && showPanel != 3 && showPanel != 4 && showPanel != 5 && showPanel != 6)
        {
            showPanel = 0;
        }

        switch (showPanel)
        {
        case 1:
            ShowBuilder();
            break;

        case 2:
            ShowContent();
            break;

        case 3:
            ShowSettings();
            break;

        case 4:
            ShowImports();
            break;

        case 5:
            ShowChanges();
            break;

        case 0:
        default:
            ShowAccount();
            break;
        }
    }
        public override void OnGUIAvatar(VRC_AvatarDescriptor avatar)
        {
            EditorGUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle);
            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();

            GUI.enabled = (EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows ||
                           EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64) &&
                          (_builder.NoGuiErrorsOrIssues() || Core.APIUser.CurrentUser.developerType ==
                           Core.APIUser.DeveloperType.Internal);

            GUILayout.Label("Offline Testing", VRCSdkControlPanel.infoGuiStyle);
            if (GUI.enabled)
            {
                GUILayout.Label(
                    "Before uploading your avatar you may build and test it in the VRChat client. Other users will not able to see the test avatar.",
                    VRCSdkControlPanel.infoGuiStyle);
            }
            else
            {
                GUILayout.Label(
                    "(Not available for Android build target)",
                    VRCSdkControlPanel.infoGuiStyle);
            }

            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            if (GUILayout.Button("Build & Test"))
            {
                if (Core.APIUser.CurrentUser.canPublishAvatars)
                {
                    VRC_SdkBuilder.ExportAndTestAvatarBlueprint(avatar.gameObject);

                    EditorUtility.DisplayDialog("VRChat SDK", "Test Avatar Built", "OK");
                }
                else
                {
                    VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            EditorGUILayout.Separator();

            EditorGUILayout.BeginVertical(VRCSdkControlPanel.boxGuiStyle);
            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(GUILayout.Width(300));
            EditorGUILayout.Space();

            GUILayout.Label("Online Publishing", VRCSdkControlPanel.infoGuiStyle);
            GUILayout.Label(
                "In order for other people to see your avatar in VRChat it must be built and published to our game servers.",
                VRCSdkControlPanel.infoGuiStyle);

            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUILayout.Width(200));
            EditorGUILayout.Space();

            GUI.enabled = _builder.NoGuiErrorsOrIssues() ||
                          Core.APIUser.CurrentUser.developerType == Core.APIUser.DeveloperType.Internal;
            if (GUILayout.Button(VRCSdkControlPanel.GetBuildAndPublishButtonString()))
            {
                bool buildBlocked = !VRCBuildPipelineCallbacks.OnVRCSDKBuildRequested(VRCSDKRequestedBuildType.Avatar);
                if (!buildBlocked)
                {
                    if (Core.APIUser.CurrentUser.canPublishAvatars)
                    {
                        EnvConfig.FogSettings originalFogSettings = EnvConfig.GetFogSettings();
                        EnvConfig.SetFogSettings(
                            new EnvConfig.FogSettings(EnvConfig.FogSettings.FogStrippingMode.Custom, true, true, true));

#if UNITY_ANDROID
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", true);
#else
                        EditorPrefs.SetBool("VRC.SDKBase_StripAllShaders", false);
#endif

                        VRC_SdkBuilder.shouldBuildUnityPackage = VRCSdkControlPanel.FutureProofPublishEnabled;
                        VRC_SdkBuilder.ExportAndUploadAvatarBlueprint(avatar.gameObject);

                        EnvConfig.SetFogSettings(originalFogSettings);

                        // this seems to workaround a Unity bug that is clearing the formatting of two levels of Layout
                        // when we call the upload functions
                        return;
                    }
                    else
                    {
                        VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
                    }
                }
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.Space();

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            GUI.enabled = true;
        }
        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);
                        }
                    }
                }
            }

            List <Component> componentsToRemove = AvatarValidation.FindIllegalComponents(avatar.gameObject).ToList();

            // create a list of the PipelineSaver component(s)
            List <Component> toRemoveSilently = new List <Component>();

            foreach (Component c in componentsToRemove)
            {
                if (c.GetType().Name == "PipelineSaver")
                {
                    toRemoveSilently.Add(c);
                }
            }

            // delete PipelineSaver(s) from the list of the Components we will destroy now
            foreach (Component c in toRemoveSilently)
            {
                componentsToRemove.Remove(c);
            }

            HashSet <string> componentsToRemoveNames = new HashSet <string>();
            List <Component> toRemove = componentsToRemove as List <Component> ?? componentsToRemove;

            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);
            CheckAvatarMeshesForMeshReadWriteSetting(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);
        }
예제 #13
0
    void OnGUI()
    {
        if (window == null)
        {
            window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
            InitializeStyles();
        }

        if (_bannerImage == null)
        {
            _bannerImage = AssetDatabase.LoadAssetAtPath("Assets/VRCSDK/Dependencies/VRChat/SdkGraphics/SDK_Panel_Banner.png", typeof(Texture2D)) as Texture2D;
        }

        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        GUILayout.BeginVertical();

        GUILayout.Box(_bannerImage);

        if (Application.isPlaying)
        {
            GUI.enabled = false;
            GUILayout.Space(20);
            EditorGUILayout.LabelField("Unity Application is running ...\nStop it to access the Control Panel", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
            GUI.enabled = true;
            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            return;
        }

        EditorGUILayout.Space();

        EnvConfig.SetActiveSDKDefines();

        int showPanel = GUILayout.Toolbar(VRCSettings.ActiveWindowPanel, _toolbarLabels, APIUser.IsLoggedIn ? _toolbarOptionsLoggedIn : _toolbarOptionsNotLoggedIn, null, GUILayout.Width(SdkWindowWidth));

        // Only show Account or Settings panels if not logged in
        if (APIUser.IsLoggedIn == false && showPanel != 3)
        {
            showPanel = 0;
        }

        if (showPanel != VRCSettings.ActiveWindowPanel)
        {
            VRCSettings.ActiveWindowPanel = showPanel;
        }

        GUILayout.EndVertical();
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        switch (showPanel)
        {
        case 1:
            ShowBuilders();
            break;

        case 2:
            ShowContent();
            break;

        case 3:
            ShowSettings();
            break;

        case 0:
        default:
            ShowAccount();
            break;
        }
    }