Advertisement
Guest User

Untitled

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