RiverModelerInspector.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // River Modeler
  2. // Staggart Creations (http://staggart.xyz)
  3. // Copyright protected under Unity Asset Store EULA
  4. // Copying or referencing source code for the production of new asset store content is strictly prohibited.
  5. using System;
  6. using sc.modeling.river.runtime;
  7. using sc.modeling.water.common.editor;
  8. using UnityEditor;
  9. using UnityEditor.EditorTools;
  10. using UnityEditor.SceneManagement;
  11. using UnityEngine;
  12. #if SPLINES
  13. using UnityEngine.Splines;
  14. using UnityEditor.Splines;
  15. #endif
  16. namespace sc.modeling.river.editor
  17. {
  18. [CustomEditor(typeof(RiverModeler))]
  19. [CanEditMultipleObjects]
  20. public class RiverModelerInspector : Editor
  21. {
  22. //References
  23. private SerializedProperty splineContainer;
  24. private SerializedProperty splineChangeMode;
  25. private SerializedProperty rebuildTriggers;
  26. private SerializedProperty meshFilter;
  27. private SerializedProperty orderInLayer;
  28. private SerializedProperty settings;
  29. private SerializedProperty triangulation;
  30. private SerializedProperty shape;
  31. private SerializedProperty displacement;
  32. private SerializedProperty uv;
  33. private SerializedProperty foam;
  34. private SerializedProperty transparency;
  35. private SerializedProperty debugMode;
  36. private SerializedProperty microVerseSettings;
  37. private SerializedProperty exportToFBX;
  38. private SerializedProperty fbxFilePath;
  39. private SerializedProperty onPreRebuild;
  40. private SerializedProperty onPostRebuild;
  41. private bool isPrefab;
  42. #if MICROVERSE_SPLINES
  43. private const bool MICROVERSE_INSTALLED = true;
  44. #else
  45. private const bool MICROVERSE_INSTALLED = false;
  46. #endif
  47. private static bool ExpandMesh
  48. {
  49. get => SessionState.GetBool("RM_EXPAND_MESH", true);
  50. set => SessionState.SetBool("RM_EXPAND_MESH", value);
  51. }
  52. private static bool ExpandEvents
  53. {
  54. get => SessionState.GetBool("RM_EXPAND_EVENTS", false);
  55. set => SessionState.SetBool("RM_EXPAND_EVENTS", value);
  56. }
  57. private static bool ExpandMV
  58. {
  59. get => SessionState.GetBool("RM_EXPAND_MV", false);
  60. set => SessionState.SetBool("RM_EXPAND_MV", value);
  61. }
  62. private static bool ExpandExport
  63. {
  64. get => SessionState.GetBool("RM_EXPAND_EXPORT", false);
  65. set => SessionState.SetBool("RM_EXPAND_EXPORT", value);
  66. }
  67. private RiverModeler.ChangeFlags changeFlags;
  68. private bool requiresRebuild = false;
  69. private System.Diagnostics.Stopwatch rebuildTimer;
  70. private void OnEnable()
  71. {
  72. rebuildTimer = new System.Diagnostics.Stopwatch();
  73. #if SPLINES
  74. splineContainer = serializedObject.FindProperty("splineContainer");
  75. splineChangeMode = serializedObject.FindProperty("splineChangeMode");
  76. #endif
  77. rebuildTriggers = serializedObject.FindProperty("rebuildTriggers");
  78. meshFilter = serializedObject.FindProperty("meshFilter");
  79. orderInLayer = serializedObject.FindProperty("orderInLayer");
  80. settings = serializedObject.FindProperty("settings");
  81. {
  82. triangulation = settings.FindPropertyRelative("triangulation");
  83. shape = settings.FindPropertyRelative("shape");
  84. displacement = settings.FindPropertyRelative("displacement");
  85. uv = settings.FindPropertyRelative("uv");
  86. foam = settings.FindPropertyRelative("foam");
  87. transparency = settings.FindPropertyRelative("transparency");
  88. }
  89. debugMode = serializedObject.FindProperty("debugMode");
  90. #pragma warning disable CS0162
  91. if (MICROVERSE_INSTALLED)
  92. {
  93. microVerseSettings = serializedObject.FindProperty("microVerseSettings");
  94. }
  95. #pragma warning restore CS0162
  96. isPrefab = PrefabUtility.IsPartOfPrefabInstance((RiverModeler)target) || PrefabStageUtility.GetCurrentPrefabStage();
  97. //may be too annoying, since a Spline Mesher prefab may serve as a settings container
  98. //isPrefab |= PrefabUtility.GetPrefabAssetType(target) != PrefabAssetType.NotAPrefab;
  99. exportToFBX = serializedObject.FindProperty("exportToFBX");
  100. fbxFilePath = serializedObject.FindProperty("fbxFilePath");
  101. onPreRebuild = serializedObject.FindProperty("onPreRebuild");
  102. onPostRebuild = serializedObject.FindProperty("onPostRebuild");
  103. }
  104. private void OnDisable()
  105. {
  106. ExportToFBX();
  107. }
  108. public override void OnInspectorGUI()
  109. {
  110. //base.OnInspectorGUI();
  111. UI.DrawHeader(Asset.NAME, Asset.VERSION);
  112. #if !SPLINES || !MATHEMATICS
  113. #if !SPLINES
  114. EditorGUILayout.HelpBox("The Spline package isn't installed, please install this through the Package Manager", MessageType.Error);
  115. #endif
  116. #if !MATHEMATICS
  117. EditorGUILayout.HelpBox("The Mathematics package isn't installed or outdated, please install this through the Package Manager", MessageType.Error);
  118. #endif
  119. return;
  120. #else
  121. //Reset
  122. changeFlags = RiverModeler.ChangeFlags.All;
  123. requiresRebuild = false;
  124. serializedObject.Update();
  125. EditorGUI.BeginChangeCheck();
  126. EditorGUILayout.LabelField("Source", EditorStyles.boldLabel);
  127. using (new EditorGUILayout.HorizontalScope())
  128. {
  129. EditorGUI.BeginChangeCheck();
  130. EditorGUILayout.PropertyField(splineContainer);
  131. if (EditorGUI.EndChangeCheck())
  132. {
  133. if (splineContainer.objectReferenceValue)
  134. {
  135. foreach (var target in targets)
  136. {
  137. ((RiverModeler)target).ValidateData(splineContainer.objectReferenceValue as SplineContainer);
  138. }
  139. changeFlags |= RiverModeler.ChangeFlags.Spline;
  140. requiresRebuild = true;
  141. }
  142. }
  143. EditorGUI.BeginDisabledGroup(splineContainer.objectReferenceValue == null);
  144. if (GUILayout.Button("Edit", EditorStyles.miniButton, GUILayout.Width(50f)))
  145. {
  146. Selection.activeGameObject = ((RiverModeler)target).splineContainer.gameObject;
  147. EditorApplication.delayCall += ToolManager.SetActiveContext<SplineToolContext>;
  148. }
  149. EditorGUI.EndDisabledGroup();
  150. }
  151. if (splineContainer.objectReferenceValue)
  152. {
  153. EditorGUILayout.Space();
  154. int rebuildTrigger = rebuildTriggers.intValue;
  155. if ((rebuildTrigger & (int)RiverModeler.RebuildTriggers.OnStart) != (int)RiverModeler.RebuildTriggers.OnStart && isPrefab)
  156. {
  157. EditorGUILayout.HelpBox("Procedurally created geometry cannot be used in a prefab." +
  158. "\n\nMesh data will be lost when the prefab is used outside of the scene it was created in." +
  159. "\n\nExport the created mesh to an FBX file, and use that instead. Or enable the \"On Start()\" option under Rebuild Triggers.", MessageType.Warning);
  160. }
  161. EditorGUILayout.Space();
  162. using (new EditorGUILayout.HorizontalScope())
  163. {
  164. EditorGUILayout.PropertyField(rebuildTriggers, new GUIContent("Rebuild triggers", rebuildTriggers.tooltip), GUILayout.Width(EditorGUIUtility.labelWidth + 140f));
  165. if (GUILayout.Button(new GUIContent(" Rebuild", EditorGUIUtility.IconContent("d_Refresh").image)))
  166. {
  167. Rebuild();
  168. }
  169. GUILayout.FlexibleSpace();
  170. }
  171. if ((rebuildTrigger & (int)RiverModeler.RebuildTriggers.OnSplineChanged) == (int)RiverModeler.RebuildTriggers.OnSplineChanged)
  172. {
  173. EditorGUI.indentLevel++;
  174. EditorGUILayout.PropertyField(splineChangeMode, new GUIContent("Spline Change Mode", splineChangeMode.tooltip), GUILayout.MaxWidth(EditorGUIUtility.labelWidth + 140f));
  175. EditorGUI.indentLevel--;
  176. }
  177. EditorGUILayout.Space();
  178. EditorGUILayout.LabelField("Destination", EditorStyles.boldLabel);
  179. EditorGUI.BeginChangeCheck();
  180. EditorGUILayout.PropertyField(meshFilter);
  181. if (EditorGUI.EndChangeCheck())
  182. {
  183. changeFlags |= RiverModeler.ChangeFlags.Geometry;
  184. requiresRebuild = true;
  185. }
  186. EditorGUI.indentLevel++;
  187. EditorGUILayout.PropertyField(orderInLayer);
  188. EditorGUI.indentLevel--;
  189. EditorGUILayout.Space();
  190. EditorGUILayout.PropertyField(debugMode, new GUIContent("Visualize"));
  191. EditorGUILayout.Space();
  192. if (meshFilter.objectReferenceValue)
  193. {
  194. ExpandMesh = EditorGUILayout.Foldout(ExpandMesh, "Mesh generation settings", EditorStyles.foldoutHeader);
  195. EditorGUILayout.BeginFadeGroup(ExpandMesh ? 1f : 0.001f);
  196. {
  197. EditorGUI.indentLevel++;
  198. EditorGUI.BeginChangeCheck();
  199. EditorGUILayout.PropertyField(triangulation);
  200. EditorGUILayout.PropertyField(shape);
  201. EditorGUILayout.PropertyField(displacement);
  202. if (EditorGUI.EndChangeCheck())
  203. {
  204. changeFlags |= RiverModeler.ChangeFlags.Geometry;
  205. requiresRebuild = true;
  206. }
  207. if (displacement.isExpanded)
  208. {
  209. using (new EditorGUILayout.HorizontalScope())
  210. {
  211. GUILayout.FlexibleSpace();
  212. if (GUILayout.Button(new GUIContent("Open Editor")))
  213. {
  214. ToolManager.SetActiveTool<ScaleTool>();
  215. }
  216. }
  217. }
  218. EditorGUI.BeginChangeCheck();
  219. EditorGUILayout.PropertyField(uv, new GUIContent("UV Coordinates", "Controls the UV coordinates, used for tiling and animating textures in the shader"));
  220. if (EditorGUI.EndChangeCheck())
  221. {
  222. changeFlags |= RiverModeler.ChangeFlags.VertexAttribute;
  223. requiresRebuild = true;
  224. }
  225. EditorGUI.BeginChangeCheck();
  226. EditorGUILayout.PropertyField(foam);
  227. if (EditorGUI.EndChangeCheck())
  228. {
  229. changeFlags |= RiverModeler.ChangeFlags.VertexAttribute | RiverModeler.ChangeFlags.Foam;
  230. requiresRebuild = true;
  231. }
  232. if (foam.isExpanded)
  233. {
  234. using (new EditorGUILayout.HorizontalScope())
  235. {
  236. GUILayout.FlexibleSpace();
  237. if (GUILayout.Button(new GUIContent("Open Editor")))
  238. {
  239. ToolManager.SetActiveTool<FoamTool>();
  240. }
  241. }
  242. }
  243. EditorGUI.BeginChangeCheck();
  244. EditorGUILayout.PropertyField(transparency);
  245. if (EditorGUI.EndChangeCheck())
  246. {
  247. changeFlags |= RiverModeler.ChangeFlags.VertexAttribute;
  248. requiresRebuild = true;
  249. }
  250. if (transparency.isExpanded)
  251. {
  252. using (new EditorGUILayout.HorizontalScope())
  253. {
  254. GUILayout.FlexibleSpace();
  255. if (GUILayout.Button(new GUIContent("Open Editor")))
  256. {
  257. ToolManager.SetActiveTool<TransparencyTool>();
  258. }
  259. }
  260. }
  261. EditorGUI.indentLevel--;
  262. }
  263. EditorGUILayout.EndFadeGroup();
  264. EditorGUILayout.Space();
  265. ExpandEvents = EditorGUILayout.Foldout(ExpandEvents, "Events", EditorStyles.foldoutHeader);
  266. EditorGUILayout.BeginFadeGroup(ExpandEvents ? 1f : 0.001f);
  267. {
  268. EditorGUILayout.PropertyField(onPreRebuild);
  269. EditorGUILayout.PropertyField(onPostRebuild);
  270. }
  271. EditorGUILayout.EndFadeGroup();
  272. EditorGUILayout.Space();
  273. //EditorGUILayout.LabelField("MicroVerse integration", EditorStyles.boldLabel);
  274. ExpandMV = EditorGUILayout.Foldout(ExpandMV, "MicroVerse integration", EditorStyles.foldoutHeader);
  275. EditorGUILayout.BeginFadeGroup(ExpandMV ? 1f : 0.001f);
  276. {
  277. #pragma warning disable CS0162
  278. if (MICROVERSE_INSTALLED)
  279. {
  280. microVerseSettings.isExpanded = true;
  281. EditorGUI.BeginChangeCheck();
  282. EditorGUILayout.PropertyField(microVerseSettings, true);
  283. if (EditorGUI.EndChangeCheck())
  284. {
  285. changeFlags |= RiverModeler.ChangeFlags.Data;
  286. foreach (var target in targets)
  287. {
  288. ((RiverModeler)target).UpdateMicroVerseSpline();
  289. }
  290. }
  291. }
  292. else
  293. {
  294. UI.DrawActionBox("The MicroVerse Splines module isn't installed", MessageType.Info, "Open Store Page", () => UI.OpenStorePage(232974, "rivermodelereditor"));
  295. EditorGUILayout.HelpBox("The MicroVerse asset, with its Splines module, allows for rivers to carve into the terrain and create a bank on its sides.\n\n" +
  296. "When installed, updates to this component's will trigger the MicroVerse spline to update as well.", MessageType.None);
  297. }
  298. #pragma warning restore CS0162
  299. }
  300. EditorGUILayout.EndFadeGroup();
  301. EditorGUILayout.Space();
  302. ExpandExport = EditorGUILayout.Foldout(ExpandExport, "FBX Export", EditorStyles.foldoutHeader);
  303. EditorGUILayout.BeginFadeGroup(ExpandExport ? 1f : 0.001f);
  304. {
  305. EditorGUILayout.PropertyField(exportToFBX, new GUIContent("Enable", exportToFBX.tooltip));
  306. if (exportToFBX.boolValue == false)
  307. {
  308. EditorGUILayout.HelpBox("\n" +
  309. "Saving the generated mesh to an FBX file is recommended to benefit from file compression and segmenting scene contents for optimal loading performance " +
  310. "(in large and complex scenes)." +
  311. "\n\n" +
  312. "For use in Prefabs, this is required to retain the mesh data. It is otherwise saved and available purely only the scene." +
  313. "\n", MessageType.Info);
  314. }
  315. if (exportToFBX.boolValue)
  316. {
  317. EditorGUILayout.Space();
  318. #if FBX_EXPORTER
  319. EditorGUILayout.BeginHorizontal();
  320. {
  321. EditorGUILayout.PropertyField(fbxFilePath, new GUIContent("File path", fbxFilePath.tooltip));
  322. if (GUILayout.Button(new GUIContent("...", "Browse"), GUILayout.Width(50f)))
  323. {
  324. string newPath = EditorUtility.SaveFilePanelInProject("FBX destination folder", target.name, "fbx", "Choose FBX file location");
  325. if (newPath != string.Empty)
  326. {
  327. fbxFilePath.stringValue = newPath;
  328. }
  329. }
  330. if (GUILayout.Button("Ping", GUILayout.Width(60f)))
  331. {
  332. UnityEngine.Object exportedObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(fbxFilePath.stringValue);
  333. if (exportedObject)
  334. {
  335. EditorGUIUtility.PingObject(exportedObject);
  336. var fileInfo = new System.IO.FileInfo(fbxFilePath.stringValue);
  337. float bytes = (fileInfo.Length / 1024f) / 1024f;
  338. SceneView.lastActiveSceneView.ShowNotification(new GUIContent($"File size: {(float)System.Math.Round(bytes, 2)}mb"), 1f);
  339. }
  340. }
  341. }
  342. EditorGUILayout.EndHorizontal();
  343. if (fbxFilePath.stringValue == string.Empty)
  344. {
  345. EditorGUILayout.HelpBox("File path should not be blank", MessageType.Warning);
  346. }
  347. EditorGUILayout.HelpBox("The mesh will be re-exported if it has changed once the inspector is closed.", MessageType.Info);
  348. #else
  349. UI.DrawActionBox("The FBX Exporter package is not installed, it is required for this functionality", MessageType.Error, "Install", PackageInstaller.InstallFBXExporter);
  350. #endif
  351. }
  352. }
  353. EditorGUILayout.EndFadeGroup();
  354. }
  355. else
  356. {
  357. EditorGUILayout.HelpBox("A Mesh Filter for output is required. Assign one you wish to generate a river mesh for.", MessageType.Error);
  358. }
  359. }
  360. else
  361. {
  362. EditorGUILayout.HelpBox("A spline container must be assigned. Assign one you wish to generate a river from.", MessageType.Warning);
  363. }
  364. if (EditorGUI.EndChangeCheck())
  365. {
  366. serializedObject.ApplyModifiedProperties();
  367. if (requiresRebuild)
  368. {
  369. if(((RiverModeler)target).rebuildTriggers.HasFlag(RiverModeler.RebuildTriggers.OnUIChange))
  370. {
  371. Rebuild();
  372. }
  373. }
  374. }
  375. EditorGUILayout.Space();
  376. if (((RiverModeler)target).rebuildTriggers.HasFlag(RiverModeler.RebuildTriggers.OnUIChange) == false)
  377. {
  378. EditorGUILayout.HelpBox("Auto-rebuilding on UI change is disabled (see Rebuilder Triggers)", MessageType.None);
  379. using (new EditorGUILayout.HorizontalScope())
  380. {
  381. GUILayout.FlexibleSpace();
  382. if (GUILayout.Button("Rebuild now"))
  383. {
  384. Rebuild();
  385. }
  386. GUILayout.FlexibleSpace();
  387. }
  388. }
  389. #endif
  390. UI.DrawFooter();
  391. #if WGT_DEV
  392. using (new EditorGUILayout.HorizontalScope())
  393. {
  394. EditorGUILayout.LabelField($"Rebuild time: {rebuildTimer.ElapsedMilliseconds}ms", EditorStyles.miniLabel);
  395. }
  396. #endif
  397. }
  398. private void Rebuild()
  399. {
  400. requiresRebuild = false;
  401. if (Asset.Preferences.RebuildEveryFrame)
  402. {
  403. RebuildTargets();
  404. }
  405. else
  406. {
  407. EditorApplication.delayCall += RebuildTargets;
  408. }
  409. }
  410. private void RebuildTargets()
  411. {
  412. rebuildTimer.Reset();
  413. rebuildTimer.Start();
  414. foreach (var m_target in targets)
  415. {
  416. ((RiverModeler)m_target).Rebuild(changeFlags);
  417. }
  418. rebuildTimer.Stop();
  419. }
  420. private void ExportToFBX()
  421. {
  422. #if FBX_EXPORTER
  423. foreach (var target in targets)
  424. {
  425. if (((RiverModeler)target).exportToFBX)
  426. {
  427. RiverModeler component = ((RiverModeler)target);
  428. if (component.meshFilter && component.meshFilter.sharedMesh)
  429. {
  430. if (EditorUtility.IsDirty(component.meshFilter.sharedMesh) || !EditorUtility.IsPersistent(component.meshFilter.sharedMesh))
  431. {
  432. //Debug.Log($"Mesh ({component.meshFilter.sharedMesh.name}) was changed, exporting to {component.fbxFilePath}");
  433. EditorUtilities.FBX.SaveToFileAndReference(component.gameObject, component.fbxFilePath);
  434. }
  435. /*
  436. else
  437. {
  438. Debug.Log($"Mesh ({component.meshFilter.sharedMesh.name}) hasn't changed, so won't require exporting");
  439. }
  440. */
  441. }
  442. }
  443. }
  444. #endif
  445. }
  446. }
  447. }