PaintHeightEditor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. using System.Collections.Generic;
  2. using UnityEditor;
  3. using UnityEngine;
  4. namespace MTE
  5. {
  6. internal class PaintHeightEditor : IEditor
  7. {
  8. public int Id { get; } = 2;
  9. public bool Enabled { get; set; } = true;
  10. public string Name { get; } = "PaintHeightEditor";
  11. public Texture Icon { get; } =
  12. EditorGUIUtility.IconContent("TerrainInspector.TerrainToolSetHeight").image;
  13. public bool WillEditMesh { get; } = true;
  14. public bool WantMouseMove { get; } = false;
  15. #region Parameters
  16. #region Constant
  17. // default
  18. const float DefaultBrushSize = 4;
  19. const float DefaultSpeed = 1f;
  20. // min/max
  21. const float MinHeight = -2000;
  22. const float MaxHeight = 2000;
  23. const float MinBrushSize = 0.1f;
  24. const float MaxBrushSize = 10f;
  25. const float MinSpeed = 0.01f;
  26. const float MaxSpeed = 5f;
  27. #endregion
  28. private float brushSize;
  29. public float theHeight;
  30. private float speed;
  31. /// <summary>
  32. /// Brush size (unit: meter in world space)
  33. /// </summary>
  34. public float BrushSize
  35. {
  36. get { return brushSize; }
  37. set
  38. {
  39. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  40. if (!MathEx.AmostEqual(value, brushSize))
  41. {
  42. brushSize = value;
  43. EditorPrefs.SetFloat("MTE_PaintHeightEditor.brushSize", value);
  44. }
  45. }
  46. }
  47. private float BrushSizeInU3D { get { return BrushSize * Settings.BrushUnit; } }
  48. /// <summary>
  49. /// Targeting height (unit: meter in local space)
  50. /// </summary>
  51. public float TheHeight
  52. {
  53. get { return theHeight; }
  54. set { theHeight = value; }
  55. }
  56. /// <summary>
  57. /// Speed
  58. /// </summary>
  59. public float Speed
  60. {
  61. get { return speed; }
  62. set
  63. {
  64. value = Mathf.Clamp(value, MinSpeed, MaxSpeed);
  65. if (System.Math.Abs(value - speed) > 0.0001f)
  66. {
  67. speed = value;
  68. EditorPrefs.SetFloat("MTE_PaintHeightEditor.speed", value);
  69. }
  70. }
  71. }
  72. #endregion
  73. public PaintHeightEditor()
  74. {
  75. MTEContext.EnableEvent += (sender, args) =>
  76. {
  77. if (MTEContext.editor == this)
  78. {
  79. LoadSavedParamter();
  80. }
  81. };
  82. MTEContext.EditTypeChangedEvent += (sender, args) =>
  83. {
  84. if (MTEContext.editor == this)
  85. {
  86. LoadSavedParamter();
  87. }
  88. };
  89. // Load default parameters
  90. brushSize = DefaultBrushSize;
  91. speed = DefaultSpeed;
  92. }
  93. private void LoadSavedParamter()
  94. {
  95. // Load parameters from the EditorPrefs
  96. brushSize = EditorPrefs.GetFloat("MTE_PaintHeightEditor.brushSize", DefaultBrushSize);
  97. speed = EditorPrefs.GetFloat("MTE_PaintHeightEditor.speed", DefaultSpeed);
  98. }
  99. public HashSet<Hotkey> DefineHotkeys()
  100. {
  101. var hotkeys = new HashSet<Hotkey>
  102. {
  103. new Hotkey(this, KeyCode.Minus, () =>
  104. {
  105. Speed -= 0.01f;
  106. MTEEditorWindow.Instance.Repaint();
  107. }),
  108. new Hotkey(this, KeyCode.Equals, () =>
  109. {
  110. Speed += 0.01f;
  111. MTEEditorWindow.Instance.Repaint();
  112. }),
  113. new Hotkey(this, KeyCode.LeftBracket, () =>
  114. {
  115. BrushSize -= 1;
  116. MTEEditorWindow.Instance.Repaint();
  117. }),
  118. new Hotkey(this, KeyCode.RightBracket, () =>
  119. {
  120. BrushSize += 1;
  121. MTEEditorWindow.Instance.Repaint();
  122. })
  123. };
  124. return hotkeys;
  125. }
  126. public string Header { get { return StringTable.Get(C.PaintHeight_Header); } }
  127. public string Description { get { return StringTable.Get(C.PaintHeight_Description); } }
  128. public void DoArgsGUI()
  129. {
  130. if (!Settings.CompactGUI)
  131. {
  132. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  133. }
  134. TheHeight = EditorGUILayoutEx.Slider(StringTable.Get(C.Height), " ", " ", TheHeight, MinHeight, MaxHeight);
  135. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  136. Speed = EditorGUILayoutEx.Slider(StringTable.Get(C.Speed), "[", "]", Speed, MinSpeed, MaxSpeed);
  137. }
  138. private readonly List<MeshModifyGroup> modifyGroups = new List<MeshModifyGroup>(4);
  139. public void OnSceneGUI()
  140. {
  141. var e = Event.current;
  142. if (!(EditorWindow.mouseOverWindow is SceneView))
  143. {
  144. return;
  145. }
  146. // do nothing when mouse middle/right button, alt key is pressed
  147. if (e.button != 0 || e.alt)
  148. return;
  149. // hold control key to sample height
  150. if (e.control && !e.isKey)
  151. {
  152. float sampledHeight;
  153. if (SampleHeight(e.mousePosition, out sampledHeight))
  154. {
  155. if (Mathf.Abs(TheHeight - sampledHeight) > Mathf.Epsilon)
  156. {
  157. MTEEditorWindow.Instance.Repaint();
  158. }
  159. TheHeight = sampledHeight;
  160. }
  161. }
  162. else
  163. {
  164. HandleUtility.AddDefaultControl(0);
  165. RaycastHit raycastHit;
  166. Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  167. //Debug.Log(string.Format("mouse at ({0}, {1})", e.mousePosition.x, e.mousePosition.y));
  168. if(Physics.Raycast(ray, out raycastHit,
  169. Mathf.Infinity,
  170. 1 << MTEContext.TargetLayer//only hit target layer
  171. ))
  172. {
  173. //check tag
  174. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  175. {
  176. return;
  177. }
  178. if (Settings.ShowBrushRect)
  179. {
  180. Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
  181. }
  182. // collect modifiy group
  183. modifyGroups.Clear();
  184. foreach (var target in MTEContext.Targets)
  185. {
  186. var meshFilter = target.GetComponent<MeshFilter>();
  187. var meshCollider = target.GetComponent<MeshCollider>();
  188. var mesh = meshFilter.sharedMesh;
  189. var hitPointLocal = target.transform.InverseTransformPoint(raycastHit.point);
  190. List<int> vIndex;
  191. List<float> vDistance;
  192. VertexMap.GetAffectedVertex(target, hitPointLocal, BrushSizeInU3D, out vIndex, out vDistance);
  193. if (Settings.ShowAffectedVertex)
  194. {
  195. Utility.ShowAffectedVertices(target, mesh, vIndex);
  196. }
  197. if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
  198. {
  199. if (vIndex.Count != 0)
  200. {
  201. modifyGroups.Add(new MeshModifyGroup(target, mesh, meshCollider, vIndex, vDistance));
  202. }
  203. }
  204. }
  205. //record undo operation for targets that to be modified
  206. if (e.type == EventType.MouseDown)
  207. {
  208. Utility.Record("Paint Height", raycastHit.point, this.BrushSizeInU3D);
  209. }
  210. // execute the modification
  211. if (modifyGroups.Count != 0)
  212. {
  213. for (int i = 0; i < modifyGroups.Count; i++)
  214. {
  215. var modifyGroup = modifyGroups[i];
  216. var mesh = modifyGroup.mesh;
  217. var meshCollider = modifyGroup.meshCollider;
  218. var vIndex = modifyGroup.vIndex;
  219. PaintHeight(mesh, meshCollider, vIndex);
  220. }
  221. }
  222. if (e.type == EventType.MouseUp)
  223. {
  224. MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
  225. MTEEditorWindow.Instance.HandleMeshSave();
  226. }
  227. }
  228. }
  229. SceneView.RepaintAll();
  230. }
  231. /// <summary>
  232. /// sample height(y)
  233. /// </summary>
  234. /// <param name="mousePosition">screen position of mouse</param>
  235. /// <param name="height">sampled height</param>
  236. /// <returns>true: success/false: failed</returns>
  237. private static bool SampleHeight(Vector2 mousePosition, out float height)
  238. {
  239. height = 0;
  240. HandleUtility.AddDefaultControl(0);
  241. RaycastHit raycastHit;
  242. Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
  243. if (Physics.Raycast(ray, out raycastHit))
  244. {
  245. if (!MTEContext.IsMatchFilter(raycastHit.transform.gameObject))
  246. {
  247. return false;
  248. }
  249. Handles.color = Color.green;
  250. var size = HandleUtility.GetHandleSize(raycastHit.point) * Settings.PointSize;
  251. Handles.SphereHandleCap(0, raycastHit.point, Quaternion.identity, size, EventType.Repaint);
  252. var hitPointLocal = raycastHit.transform.InverseTransformPoint(raycastHit.point);//convert hitpoint's position to mesh's local coordinate
  253. height = hitPointLocal.y;
  254. return true;
  255. }
  256. return false;
  257. }
  258. /// <summary>
  259. /// Move height of vertex towards the specified height
  260. /// </summary>
  261. /// <param name="mesh">mesh to modify</param>
  262. /// <param name="meshCollider">meshcollider related to the mesh</param>
  263. /// <param name="vIndex">indexes of modifying vertexes</param>
  264. private void PaintHeight(Mesh mesh, MeshCollider meshCollider, List<int> vIndex)
  265. {
  266. var vertexes = mesh.vertices;
  267. for (var i = 0; i < vIndex.Count; ++i)
  268. {
  269. var index = vIndex[i];
  270. vertexes[index].y =
  271. Mathf.MoveTowards(vertexes[index].y, TheHeight, Speed);
  272. }
  273. mesh.vertices = vertexes;
  274. MTEEditorWindow.Instance.SetMeshDirty(meshCollider.gameObject);
  275. MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
  276. }
  277. }
  278. }