SmoothHeightEditor.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System.Collections.Generic;
  2. using UnityEditor;
  3. using UnityEngine;
  4. namespace MTE
  5. {
  6. internal class SmoothHeightEditor : IEditor
  7. {
  8. public int Id { get; } = 3;
  9. public bool Enabled { get; set; } = true;
  10. public string Name { get; } = "SmoothHeightEditor";
  11. public Texture Icon { get; } =
  12. EditorGUIUtility.IconContent("TerrainInspector.TerrainToolSmoothHeight").image;
  13. public bool WantMouseMove { get; } = false;
  14. public bool WillEditMesh { get; } = true;
  15. #region Parameters
  16. #region Constant
  17. // default
  18. const float DefaultBrushSize = 4;
  19. const float DefaultSpeed = 0.5f;
  20. // min/max
  21. const float MinBrushSize = 0.1f;
  22. const float MaxBrushSize = 10f;
  23. const float MinSpeed = 0.01f;
  24. const float MaxSpeed = 1f;
  25. #endregion
  26. private float brushSize;
  27. private float speed;
  28. /// <summary>
  29. /// Brush size (unit: meter)
  30. /// </summary>
  31. public float BrushSize
  32. {
  33. get { return brushSize; }
  34. set
  35. {
  36. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  37. if (!MathEx.AmostEqual(value, brushSize))
  38. {
  39. brushSize = value;
  40. EditorPrefs.SetFloat("MTE_SmoothHeightEditor.brushSize", value);
  41. }
  42. }
  43. }
  44. private float BrushSizeInU3D { get { return BrushSize * Settings.BrushUnit; } }
  45. /// <summary>
  46. /// Speed
  47. /// </summary>
  48. public float Speed
  49. {
  50. get { return speed; }
  51. set
  52. {
  53. value = Mathf.Clamp(value, MinSpeed, MaxSpeed);
  54. if (!MathEx.AmostEqual(value, speed))
  55. {
  56. speed = value;
  57. EditorPrefs.SetFloat("MTE_SmoothHeightEditor.speed", value);
  58. }
  59. }
  60. }
  61. public int JumpUpdateFrameCount = 5;
  62. #endregion
  63. public SmoothHeightEditor()
  64. {
  65. MTEContext.EnableEvent += (sender, args) =>
  66. {
  67. if (MTEContext.editor == this)
  68. {
  69. LoadSavedParamter();
  70. }
  71. };
  72. MTEContext.EditTypeChangedEvent += (sender, args) =>
  73. {
  74. if (MTEContext.editor == this)
  75. {
  76. LoadSavedParamter();
  77. }
  78. };
  79. brushSize = DefaultBrushSize;
  80. speed = DefaultSpeed;
  81. }
  82. private void LoadSavedParamter()
  83. {
  84. // Load parameters from the EditorPrefs
  85. brushSize = EditorPrefs.GetFloat("MTE_SmoothHeightEditor.brushSize", DefaultBrushSize);
  86. speed = EditorPrefs.GetFloat("MTE_SmoothHeightEditor.speed", DefaultSpeed);
  87. }
  88. public HashSet<Hotkey> DefineHotkeys()
  89. {
  90. return new HashSet<Hotkey>
  91. {
  92. new Hotkey(this, KeyCode.Minus, () =>
  93. {
  94. Speed -= 0.01f;
  95. MTEEditorWindow.Instance.Repaint();
  96. }),
  97. new Hotkey(this, KeyCode.Equals, () =>
  98. {
  99. Speed += 0.01f;
  100. MTEEditorWindow.Instance.Repaint();
  101. }),
  102. new Hotkey(this, KeyCode.LeftBracket, () =>
  103. {
  104. BrushSize -= 1;
  105. MTEEditorWindow.Instance.Repaint();
  106. }),
  107. new Hotkey(this, KeyCode.RightBracket, () =>
  108. {
  109. BrushSize += 1;
  110. MTEEditorWindow.Instance.Repaint();
  111. })
  112. };
  113. }
  114. public string Header { get { return StringTable.Get(C.SmoothHeight_Header); } }
  115. public string Description { get { return StringTable.Get(C.SmoothHeight_Description); } }
  116. public void DoArgsGUI()
  117. {
  118. if (!Settings.CompactGUI)
  119. {
  120. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  121. }
  122. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  123. Speed = EditorGUILayoutEx.Slider(StringTable.Get(C.Speed), "[", "]", Speed, MinSpeed, MaxSpeed);
  124. }
  125. private readonly List<MeshModifyGroup> modifyGroups = new List<MeshModifyGroup>(4);
  126. public void OnSceneGUI()
  127. {
  128. var e = Event.current;
  129. if (!(EditorWindow.mouseOverWindow is SceneView))
  130. {
  131. return;
  132. }
  133. // do nothing when mouse middle/right button, control/alt key is pressed
  134. if (e.button != 0 || e.control || e.alt)
  135. return;
  136. HandleUtility.AddDefaultControl(0);
  137. RaycastHit raycastHit;
  138. Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  139. //Debug.Log(string.Format("mouse at ({0}, {1})", e.mousePosition.x, e.mousePosition.y));
  140. if (Physics.Raycast(ray, out raycastHit,
  141. Mathf.Infinity,
  142. 1 << MTEContext.TargetLayer//only hit target layer
  143. ))
  144. {
  145. //check tag
  146. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  147. {
  148. return;
  149. }
  150. if (Settings.ShowBrushRect)
  151. {
  152. Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
  153. }
  154. // collect modifiy group
  155. modifyGroups.Clear();
  156. foreach (var target in MTEContext.Targets)
  157. {
  158. var meshFilter = target.GetComponent<MeshFilter>();
  159. var meshCollider = target.GetComponent<MeshCollider>();
  160. var mesh = meshFilter.sharedMesh;
  161. var hitPointLocal = target.transform.InverseTransformPoint(raycastHit.point);
  162. List<int> vIndex;
  163. VertexMap.GetAffectedVertex(target, hitPointLocal, BrushSizeInU3D, out vIndex);
  164. if (Settings.ShowAffectedVertex)
  165. {
  166. Utility.ShowAffectedVertices(target, mesh, vIndex);
  167. }
  168. if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
  169. {
  170. if (vIndex.Count != 0)
  171. {
  172. modifyGroups.Add(new MeshModifyGroup(target, mesh, meshCollider, vIndex, vDistance: null));
  173. }
  174. }
  175. }
  176. //record undo operation for targets that to be modified
  177. if (e.type == EventType.MouseDown)
  178. {
  179. Utility.Record("Smooth Height", raycastHit.point, this.BrushSizeInU3D);
  180. }
  181. // execute the modification
  182. if (modifyGroups.Count != 0)
  183. {
  184. for (int i = 0; i < modifyGroups.Count; i++)
  185. {
  186. var modifyGroup = modifyGroups[i];
  187. SmoothHeight(modifyGroup.gameObject, modifyGroup.meshCollider, modifyGroup.vIndex);
  188. }
  189. }
  190. if (e.type == EventType.MouseUp)
  191. {
  192. MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
  193. MTEEditorWindow.Instance.HandleMeshSave();
  194. }
  195. }
  196. SceneView.RepaintAll();
  197. }
  198. /// <summary>
  199. /// Smooth heights of vertexes
  200. /// </summary>
  201. public void SmoothHeight(GameObject obj, MeshCollider meshCollider, List<int> vIndex)
  202. {
  203. float currentBrushSize = BrushSizeInU3D;
  204. var mesh = obj.GetComponent<MeshFilter>().sharedMesh;
  205. Transform transform = obj.transform;
  206. var copied_vertexes = mesh.vertices;
  207. for (var i = 0; i < vIndex.Count; ++i)
  208. {
  209. var index = vIndex[i];
  210. var vertex = copied_vertexes[index];
  211. var vertex_world = transform.TransformPoint(vertex);
  212. var nearbyAverage = VertexMap.GetNearByVerticesAverageHeight(vertex_world, currentBrushSize);
  213. var originalHeight = vertex.y;
  214. var average = 0.5f * nearbyAverage + 0.5f * originalHeight;
  215. var detla = average - originalHeight;
  216. var smoothRise = detla * Speed;
  217. copied_vertexes[index].y += smoothRise;
  218. }
  219. mesh.vertices = copied_vertexes;
  220. // rebuild vertex map, because smooth needs the real heights of each vertex
  221. VertexMap.Rebuild(obj);
  222. MTEEditorWindow.Instance.SetMeshDirty(obj);
  223. MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
  224. }
  225. }
  226. }