Advertisement
Guest User

Untitled

a guest
Feb 18th, 2019
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 46.20 KB | None | 0 0
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using VRC.Core;
  6. using VRCSDK2;
  7.  
  8. [ExecuteInEditMode]
  9. public class VRC_SdkControlPanel : EditorWindow
  10. {
  11. static VRC_SdkControlPanel window;
  12.  
  13. public static System.Action _EnableSpatialization = null; // assigned in AutoAddONSPAudioSourceComponents
  14.  
  15. const string kCantPublishContent = "Before you can upload avatars or worlds, you will need to spend some time in VRChat.";
  16. const string kCantPublishAvatars = "Before you can upload avatars, you will need to spend some time in VRChat.";
  17. const string kCantPublishWorlds = "Before you can upload worlds, you will need to spend some time in VRChat.";
  18. private const string kAvatarOptimizationTipsURL = "https://docs.vrchat.com/docs/avatar-optimizing-tips";
  19. private const string kAvatarRigRequirementsURL = "https://docs.vrchat.com/docs/rig-requirements";
  20.  
  21. static Texture _perfIcon_VeryGood;
  22. static Texture _perfIcon_Good;
  23. static Texture _perfIcon_Medium;
  24. static Texture _perfIcon_Bad;
  25. static Texture _perfIcon_VeryBad;
  26.  
  27.  
  28. [MenuItem("VRChat SDK/Show Build Control Panel")]
  29. static void Init()
  30. {
  31. if (!RemoteConfig.IsInitialized())
  32. {
  33. VRC.Core.API.SetOnlineMode(true, "vrchat");
  34. RemoteConfig.Init(() => Init());
  35. return;
  36. }
  37.  
  38. window = (VRC_SdkControlPanel)EditorWindow.GetWindow(typeof(VRC_SdkControlPanel));
  39. window.titleContent.text = "VRChat";
  40.  
  41. window.ResetIssues();
  42.  
  43. window.Show();
  44. }
  45.  
  46. bool UseDevApi
  47. {
  48. get
  49. {
  50. return VRC.Core.API.GetApiUrl() == API.devApiUrl;
  51. }
  52. }
  53.  
  54. private void ResetIssues()
  55. {
  56. GUIErrors.Clear();
  57. GUIInfos.Clear();
  58. GUIWarnings.Clear();
  59. GUILinks.Clear();
  60. GUIStats.Clear();
  61. checkedForIssues = false;
  62. }
  63.  
  64. private void OnFocus()
  65. {
  66. ResetIssues();
  67. }
  68.  
  69. private void OnLostFocus()
  70. {
  71. ResetIssues();
  72. }
  73.  
  74. bool checkedForIssues = false;
  75.  
  76. Dictionary<Object, List<string>> GUIErrors = new Dictionary<Object, List<string>>();
  77. Dictionary<Object, List<string>> GUIWarnings = new Dictionary<Object, List<string>>();
  78. Dictionary<Object, List<string>> GUIInfos = new Dictionary<Object, List<string>>();
  79. Dictionary<Object, List<string>> GUILinks = new Dictionary<Object, List<string>>();
  80. Dictionary<Object, List<KeyValuePair<string, PerformanceRating>>> GUIStats = new Dictionary<Object, List<KeyValuePair<string, PerformanceRating>>>();
  81.  
  82. void AddToReport(Dictionary<Object, List<string>> report, Object subject, string output)
  83. {
  84. if (subject == null)
  85. subject = this;
  86. if (!report.ContainsKey(subject))
  87. report.Add(subject, new List<string>());
  88. report[subject].Add(output);
  89. }
  90.  
  91. void OnGUIError(Object subject, string output)
  92. {
  93. if (!checkedForIssues)
  94. AddToReport(GUIErrors, subject, output);
  95. }
  96.  
  97. void OnGUIWarning(Object subject, string output)
  98. {
  99. if (!checkedForIssues)
  100. AddToReport(GUIWarnings, subject, output);
  101. }
  102.  
  103. void OnGUIInformation(Object subject, string output)
  104. {
  105. if (!checkedForIssues)
  106. AddToReport(GUIInfos, subject, output);
  107. }
  108.  
  109. void OnGUILink(Object subject, string output, string link)
  110. {
  111. if (!checkedForIssues)
  112. AddToReport(GUILinks, subject, output + "\n" + link);
  113. }
  114.  
  115. void OnGUIStat(Object subject, string output, PerformanceRating rating)
  116. {
  117. if (checkedForIssues)
  118. return;
  119.  
  120. if (subject == null)
  121. subject = this;
  122. if (!GUIStats.ContainsKey(subject))
  123. GUIStats.Add(subject, new List<KeyValuePair<string, PerformanceRating>>());
  124. GUIStats[subject].Add(new KeyValuePair<string, PerformanceRating>(output, rating));
  125. }
  126.  
  127. VRCSDK2.VRC_SceneDescriptor[] scenes;
  128. VRCSDK2.VRC_AvatarDescriptor[] avatars;
  129. Vector2 scrollPos;
  130.  
  131. private bool showAvatarPerformanceDetails
  132. {
  133. get { return EditorPrefs.GetBool("VRCSDK2_showAvatarPerformanceDetails", false); }
  134. set { EditorPrefs.SetBool("VRCSDK2_showAvatarPerformanceDetails", value); }
  135. }
  136.  
  137. void Update()
  138. {
  139. Repaint();
  140. }
  141.  
  142. void OnGUI()
  143. {
  144. if (window == null)
  145. window = (VRC_SdkControlPanel)EditorWindow.GetWindow(typeof(VRC_SdkControlPanel));
  146.  
  147. if (!VRC.AccountEditorWindow.OnShowStatus())
  148. return;
  149.  
  150. if (Application.isPlaying)
  151. {
  152. EditorGUILayout.LabelField("You cannot edit your VRChat data while the Unity Application is running");
  153. return;
  154. }
  155.  
  156. EditorGUILayout.Space();
  157. EditorGUILayout.LabelField("General", EditorStyles.boldLabel);
  158.  
  159. EditorGUILayout.Space();
  160.  
  161. ShowBuildControls();
  162.  
  163. //window.Repaint();
  164. }
  165.  
  166. void ShowBuildControls()
  167. {
  168. if (!checkedForIssues)
  169. EnvConfig.ConfigurePlayerSettings();
  170.  
  171. EditorGUILayout.LabelField("Client Version Date", VRC.Core.SDKClientUtilities.GetTestClientVersionDate());
  172. EditorGUILayout.LabelField("SDK Version Date", VRC.Core.SDKClientUtilities.GetSDKVersionDate());
  173.  
  174. /**
  175. // Commented this out 12/12/2017 bc client no longer produces version files, resulting in this warning always appearing - Graham
  176. if (!VRC.Core.SDKClientUtilities.IsClientNewerThanSDK())
  177. {
  178. OnGUIWarning(null, "Your SDK is newer than the VRChat client you're testing with. Some SDK features may not work as expected. You can change VRC clients in VRChat SDK/Settings.");
  179. }
  180. **/
  181.  
  182. if (VRC.Core.RemoteConfig.IsInitialized())
  183. {
  184. string sdkUnityVersion = VRC.Core.RemoteConfig.GetString("sdkUnityVersion");
  185. if (Application.unityVersion != sdkUnityVersion)
  186. {
  187. OnGUIWarning(null, "You are not using the recommended Unity version for the VRChat SDK. Content built with this version may not work correctly. Please use Unity " + sdkUnityVersion);
  188. }
  189. }
  190.  
  191. scenes = (VRCSDK2.VRC_SceneDescriptor[])VRC.Tools.FindSceneObjectsOfTypeAll<VRCSDK2.VRC_SceneDescriptor>();
  192. List<VRCSDK2.VRC_AvatarDescriptor> allavatars = VRC.Tools.FindSceneObjectsOfTypeAll<VRCSDK2.VRC_AvatarDescriptor>().ToList();
  193. // select only the active avatars
  194. avatars = allavatars.Where(av => av.gameObject.activeInHierarchy).ToArray();
  195.  
  196. // if (scenes.Length > 0 && avatars.Length > 0)
  197. // {
  198. // GameObject[] gos = new GameObject[avatars.Length];
  199. // for (int i = 0; i < avatars.Length; ++i)
  200. // gos[i] = avatars[i].gameObject;
  201. // OnGUIError(null, "a unity scene containing a VRChat Scene Descriptor should not also contain avatars.");
  202. // }
  203. // else if (scenes.Length > 1)
  204. // {
  205. // GameObject[] gos = new GameObject[scenes.Length];
  206. // for (int i = 0; i < scenes.Length; ++i)
  207. // gos[i] = scenes[i].gameObject;
  208. // OnGUIError(null, "a unity scene containing a VRChat Scene Descriptor should only contain one scene descriptor.");
  209. // }
  210. // else
  211. if (scenes.Length == 1)
  212. {
  213. GUILayout.Label("Scene Options", EditorStyles.boldLabel);
  214. scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
  215. EditorGUILayout.Space();
  216. try
  217. {
  218. EditorGUI.BeginChangeCheck();
  219.  
  220. if (!checkedForIssues)
  221. OnGUISceneCheck(scenes[0]);
  222.  
  223. if (EditorGUI.EndChangeCheck())
  224. {
  225. EditorUtility.SetDirty(scenes[0]);
  226. UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
  227. }
  228.  
  229. OnGUIScene(scenes[0]);
  230. OnGUIShowIssues(scenes[0]);
  231. }
  232. catch (System.Exception)
  233. {
  234. }
  235. EditorGUILayout.EndScrollView();
  236. }
  237. else if (avatars.Length > 0)
  238. {
  239. GUILayout.Label("Avatar Options", EditorStyles.boldLabel);
  240. bool prevShowPerfDetails = showAvatarPerformanceDetails;
  241. bool showPerfDetails = EditorGUILayout.ToggleLeft("Show All Avatar Performance Details", prevShowPerfDetails);
  242. if (showPerfDetails != prevShowPerfDetails)
  243. {
  244. showAvatarPerformanceDetails = showPerfDetails;
  245. ResetIssues();
  246. }
  247.  
  248. scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
  249. foreach (var av in avatars)
  250. {
  251. EditorGUI.BeginChangeCheck();
  252.  
  253. EditorGUILayout.Space();
  254. if (!checkedForIssues)
  255. OnGUIAvatarCheck(av);
  256.  
  257. OnGUIAvatar(av);
  258.  
  259. if (EditorGUI.EndChangeCheck())
  260. {
  261. EditorUtility.SetDirty(av);
  262. UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
  263. }
  264. }
  265. EditorGUILayout.EndScrollView();
  266. }
  267. else
  268. {
  269. OnGUIError(null, "Please add a scene descriptor or avatar descriptor to your project.");
  270. }
  271. OnGUIShowIssues();
  272. checkedForIssues = true;
  273. }
  274.  
  275. bool showLayerHelp = false;
  276. int numClients = 1;
  277.  
  278. void CheckUploadChanges(VRCSDK2.VRC_SceneDescriptor scene)
  279. {
  280. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_scene_changed") &&
  281. UnityEditor.EditorPrefs.GetBool("VRCSDK2_scene_changed"))
  282. {
  283. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_scene_changed");
  284.  
  285. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_capacity"))
  286. {
  287. scene.capacity = UnityEditor.EditorPrefs.GetInt("VRCSDK2_capacity");
  288. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_capacity");
  289. }
  290. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_content_sex"))
  291. {
  292. scene.contentSex = UnityEditor.EditorPrefs.GetBool("VRCSDK2_content_sex");
  293. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_content_sex");
  294. }
  295. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_content_violence"))
  296. {
  297. scene.contentViolence = UnityEditor.EditorPrefs.GetBool("VRCSDK2_content_violence");
  298. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_content_violence");
  299. }
  300. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_content_gore"))
  301. {
  302. scene.contentGore = UnityEditor.EditorPrefs.GetBool("VRCSDK2_content_gore");
  303. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_content_gore");
  304. }
  305. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_content_other"))
  306. {
  307. scene.contentOther = UnityEditor.EditorPrefs.GetBool("VRCSDK2_content_other");
  308. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_content_other");
  309. }
  310. if (UnityEditor.EditorPrefs.HasKey("VRCSDK2_release_public"))
  311. {
  312. scene.releasePublic = UnityEditor.EditorPrefs.GetBool("VRCSDK2_release_public");
  313. UnityEditor.EditorPrefs.DeleteKey("VRCSDK2_release_public");
  314. }
  315.  
  316. EditorUtility.SetDirty(scene);
  317. UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
  318. }
  319. }
  320.  
  321. bool ShouldShowLightmapWarning
  322. {
  323. get
  324. {
  325. const string GraphicsSettingsAssetPath = "ProjectSettings/GraphicsSettings.asset";
  326. SerializedObject graphicsManager = new SerializedObject(UnityEditor.AssetDatabase.LoadAllAssetsAtPath(GraphicsSettingsAssetPath)[0]);
  327. SerializedProperty lightmapStripping = graphicsManager.FindProperty("m_LightmapStripping");
  328. return lightmapStripping.enumValueIndex == 0;
  329. }
  330. }
  331.  
  332. bool ShouldShowFogWarning
  333. {
  334. get
  335. {
  336. const string GraphicsSettingsAssetPath = "ProjectSettings/GraphicsSettings.asset";
  337. SerializedObject graphicsManager = new SerializedObject(UnityEditor.AssetDatabase.LoadAllAssetsAtPath(GraphicsSettingsAssetPath)[0]);
  338. SerializedProperty lightmapStripping = graphicsManager.FindProperty("m_FogStripping");
  339. return lightmapStripping.enumValueIndex == 0;
  340. }
  341. }
  342.  
  343. void OnGUIShowIssues(Object subject = null)
  344. {
  345. if (subject == null)
  346. subject = this;
  347.  
  348. if (GUIErrors.ContainsKey(subject))
  349. foreach (string error in GUIErrors[subject].Where(s => !string.IsNullOrEmpty(s)))
  350. EditorGUILayout.HelpBox(error, MessageType.Error);
  351. if (GUIWarnings.ContainsKey(subject))
  352. foreach (string error in GUIWarnings[subject].Where(s => !string.IsNullOrEmpty(s)))
  353. EditorGUILayout.HelpBox(error, MessageType.Warning);
  354.  
  355. if (GUIStats.ContainsKey(subject))
  356. {
  357. GUIStyle style = GUI.skin.GetStyle("HelpBox");
  358.  
  359. foreach (var kvp in GUIStats[subject].Where(k => k.Value == PerformanceRating.VeryBad))
  360. GUILayout.Box(new GUIContent(kvp.Key, GetPerformanceIconForRating(kvp.Value)), style);
  361.  
  362. foreach (var kvp in GUIStats[subject].Where(k => k.Value == PerformanceRating.Bad))
  363. GUILayout.Box(new GUIContent(kvp.Key, GetPerformanceIconForRating(kvp.Value)), style);
  364.  
  365. foreach (var kvp in GUIStats[subject].Where(k => k.Value == PerformanceRating.Medium))
  366. GUILayout.Box(new GUIContent(kvp.Key, GetPerformanceIconForRating(kvp.Value)), style);
  367.  
  368. foreach (var kvp in GUIStats[subject].Where(k => k.Value == PerformanceRating.Good || k.Value == PerformanceRating.VeryGood))
  369. GUILayout.Box(new GUIContent(kvp.Key, GetPerformanceIconForRating(kvp.Value)), style);
  370. }
  371.  
  372. if (GUIInfos.ContainsKey(subject))
  373. foreach (string error in GUIInfos[subject].Where(s => !string.IsNullOrEmpty(s)))
  374. EditorGUILayout.HelpBox(error, MessageType.Info);
  375. if (GUILinks.ContainsKey(subject))
  376. foreach (string error in GUILinks[subject].Where(s => !string.IsNullOrEmpty(s)))
  377. {
  378. var s = error.Split('\n');
  379. if (GUILayout.Button(s[0]))
  380. Application.OpenURL(s[1]);
  381. }
  382. }
  383.  
  384. private Texture GetPerformanceIconForRating(PerformanceRating value)
  385. {
  386. if (_perfIcon_VeryGood == null)
  387. _perfIcon_VeryGood = Resources.Load<Texture>("PerformanceIcons/Perf_Great_32");
  388. if (_perfIcon_Good == null)
  389. _perfIcon_Good = Resources.Load<Texture>("PerformanceIcons/Perf_Good_32");
  390. if (_perfIcon_Medium == null)
  391. _perfIcon_Medium = Resources.Load<Texture>("PerformanceIcons/Perf_Medium_32");
  392. if (_perfIcon_Bad == null)
  393. _perfIcon_Bad = Resources.Load<Texture>("PerformanceIcons/Perf_Poor_32");
  394. if (_perfIcon_VeryBad == null)
  395. _perfIcon_VeryBad = Resources.Load<Texture>("PerformanceIcons/Perf_Horrible_32");
  396.  
  397. switch (value)
  398. {
  399. case PerformanceRating.None:
  400. case PerformanceRating.VeryGood:
  401. return _perfIcon_VeryGood;
  402. case PerformanceRating.Good:
  403. return _perfIcon_Good;
  404. case PerformanceRating.Medium:
  405. return _perfIcon_Medium;
  406. case PerformanceRating.Bad:
  407. return _perfIcon_Bad;
  408. case PerformanceRating.VeryBad:
  409. return _perfIcon_VeryBad;
  410. }
  411.  
  412. return _perfIcon_VeryGood;
  413. }
  414.  
  415. void OnGUISceneCheck(VRCSDK2.VRC_SceneDescriptor scene)
  416. {
  417. CheckUploadChanges(scene);
  418.  
  419. EditorGUILayout.InspectorTitlebar(true, scene.gameObject);
  420.  
  421. if (VRC.Core.APIUser.CurrentUser != null && VRC.Core.APIUser.CurrentUser.hasScriptingAccess && !CustomDLLMaker.DoesScriptDirExist())
  422. {
  423. CustomDLLMaker.CreateDirectories();
  424. }
  425.  
  426. Vector3 g = Physics.gravity;
  427. if (g.x != 0.0f || g.z != 0.0f)
  428. 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.");
  429. if (g.y > 0)
  430. OnGUIWarning(scene, "Gravity vector is not straight down, inverted or zero gravity will make walking extremely difficult.");
  431. if (g.y == 0)
  432. 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.");
  433.  
  434. #if PLAYMAKER
  435. if (VRCSDK2.VRC_PlaymakerHelper.ValidatePlaymaker() == false)
  436. OnGUIError(scene, VRCSDK2.VRC_PlaymakerHelper.GetErrors());
  437. #endif
  438.  
  439. if (!UpdateLayers.AreLayersSetup())
  440. OnGUIError(scene, "Layers are not yet configured for VRChat. Please press the 'Setup Layers for VRChat' button above to apply layer settings and enable Test/Publish.");
  441.  
  442. if (UpdateLayers.AreLayersSetup() && !UpdateLayers.IsCollisionLayerMatrixSetup())
  443. OnGUIError(scene, "Physics Collision Layer Matrix is not yet configured for VRChat. Please press the 'Setup Collision Layer Matrix for VRChat' button above to apply collision settings and enable Test/Publish.");
  444.  
  445. // warn those without scripting access if they choose to script locally
  446. if (VRC.Core.APIUser.CurrentUser != null && !VRC.Core.APIUser.CurrentUser.hasScriptingAccess && CustomDLLMaker.DoesScriptDirExist())
  447. {
  448. OnGUIWarning(scene, "Your account does not have permissions to upload custom scripts. You can test locally but need to contact VRChat to publish your world with scripts.");
  449. }
  450.  
  451. foreach (VRCSDK2.VRC_DataStorage ds in GameObject.FindObjectsOfType<VRCSDK2.VRC_DataStorage>())
  452. {
  453. VRCSDK2.VRC_ObjectSync os = ds.GetComponent<VRCSDK2.VRC_ObjectSync>();
  454. if (os != null && os.SynchronizePhysics)
  455. OnGUIWarning(scene, ds.name + " has a VRC_DataStorage and VRC_ObjectSync, with SynchronizePhysics enabled.");
  456. }
  457.  
  458. // auto create VRCScript dir for those with access
  459. if (VRC.Core.APIUser.CurrentUser != null && VRC.Core.APIUser.CurrentUser.hasScriptingAccess && !CustomDLLMaker.DoesScriptDirExist())
  460. {
  461. CustomDLLMaker.CreateDirectories();
  462. }
  463.  
  464. if (scene.UpdateTimeInMS < (int)(1000f / 90f * 3f))
  465. OnGUIWarning(scene, "Room has a very fast update rate; experience may suffer with many users.");
  466. }
  467.  
  468. void OnGUIScene(VRCSDK2.VRC_SceneDescriptor scene)
  469. {
  470. string lastUrl = VRC_SdkBuilder.GetLastUrl();
  471. bool lastBuildPresent = lastUrl != null;
  472.  
  473. string worldVersion = "-1";
  474. PipelineManager[] pms = (PipelineManager[])VRC.Tools.FindSceneObjectsOfTypeAll<PipelineManager>();
  475. if (pms.Length == 1 && !string.IsNullOrEmpty(pms[0].blueprintId))
  476. {
  477. if (scene.apiWorld == null)
  478. {
  479. ApiWorld world = API.FromCacheOrNew<ApiWorld>(pms[0].blueprintId);
  480. world.Fetch(null, false,
  481. (c) => scene.apiWorld = c.Model as ApiWorld,
  482. (c) =>
  483. {
  484. if (c.Code == 404)
  485. {
  486. Debug.LogErrorFormat("Could not load world {0} because it didn't exist.", pms[0].blueprintId);
  487. ApiCache.Invalidate<ApiWorld>(pms[0].blueprintId);
  488. }
  489. else
  490. Debug.LogErrorFormat("Could not load world {0} because {1}", pms[0].blueprintId, c.Error);
  491. });
  492. scene.apiWorld = world;
  493. }
  494. worldVersion = (scene.apiWorld as ApiWorld).version.ToString();
  495. }
  496. EditorGUILayout.LabelField("World Version: " + worldVersion);
  497.  
  498. EditorGUILayout.Space();
  499.  
  500. if (!UpdateLayers.AreLayersSetup() && GUILayout.Button("Setup Layers for VRChat"))
  501. {
  502. bool doIt = EditorUtility.DisplayDialog("Setup Layers for VRChat", "This adds all VRChat layers to your project and pushes any custom layers down the layer list. If you have custom layers assigned to gameObjects, you'll need to reassign them. Are you sure you want to continue?", "Do it!", "Don't do it");
  503. if (doIt)
  504. {
  505. UpdateLayers.SetupEditorLayers();
  506. }
  507. }
  508.  
  509. if (UpdateLayers.AreLayersSetup() && !UpdateLayers.IsCollisionLayerMatrixSetup() && GUILayout.Button("Setup Collision Layer Matrix for VRChat"))
  510. {
  511. bool doIt = EditorUtility.DisplayDialog("Setup Collision Layer Matrix for VRChat", "This will setup the correct physics collisions in the PhysicsManager for VRChat layers. Are you sure you want to continue?", "Do it!", "Don't do it");
  512. if (doIt)
  513. {
  514. UpdateLayers.SetupCollisionLayerMatrix();
  515. }
  516. }
  517.  
  518. scene.autoSpatializeAudioSources = EditorGUILayout.ToggleLeft("Apply 3D spatialization to AudioSources automatically at runtime (override settings by adding an ONSPAudioSource component to game object)", scene.autoSpatializeAudioSources);
  519. if (GUILayout.Button("Enable 3D spatialization on all 3D AudioSources in scene now"))
  520. {
  521. bool doIt = EditorUtility.DisplayDialog("Enable Spatialization", "This will add an ONSPAudioSource script to every 3D AudioSource in the current scene, and enable default settings for spatialization. Are you sure you want to continue?", "Do it!", "Don't do it");
  522. if (doIt)
  523. {
  524. if (_EnableSpatialization != null)
  525. _EnableSpatialization();
  526. else
  527. Debug.LogError("VrcSdkControlPanel: EnableSpatialization callback not found!");
  528. }
  529. }
  530.  
  531. GUI.enabled = (GUIErrors.Count == 0 && checkedForIssues);
  532. EditorGUILayout.Space();
  533. EditorGUILayout.BeginVertical();
  534. EditorGUILayout.LabelField("Test", EditorStyles.boldLabel);
  535. numClients = EditorGUILayout.IntField("Number of Clients", numClients);
  536. if (lastBuildPresent == false)
  537. GUI.enabled = false;
  538. if (GUILayout.Button("Last Build"))
  539. {
  540. VRC_SdkBuilder.shouldBuildUnityPackage = false;
  541. VRC_SdkBuilder.numClientsToLaunch = numClients;
  542. VRC_SdkBuilder.RunLastExportedSceneResource();
  543. }
  544. if (APIUser.CurrentUser.hasSuperPowers)
  545. {
  546. if (GUILayout.Button("Copy Test URL"))
  547. {
  548. TextEditor te = new TextEditor();
  549. te.text = lastUrl;
  550. te.SelectAll();
  551. te.Copy();
  552. }
  553. }
  554. if (lastBuildPresent == false)
  555. GUI.enabled = true;
  556. if (GUILayout.Button("New Build"))
  557. {
  558. EnvConfig.ConfigurePlayerSettings();
  559. VRC_SdkBuilder.shouldBuildUnityPackage = false;
  560. VRC.AssetExporter.CleanupUnityPackageExport(); // force unity package rebuild on next publish
  561. VRC_SdkBuilder.numClientsToLaunch = numClients;
  562. VRC_SdkBuilder.PreBuildBehaviourPackaging();
  563. VRC_SdkBuilder.ExportSceneResourceAndRun();
  564. }
  565. EditorGUILayout.EndVertical();
  566. EditorGUILayout.Space();
  567. EditorGUILayout.BeginVertical();
  568. EditorGUILayout.LabelField("Publish", EditorStyles.boldLabel);
  569. if (lastBuildPresent == false)
  570. GUI.enabled = false;
  571. if (GUILayout.Button("Last Build"))
  572. {
  573. if (APIUser.CurrentUser.canPublishWorlds)
  574. {
  575. VRC_SdkBuilder.shouldBuildUnityPackage = VRC.AccountEditorWindow.FutureProofPublishEnabled;
  576. VRC_SdkBuilder.UploadLastExportedSceneBlueprint();
  577. }
  578. else
  579. {
  580. ShowContentPublishPermissionsDialog();
  581. }
  582. }
  583. if (lastBuildPresent == false)
  584. GUI.enabled = true;
  585. if (GUILayout.Button("New Build"))
  586. {
  587. if (APIUser.CurrentUser.canPublishWorlds)
  588. {
  589. EnvConfig.ConfigurePlayerSettings();
  590. VRC_SdkBuilder.shouldBuildUnityPackage = VRC.AccountEditorWindow.FutureProofPublishEnabled;
  591. VRC_SdkBuilder.PreBuildBehaviourPackaging();
  592. VRC_SdkBuilder.ExportAndUploadSceneBlueprint();
  593. }
  594. else
  595. {
  596. ShowContentPublishPermissionsDialog();
  597. }
  598. }
  599. EditorGUILayout.EndVertical();
  600. GUI.enabled = true;
  601. }
  602.  
  603. void OnGUISceneLayer(int layer, string name, string description)
  604. {
  605. if (LayerMask.LayerToName(layer) != name)
  606. OnGUIError(null, "Layer " + layer + " must be renamed to '" + name + "'");
  607.  
  608. if (showLayerHelp)
  609. OnGUIInformation(null, "Layer " + layer + " " + name + "\n" + description);
  610. }
  611.  
  612. bool IsAncestor(Transform ancestor, Transform child)
  613. {
  614. bool found = false;
  615. Transform thisParent = child.parent;
  616. while (thisParent != null)
  617. {
  618. if (thisParent == ancestor) { found = true; break; }
  619. thisParent = thisParent.parent;
  620. }
  621.  
  622. return found;
  623. }
  624.  
  625. List<Transform> FindBonesBetween(Transform top, Transform bottom)
  626. {
  627. List<Transform> list = new List<Transform>();
  628. if (top == null || bottom == null) return list;
  629. Transform bt = top.parent;
  630. while (bt != bottom && bt != null)
  631. {
  632. list.Add(bt);
  633. bt = bt.parent;
  634. }
  635. return list;
  636. }
  637.  
  638. // Attempts to remap a mecanim rig so that the upper chest bone
  639. // is blank, by moving the upper chest bone to chest and rebuilding
  640. // CURRENTLY DOES NOT WORK!
  641. void UpperChestFix(VRCSDK2.VRC_AvatarDescriptor ad, GameObject avObj, Animator anim)
  642. {
  643. // if upper chest was mapped we need to reconfigure rig
  644. // by moving upper chest to chest
  645. Transform pelvis = anim.GetBoneTransform(HumanBodyBones.Hips);
  646. Transform upchest = anim.GetBoneTransform(HumanBodyBones.UpperChest);
  647. Transform chest = anim.GetBoneTransform(HumanBodyBones.Chest);
  648. Transform torso = anim.GetBoneTransform(HumanBodyBones.Spine);
  649. Avatar origAvatar = anim.avatar;
  650.  
  651. if (upchest != null)
  652. {
  653. // get every child transform of the animator
  654. Transform[] allBones = anim.GetComponentsInChildren<Transform>();
  655.  
  656. // get a list of the extra spine bones between spine and pelvis
  657. List<Transform> extras = FindBonesBetween(torso, pelvis);
  658.  
  659. HumanDescription desc = new HumanDescription();
  660. desc.upperArmTwist = 0.5f;
  661. desc.lowerArmTwist = 0.5f;
  662. desc.upperLegTwist = 0.5f;
  663. desc.lowerLegTwist = 0.5f;
  664. desc.armStretch = 0.05f;
  665. desc.legStretch = 0.05f;
  666. desc.feetSpacing = 0.0f;
  667. List<HumanBone> hbList = new List<HumanBone>();
  668. List<SkeletonBone> sbList = new List<SkeletonBone>();
  669. HumanBodyBones[] hbbArray = (HumanBodyBones[])System.Enum.GetValues(typeof(HumanBodyBones));
  670. Dictionary<Transform, string> hbbDict = new Dictionary<Transform, string>();
  671.  
  672. for (int i = 0; i < hbbArray.Length; i++)
  673. {
  674. Transform t = anim.GetBoneTransform(hbbArray[i]);
  675. string n = hbbArray[i].ToString();
  676. if (t != null && n != "LastBone")
  677. {
  678. hbbDict[t] = n;
  679. //Debug.LogError("Dictionary Added:"+hbbArray[i].ToString());
  680. }
  681. }
  682.  
  683. foreach (Transform bt in allBones)
  684. {
  685. // map the human bones
  686. if (hbbDict.Keys.Contains(bt))
  687. {
  688. string hbName = hbbDict[bt];
  689. //Debug.LogError("Processing: "+hbName);
  690. if (hbName != "Spine" && bt != null && !extras.Contains(bt))
  691. {
  692. if (bt == upchest) hbName = "Chest";
  693. else if (bt == chest) hbName = "Spine";
  694. HumanBone hb = new HumanBone();
  695. hb.boneName = bt.name;
  696. hb.humanName = hbName;
  697. //Debug.Log("Mapped human bone:" + hb.humanName + " to " + hb.boneName);
  698. hbList.Add(hb);
  699. }
  700. else
  701. {
  702. //Debug.LogError("Skipped:" + hbbDict[bt]);
  703. }
  704. }
  705.  
  706. if (bt != null)
  707. {
  708. // THESE POSITIONS/ROTATIONS MUST BE FOR TPOSE !!!
  709. SkeletonBone sb = new SkeletonBone();
  710. sb.name = bt.name;
  711. sb.position = bt.position;
  712. sb.rotation = bt.rotation;
  713. sb.scale = bt.localScale;
  714. sbList.Add(sb);
  715. }
  716. }
  717.  
  718. // add any root bones above hip
  719. Transform root = pelvis.parent;
  720. while (root != null && root != anim.transform)
  721. {
  722. // THESE POSITIONS/ROTATIONS MUST BE FOR TPOSE !!!
  723. SkeletonBone sb = new SkeletonBone();
  724. sb.name = root.name;
  725. sb.position = root.position;
  726. sb.rotation = root.rotation;
  727. sb.scale = root.localScale;
  728. sbList.Add(sb);
  729. root = root.parent;
  730. }
  731.  
  732. desc.human = hbList.ToArray();
  733. desc.skeleton = sbList.ToArray();
  734. anim.avatar = AvatarBuilder.BuildHumanAvatar(avObj, desc);
  735. if (anim.avatar.isValid && anim.avatar.isHuman)
  736. {
  737. anim.avatar.name = "{ADJUSTED}" + origAvatar.name;
  738. // shift all the bone mappings
  739. torso = chest;
  740. chest = upchest;
  741. upchest = null;
  742. }
  743. else
  744. {
  745. OnGUIError(ad, "Automatic rig adjustment on " + origAvatar.name + " failed. You will need to manually configure the humanoid rig. Make sure the UpperChest slot is empty.");
  746. anim.avatar = origAvatar;
  747. return;
  748. }
  749. }
  750.  
  751. if (anim.avatar.name.StartsWith("{ADJUSTED}"))
  752. {
  753. OnGUIWarning(ad, "Your rig has the UPPERCHEST mapped in the Humanoid Rig, and was automatically corrected " +
  754. "to use the CHEST instead. If you run into issues we recommend leaving the " +
  755. "UPPERCHEST blank and mapping your top spine bone to the CHEST.");
  756. }
  757. }
  758.  
  759. bool AnalyzeIK(VRCSDK2.VRC_AvatarDescriptor ad, GameObject avObj, Animator anim)
  760. {
  761. bool hasHead = false;
  762. bool hasFeet = false;
  763. bool hasHands = false;
  764. bool hasThreeFingers = false;
  765. //bool hasToes = false;
  766. bool correctSpineHierarchy = false;
  767. bool correctArmHierarchy = false;
  768. bool correctLegHierarchy = false;
  769.  
  770. bool status = true;
  771.  
  772. Transform head = anim.GetBoneTransform(HumanBodyBones.Head);
  773. Transform lfoot = anim.GetBoneTransform(HumanBodyBones.LeftFoot);
  774. Transform rfoot = anim.GetBoneTransform(HumanBodyBones.RightFoot);
  775. Transform lhand = anim.GetBoneTransform(HumanBodyBones.LeftHand);
  776. Transform rhand = anim.GetBoneTransform(HumanBodyBones.RightHand);
  777.  
  778. hasHead = null != head;
  779. hasFeet = (null != lfoot && null != rfoot);
  780. hasHands = (null != lhand && null != rhand);
  781.  
  782. if (!hasHead || !hasFeet || !hasHands)
  783. {
  784. OnGUIError(ad, "Humanoid avatar must have head, hands and feet bones mapped.");
  785. return false;
  786. }
  787.  
  788. Transform lthumb = anim.GetBoneTransform(HumanBodyBones.LeftThumbProximal);
  789. Transform lindex = anim.GetBoneTransform(HumanBodyBones.LeftIndexProximal);
  790. Transform lmiddle = anim.GetBoneTransform(HumanBodyBones.LeftMiddleProximal);
  791. Transform rthumb = anim.GetBoneTransform(HumanBodyBones.RightThumbProximal);
  792. Transform rindex = anim.GetBoneTransform(HumanBodyBones.RightIndexProximal);
  793. Transform rmiddle = anim.GetBoneTransform(HumanBodyBones.RightMiddleProximal);
  794.  
  795. hasThreeFingers = null != lthumb && null != lindex && null != lmiddle && null != rthumb && null != rindex && null != rmiddle;
  796.  
  797. if (!hasThreeFingers)
  798. {
  799. // although its only a warning, we return here because the rest
  800. // of the analysis is for VRIK
  801. OnGUIWarning(ad, "Thumb, Index, and Middle finger bones are not mapped, Full-Body IK will be disabled.");
  802. status = false;
  803. }
  804.  
  805. if (anim.GetBoneTransform(HumanBodyBones.UpperChest) != null)
  806. {
  807. OnGUIError(ad, "Your rig has the UPPERCHEST mapped in the Humanoid Rig. This will cause problems with IK.");
  808. return false;
  809. }
  810.  
  811. Transform pelvis = anim.GetBoneTransform(HumanBodyBones.Hips);
  812. Transform chest = anim.GetBoneTransform(HumanBodyBones.Chest);
  813. Transform torso = anim.GetBoneTransform(HumanBodyBones.Spine);
  814.  
  815. Transform neck = anim.GetBoneTransform(HumanBodyBones.Neck);
  816. Transform lclav = anim.GetBoneTransform(HumanBodyBones.LeftShoulder);
  817. Transform rclav = anim.GetBoneTransform(HumanBodyBones.RightShoulder);
  818.  
  819. if (null == neck || null == lclav || null == rclav || null == pelvis || null == torso || null == chest)
  820. {
  821. OnGUIError(ad, "Spine hierarchy missing elements, make sure that Pelvis, Spine, Chest, Neck and Shoulders are mapped.");
  822. return false;
  823. }
  824.  
  825. correctSpineHierarchy = lclav.parent == chest && rclav.parent == chest && neck.parent == chest;
  826.  
  827. if (!correctSpineHierarchy)
  828. {
  829. OnGUIError(ad, "Spine hierarchy incorrect. Make sure that the parent of both Shoulders and the Neck is the Chest.");
  830. return false;
  831. }
  832.  
  833. Transform lshoulder = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm);
  834. Transform lelbow = anim.GetBoneTransform(HumanBodyBones.LeftLowerArm);
  835. Transform rshoulder = anim.GetBoneTransform(HumanBodyBones.RightUpperArm);
  836. Transform relbow = anim.GetBoneTransform(HumanBodyBones.RightLowerArm);
  837.  
  838. correctArmHierarchy = lshoulder.GetChild(0) == lelbow && lelbow.GetChild(0) == lhand &&
  839. rshoulder.GetChild(0) == relbow && relbow.GetChild(0) == rhand;
  840.  
  841. if (!correctArmHierarchy)
  842. {
  843. OnGUIWarning(ad, "LowerArm is not first child of UpperArm or Hand is not first child of LowerArm: you may have problems with Forearm rotations.");
  844. status = false;
  845. }
  846.  
  847. Transform lhip = anim.GetBoneTransform(HumanBodyBones.LeftUpperLeg);
  848. Transform lknee = anim.GetBoneTransform(HumanBodyBones.LeftLowerLeg);
  849. Transform rhip = anim.GetBoneTransform(HumanBodyBones.RightUpperLeg);
  850. Transform rknee = anim.GetBoneTransform(HumanBodyBones.RightLowerLeg);
  851.  
  852. correctLegHierarchy = lhip.GetChild(0) == lknee && lknee.GetChild(0) == lfoot &&
  853. rhip.GetChild(0) == rknee && rknee.GetChild(0) == rfoot;
  854.  
  855. if (!correctLegHierarchy)
  856. {
  857. OnGUIWarning(ad, "LowerLeg is not first child of UpperLeg or Foot is not first child of LowerLeg: you may have problems with Shin rotations.");
  858. status = false;
  859. }
  860.  
  861. if (!(IsAncestor(pelvis, rfoot) && IsAncestor(pelvis, lfoot) && IsAncestor(pelvis, lhand) || IsAncestor(pelvis, rhand) || IsAncestor(pelvis, lhand)))
  862. {
  863. OnGUIWarning(ad, "This avatar has a split heirarchy (Hips bone is not the ancestor of all humanoid bones). IK may not work correctly.");
  864. status = false;
  865. }
  866.  
  867. // if thigh bone rotations diverge from 180 from hip bone rotations, full-body tracking/ik does not work well
  868. Vector3 hipLocalUp = pelvis.InverseTransformVector(Vector3.up);
  869. Vector3 legLDir = lhip.TransformVector(hipLocalUp);
  870. Vector3 legRDir = rhip.TransformVector(hipLocalUp);
  871. float angL = Vector3.Angle(Vector3.up, legLDir);
  872. float angR = Vector3.Angle(Vector3.up, legRDir);
  873. if (angL < 175f || angR < 175f)
  874. {
  875. string angle = string.Format("{0:F1}", Mathf.Min(angL, angR));
  876. OnGUIWarning(ad, "The angle between pelvis and thigh bones should be close to 180 degrees (this avatar's angle is " + angle + "). Your avatar may not work well with full-body IK and Tracking.");
  877. status = false;
  878. }
  879. return status;
  880. }
  881.  
  882. void OnGUIAvatarCheck(VRCSDK2.VRC_AvatarDescriptor avatar)
  883. {
  884. AvatarPerformanceStats perfStats = AvatarPerformance.CalculatePerformanceStats(avatar.Name, avatar.gameObject);
  885.  
  886. OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.Overall);
  887. OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.PolyCount);
  888. OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.AABB);
  889.  
  890. var eventHandler = avatar.GetComponentInChildren<VRCSDK2.VRC_EventHandler>();
  891. if (eventHandler != null)
  892. {
  893. OnGUIError(avatar, "This avatar contains an EventHandler, which is not currently supported in VRChat.");
  894. }
  895.  
  896. if (avatar.lipSync == VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape && avatar.VisemeSkinnedMesh == null)
  897. OnGUIError(avatar, "This avatar uses Visemes but the Face Mesh is not specified.");
  898.  
  899. var anim = avatar.GetComponent<Animator>();
  900. if (anim == null)
  901. {
  902. OnGUIWarning(avatar, "This avatar does not contain an animator, and will not animate in VRChat.");
  903. }
  904. else if (anim.isHuman == false)
  905. {
  906. OnGUIWarning(avatar, "This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set.");
  907. }
  908. else if (avatar.gameObject.activeInHierarchy == false)
  909. {
  910. OnGUIError(avatar, "Your avatar is disabled in the scene hierarchy!");
  911. }
  912. else
  913. {
  914. Transform foot = anim.GetBoneTransform(HumanBodyBones.LeftFoot);
  915. Transform shoulder = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm);
  916. if (foot == null)
  917. OnGUIError(avatar, "Your avatar is humanoid, but it's feet aren't specified!");
  918. if (shoulder == null)
  919. OnGUIError(avatar, "Your avatar is humanoid, but it's upper arms aren't specified!");
  920.  
  921. if (foot != null && shoulder != null)
  922. {
  923. Vector3 footPos = foot.position - avatar.transform.position;
  924. if (footPos.y < 0)
  925. OnGUIWarning(avatar, "Avatar feet are beneath the avatar's origin (the floor). That's probably not what you want.");
  926. Vector3 shoulderPosition = shoulder.position - avatar.transform.position;
  927. if (shoulderPosition.y < 0.2f)
  928. OnGUIError(avatar, "This avatar is too short. The minimum is 20cm shoulder height.");
  929. else if (shoulderPosition.y < 1.0f)
  930. OnGUIWarning(avatar, "This avatar is short. This is probably shorter than you want.");
  931. else if (shoulderPosition.y > 5.0f)
  932. OnGUIWarning(avatar, "This avatar is too tall. The maximum is 5m shoulder height.");
  933. else if (shoulderPosition.y > 2.5f)
  934. OnGUIWarning(avatar, "This avatar is tall. This is probably taller than you want.");
  935. }
  936.  
  937. if (AnalyzeIK(avatar, avatar.gameObject, anim) == false)
  938. OnGUILink(avatar, "See Avatar Rig Requirements for more information.", kAvatarRigRequirementsURL);
  939. }
  940.  
  941. IEnumerable<Component> componentsToRemove = VRCSDK2.AvatarValidation.FindIllegalComponents(avatar.gameObject);
  942. HashSet<string> componentsToRemoveNames = new HashSet<string>();
  943. foreach (Component c in componentsToRemove)
  944. {
  945. if (componentsToRemoveNames.Contains(c.GetType().Name) == false)
  946. componentsToRemoveNames.Add(c.GetType().Name);
  947. }
  948.  
  949. // if (componentsToRemoveNames.Count > 0)
  950. // OnGUIError(avatar, "The following component types are found on the Avatar and will be removed by the client: " + string.Join(", ", componentsToRemoveNames.ToArray()));
  951.  
  952. if (VRCSDK2.AvatarValidation.EnforceAudioSourceLimits(avatar.gameObject).Count > 0)
  953. OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.");
  954.  
  955. if (VRCSDK2.AvatarValidation.EnforceAvatarStationLimits(avatar.gameObject).Count > 0)
  956. OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary.");
  957.  
  958. if (avatar.gameObject.GetComponentInChildren<Camera>() != null)
  959. OnGUIWarning(avatar, "Cameras are removed from non-local avatars at runtime.");
  960.  
  961. foreach (AvatarPerformanceCategory perfCategory in System.Enum.GetValues(typeof(AvatarPerformanceCategory)))
  962. {
  963. if (perfCategory == AvatarPerformanceCategory.Overall ||
  964. perfCategory == AvatarPerformanceCategory.PolyCount ||
  965. perfCategory == AvatarPerformanceCategory.AABB ||
  966. perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
  967. {
  968. continue;
  969. }
  970.  
  971. OnGUIPerformanceInfo(avatar, perfStats, perfCategory);
  972. }
  973.  
  974. OnGUILink(avatar, "Avatar Optimization Tips", kAvatarOptimizationTipsURL);
  975. }
  976.  
  977. void OnGUIPerformanceInfo(Object avatar, AvatarPerformanceStats perfStats, AvatarPerformanceCategory perfCategory)
  978. {
  979. string text;
  980. PerformanceInfoDisplayLevel displayLevel;
  981. PerformanceRating rating = perfStats.GetPerformanceRatingForCategory(perfCategory);
  982. perfStats.GetSDKPerformanceInfoText(out text, out displayLevel, perfCategory, rating);
  983.  
  984. if (displayLevel == PerformanceInfoDisplayLevel.None || string.IsNullOrEmpty(text))
  985. return;
  986.  
  987. if (displayLevel == PerformanceInfoDisplayLevel.Verbose && showAvatarPerformanceDetails)
  988. OnGUIStat(avatar, text, rating);
  989. if (displayLevel == PerformanceInfoDisplayLevel.Info)
  990. OnGUIStat(avatar, text, rating);
  991. if (displayLevel == PerformanceInfoDisplayLevel.Warning)
  992. OnGUIStat(avatar, text, rating);
  993. if (displayLevel == PerformanceInfoDisplayLevel.Error)
  994. {
  995. OnGUIStat(avatar, text, rating);
  996. OnGUIError(avatar, text);
  997. }
  998. }
  999.  
  1000. void OnGUIAvatar(VRCSDK2.VRC_AvatarDescriptor avatar)
  1001. {
  1002. EditorGUILayout.InspectorTitlebar(avatar.gameObject.activeInHierarchy, avatar.gameObject);
  1003.  
  1004. GUI.enabled = (GUIErrors.Count == 0 && checkedForIssues) || APIUser.CurrentUser.developerType == APIUser.DeveloperType.Internal;
  1005. EditorGUILayout.BeginHorizontal();
  1006. if (GUILayout.Button("Build & Publish"))
  1007. {
  1008. if (APIUser.CurrentUser.canPublishAvatars)
  1009. {
  1010. VRC_SdkBuilder.shouldBuildUnityPackage = VRC.AccountEditorWindow.FutureProofPublishEnabled;
  1011. VRC_SdkBuilder.ExportAndUploadAvatarBlueprint(avatar.gameObject);
  1012. }
  1013. else
  1014. {
  1015. ShowContentPublishPermissionsDialog();
  1016. }
  1017. }
  1018. EditorGUILayout.EndHorizontal();
  1019. GUI.enabled = true;
  1020.  
  1021. OnGUIShowIssues(avatar);
  1022. }
  1023.  
  1024. public static void ShowContentPublishPermissionsDialog()
  1025. {
  1026. if (!RemoteConfig.IsInitialized())
  1027. {
  1028. RemoteConfig.Init(() => ShowContentPublishPermissionsDialog());
  1029. return;
  1030. }
  1031.  
  1032. string message = RemoteConfig.GetString("sdkNotAllowedToPublishMessage");
  1033. int result = UnityEditor.EditorUtility.DisplayDialogComplex("VRChat SDK", message, "Developer FAQ", "VRChat Discord", "OK");
  1034. if (result == 0)
  1035. {
  1036. ShowDeveloperFAQ();
  1037. }
  1038. if (result == 1)
  1039. {
  1040. ShowVRChatDiscord();
  1041. }
  1042. }
  1043.  
  1044. [MenuItem("VRChat SDK/Help/Developer FAQ")]
  1045. public static void ShowDeveloperFAQ()
  1046. {
  1047. if (!RemoteConfig.IsInitialized())
  1048. {
  1049. RemoteConfig.Init(() => ShowDeveloperFAQ());
  1050. return;
  1051. }
  1052.  
  1053. Application.OpenURL(RemoteConfig.GetString("sdkDeveloperFaqUrl"));
  1054. }
  1055.  
  1056. [MenuItem("VRChat SDK/Help/VRChat Discord")]
  1057. public static void ShowVRChatDiscord()
  1058. {
  1059. if (!RemoteConfig.IsInitialized())
  1060. {
  1061. RemoteConfig.Init(() => ShowVRChatDiscord());
  1062. return;
  1063. }
  1064.  
  1065. Application.OpenURL(RemoteConfig.GetString("sdkDiscordUrl"));
  1066. }
  1067.  
  1068. [MenuItem("VRChat SDK/Help/Avatar Optimization Tips")]
  1069. public static void ShowAvatarOptimizationTips()
  1070. {
  1071. if (!RemoteConfig.IsInitialized())
  1072. {
  1073. RemoteConfig.Init(() => ShowAvatarOptimizationTips());
  1074. return;
  1075. }
  1076.  
  1077. Application.OpenURL(kAvatarOptimizationTipsURL);
  1078. }
  1079.  
  1080. [MenuItem("VRChat SDK/Help/Avatar Rig Requirements")]
  1081. public static void ShowAvatarRigRequirements()
  1082. {
  1083. if (!RemoteConfig.IsInitialized())
  1084. {
  1085. RemoteConfig.Init(() => ShowAvatarRigRequirements());
  1086. return;
  1087. }
  1088.  
  1089. Application.OpenURL(kAvatarRigRequirementsURL);
  1090. }
  1091. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement