RaiseLowerEditor.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using System.Text;
  6. namespace MTE
  7. {
  8. internal class RaiseLowerEditor : IEditor
  9. {
  10. public int Id { get; } = 1;
  11. public bool Enabled { get; set; } = true;
  12. public string Name { get; } = "RaiseLowerEditor";
  13. public Texture Icon { get; } =
  14. EditorGUIUtility.IconContent("TerrainInspector.TerrainToolRaise").image;
  15. public bool WillEditMesh { get; } = true;
  16. public bool WantMouseMove { get; } = false;
  17. #region Parameters
  18. #region Constant
  19. // default
  20. const float DefaultBrushSize = 4;
  21. const float DefaultSpeed = 1f;
  22. // min/max
  23. const float MinBrushSize = 0.1f;
  24. const float MaxBrushSize = 10f;
  25. const float MinStrength = 0.01f;
  26. const float MaxStrength = 1f;
  27. #endregion
  28. /// <summary>
  29. /// Current brush index (not used)
  30. /// </summary>
  31. public int brushIndex = 0;
  32. /// <summary>
  33. /// Going up/down
  34. /// </summary>
  35. public bool GoingUp = true;
  36. /// <summary>
  37. /// Brush size (cell count)
  38. /// </summary>
  39. private float brushSize;
  40. /// <summary>
  41. /// Brush strength
  42. /// </summary>
  43. private float strength;
  44. /// <summary>
  45. /// Falloff curve
  46. /// </summary>
  47. private AnimationCurve falloffCurve;
  48. /// <summary>
  49. /// Brush size (unit: brush unit)
  50. /// </summary>
  51. public float BrushSize
  52. {
  53. get { return brushSize; }
  54. set
  55. {
  56. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  57. if (!MathEx.AmostEqual(value, brushSize))
  58. {
  59. brushSize = value;
  60. EditorPrefs.SetFloat("MTE_RaiseLowerEditor.brushSize", value);
  61. }
  62. }
  63. }
  64. private float BrushSizeInU3D
  65. {
  66. [System.Diagnostics.DebuggerStepThrough]
  67. get { return BrushSize * Settings.BrushUnit; }
  68. }
  69. /// <summary>
  70. /// Brush strength
  71. /// </summary>
  72. public float Strength
  73. {
  74. get { return strength; }
  75. set
  76. {
  77. value = Mathf.Clamp(value, MinStrength, MaxStrength);
  78. if (!MathEx.AmostEqual(value, strength))
  79. {
  80. strength = value;
  81. EditorPrefs.SetFloat("MTE_RaiseLowerEditor.strength", value);
  82. }
  83. }
  84. }
  85. #endregion
  86. public RaiseLowerEditor()
  87. {
  88. MTEContext.EnableEvent += (sender, args) =>
  89. {
  90. if (MTEContext.editor == this)
  91. {
  92. LoadSavedParamter();
  93. }
  94. };
  95. // Load default parameters
  96. brushSize = DefaultBrushSize;
  97. strength = DefaultSpeed;
  98. falloffCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
  99. }
  100. private void LoadSavedParamter()
  101. {
  102. // Load parameters from the EditorPrefs
  103. brushSize = EditorPrefs.GetFloat("MTE_RaiseLowerEditor.brushSize", DefaultBrushSize);
  104. strength = EditorPrefs.GetFloat("MTE_RaiseLowerEditor.strength", DefaultSpeed);
  105. }
  106. public HashSet<Hotkey> DefineHotkeys()
  107. {
  108. return new HashSet<Hotkey>
  109. {
  110. new Hotkey(this, KeyCode.LeftBracket, () =>
  111. {
  112. BrushSize -= 1;
  113. MTEEditorWindow.Instance.Repaint();
  114. }),
  115. new Hotkey(this, KeyCode.RightBracket, () =>
  116. {
  117. BrushSize += 1;
  118. MTEEditorWindow.Instance.Repaint();
  119. }),
  120. new Hotkey(this, KeyCode.Minus, () =>
  121. {
  122. Strength -= 0.01f;
  123. MTEEditorWindow.Instance.Repaint();
  124. }),
  125. new Hotkey(this, KeyCode.Equals, () =>
  126. {
  127. Strength += 0.01f;
  128. MTEEditorWindow.Instance.Repaint();
  129. }),
  130. new Hotkey(this, KeyCode.Space, () =>
  131. {
  132. //toggle push up/down
  133. GoingUp = !GoingUp;
  134. MTEEditorWindow.Instance.Repaint();
  135. })
  136. };
  137. }
  138. public string Header { get { return StringTable.Get(C.RaiseLower_Header); } }
  139. public string Description { get { return StringTable.Get(C.RaiseLower_Description); } }
  140. public void DoArgsGUI()
  141. {
  142. var defaultGUIColor = GUI.contentColor;
  143. var defaultFontSize = GUI.skin.toggle.fontSize;
  144. GUI.color = GoingUp ? Color.green : Color.green*0.8f;
  145. GUI.skin.button.fontSize = 40;
  146. GUI.enabled = false;
  147. GUILayout.Toggle(GoingUp, GoingUp ? @"⇧" : @"⇩", "Button");
  148. GUI.color = defaultGUIColor;
  149. GUI.skin.button.fontSize = defaultFontSize;
  150. GUI.enabled = true;
  151. //Settings
  152. if (!Settings.CompactGUI)
  153. {
  154. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  155. }
  156. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  157. Strength = EditorGUILayoutEx.Slider(StringTable.Get(C.Strength), "[", "]", Strength, MinStrength, MaxStrength);
  158. EditorGUILayout.BeginHorizontal();
  159. {
  160. var label = new GUIContent(StringTable.Get(C.Falloff));
  161. var size = GUIStyle.none.CalcSize(label);
  162. GUILayout.Label(label, GUILayout.Width(size.x + 10), GUILayout.MinWidth(60));
  163. EditorGUILayout.LabelField("", GUILayout.Width(15));
  164. var rect = GUILayoutUtility.GetRect(128, 128, GUILayout.ExpandWidth(false));
  165. falloffCurve = EditorGUI.CurveField(rect, falloffCurve, Color.green, new Rect(0, 0, 1, 1));
  166. EditorGUILayout.LabelField("", GUILayout.Width(15));
  167. }
  168. EditorGUILayout.EndHorizontal();
  169. }
  170. private readonly List<MeshModifyGroup> modifyGroups = new List<MeshModifyGroup>(4);
  171. private readonly StringBuilder notice = new StringBuilder();
  172. public void OnSceneGUI()
  173. {
  174. notice.Length = 0;
  175. do
  176. {
  177. var e = Event.current;
  178. if (e.commandName == "UndoRedoPerformed")
  179. {
  180. SceneView.RepaintAll();
  181. break;
  182. }
  183. if (!(EditorWindow.mouseOverWindow is SceneView))
  184. {
  185. notice.AppendLine("Mouse not in SceneView.");
  186. break;
  187. }
  188. // do nothing when mouse middle/right button, control/alt key is pressed
  189. if (e.button != 0 || e.control || e.alt)
  190. {
  191. break;
  192. }
  193. HandleUtility.AddDefaultControl(0);
  194. RaycastHit raycastHit;
  195. Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  196. //Debug.Log(string.Format("mouse at ({0}, {1})", e.mousePosition.x, e.mousePosition.y));
  197. if (Physics.Raycast(ray, out raycastHit,
  198. Mathf.Infinity,
  199. 1 << MTEContext.TargetLayer//only hit target layer
  200. ))
  201. {
  202. //check tag
  203. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  204. {
  205. notice.AppendLine("No target was hit.");
  206. break;
  207. }
  208. if (Settings.ShowBrushRect)
  209. {
  210. Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
  211. if (Settings.DebugMode)
  212. {
  213. var oldZTest = Handles.zTest;
  214. Handles.color = Color.red;
  215. Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
  216. Handles.ArrowHandleCap(0, raycastHit.point + new Vector3(0, 200, 0), Quaternion.LookRotation(Vector3.down), 1000f, EventType.Repaint);
  217. Handles.zTest = oldZTest;
  218. }
  219. }
  220. if (!MTEContext.Targets.Any())
  221. {
  222. notice.AppendLine("No targets. Check if editing mesh-terrain(s) match the tag and layer of MTE editor.");
  223. }
  224. else
  225. {
  226. // collect modify group
  227. modifyGroups.Clear();
  228. foreach (var target in MTEContext.Targets)
  229. {
  230. var meshFilter = target.GetComponent<MeshFilter>();
  231. var meshCollider = target.GetComponent<MeshCollider>();
  232. var mesh = meshFilter.sharedMesh;
  233. var hitPointLocal = target.transform.InverseTransformPoint(raycastHit.point);
  234. List<int> vIndex;
  235. List<float> vDistance;
  236. VertexMap.GetAffectedVertex(target, hitPointLocal, BrushSizeInU3D,
  237. out vIndex, out vDistance);
  238. notice.AppendFormat("About to modify {0} vertices in {1}.\n", vIndex.Count, target.name);
  239. if (Settings.ShowAffectedVertex)
  240. {
  241. Utility.ShowAffectedVertices(target, mesh, vIndex);
  242. }
  243. if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
  244. {
  245. if (vIndex.Count != 0)
  246. {
  247. modifyGroups.Add(new MeshModifyGroup(target, mesh, meshCollider, vIndex, vDistance));
  248. }
  249. }
  250. }
  251. //record undo operation for targets that to be modified
  252. if (e.type == EventType.MouseDown)
  253. {
  254. Utility.Record("Raise/Lower", raycastHit.point, this.BrushSizeInU3D);
  255. }
  256. // execute the modification
  257. if (modifyGroups.Count != 0)
  258. {
  259. for (int i = 0; i < modifyGroups.Count; i++)
  260. {
  261. var modifyGroup = modifyGroups[i];
  262. var mesh = modifyGroup.mesh;
  263. var meshCollider = modifyGroup.meshCollider;
  264. var vIndex = modifyGroup.vIndex;
  265. var vDistance = modifyGroup.vDistance;
  266. RaiseLower(mesh, meshCollider, vIndex, vDistance);
  267. notice.AppendFormat("Modified {0} vertices in {1}.\n", vIndex.Count, modifyGroup.gameObject.name);
  268. }
  269. }
  270. if (e.type == EventType.MouseUp)
  271. {
  272. MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
  273. MTEEditorWindow.Instance.HandleMeshSave();
  274. }
  275. }
  276. }
  277. SceneView.RepaintAll();
  278. } while (false);
  279. if (Settings.DebugMode)
  280. {
  281. Handles.BeginGUI();
  282. GUILayout.Label(this.notice.ToString());
  283. Handles.EndGUI();
  284. }
  285. }
  286. /// <summary>
  287. /// Do raise/lower height for selecting vertexes
  288. /// </summary>
  289. /// <param name="mesh">mesh to modify</param>
  290. /// <param name="meshCollider">meshcollider related to the mesh</param>
  291. /// <param name="vIndex">indexes of modifying vertexes</param>
  292. /// <param name="vDistance">distances of modifying vertexes to the center, corresponding to vVertexIndex</param>
  293. public void RaiseLower(Mesh mesh, MeshCollider meshCollider, List<int> vIndex, List<float> vDistance)
  294. {
  295. var vertexes = mesh.vertices;
  296. var brushSizeInU3D = this.BrushSizeInU3D;
  297. var brushStrengthInU3D = Strength * Settings.BrushUnit;
  298. for (var i = 0; i < vIndex.Count; ++i)
  299. {
  300. var index = vIndex[i];
  301. var distance = vDistance[i];
  302. var x = Mathf.Clamp01(1 - distance / brushSizeInU3D);
  303. var falloffFactor = falloffCurve.Evaluate(x);//get falloff factor from the curve
  304. var offset = brushStrengthInU3D * falloffFactor;
  305. if (GoingUp)
  306. {
  307. vertexes[index].y += offset;
  308. }
  309. else
  310. {
  311. vertexes[index].y -= offset;
  312. }
  313. }
  314. mesh.vertices = vertexes;
  315. MTEEditorWindow.Instance.SetMeshDirty(meshCollider.gameObject);
  316. MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
  317. }
  318. }
  319. }