MeshToolbox.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. using MTE.Undo;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEditor;
  6. using UnityEngine;
  7. namespace MTE
  8. {
  9. internal class MeshToolbox : IEditor
  10. {
  11. public int Id { get; } = 9;
  12. public bool Enabled { get; set; } = true;
  13. public string Name { get; } = "MeshToolbox";
  14. public Texture Icon { get; } =
  15. EditorGUIUtility.IconContent("Mesh Icon").image;
  16. public bool WantMouseMove { get; } = false;
  17. public bool WillEditMesh { get; } = true;
  18. private enum SelectMode
  19. {
  20. Vertex,
  21. Quad,
  22. }
  23. private enum EditorState
  24. {
  25. Selecting,
  26. Action,
  27. }
  28. /// <summary>
  29. /// AABB that can contain only one point
  30. /// </summary>
  31. private class AABB
  32. {
  33. float xMin = float.NaN, yMin, zMin;
  34. float xMax, yMax, zMax;
  35. public Vector3 Min { get { return new Vector3(xMin, yMin, zMin); } }
  36. public Vector3 Max { get { return new Vector3(xMax, yMax, zMax); } }
  37. public Vector3 Center { get { return 0.5f*(Min + Max); } }
  38. public bool IsEmpty { get { return float.IsNaN(xMin); } }
  39. /// <summary>Reset to initial state (empty) </summary>
  40. public void Reset()
  41. {
  42. xMin = float.NaN;
  43. }
  44. public void AddPoint(Vector3 point)
  45. {
  46. if (IsEmpty)
  47. {
  48. this.xMin = this.xMax = point.x;
  49. this.yMin = this.yMax = point.y;
  50. this.zMin = this.zMax = point.z;
  51. }
  52. if (point.x > xMax)
  53. {
  54. this.xMax = point.x;
  55. }
  56. else if(point.x < xMin)
  57. {
  58. this.xMin = point.x;
  59. }
  60. if (point.y > yMax)
  61. {
  62. this.yMax = point.y;
  63. }
  64. else if (point.y < yMin)
  65. {
  66. this.yMin = point.y;
  67. }
  68. if (point.z > zMax)
  69. {
  70. this.zMax = point.z;
  71. }
  72. else if (point.z < zMin)
  73. {
  74. this.zMin = point.z;
  75. }
  76. }
  77. }
  78. private class VertexModifyGroup
  79. {
  80. private readonly GameObject gameObject;
  81. private List<int> vertexIndexList;
  82. public GameObject Target { get { return gameObject; } }
  83. public List<int> VertexIndexList
  84. {
  85. get { return vertexIndexList;}
  86. }
  87. public VertexModifyGroup(GameObject gameObject)
  88. {
  89. this.gameObject = gameObject;
  90. }
  91. public void AppendVertices(IList<int> vertexIndexList)
  92. {
  93. if (this.vertexIndexList == null)
  94. {
  95. this.vertexIndexList = new List<int>();
  96. }
  97. this.vertexIndexList.AddRange(vertexIndexList);
  98. this.vertexIndexList = this.vertexIndexList.Distinct().ToList();
  99. }
  100. public void RemoveVertices(IList<int> remove_vertexIndexList)
  101. {
  102. if (this.vertexIndexList == null)
  103. {
  104. return;
  105. }
  106. for (var i = this.VertexIndexList.Count - 1; i >= 0; i--)
  107. {
  108. var vertexIndex = this.VertexIndexList[i];
  109. var foundIndex = remove_vertexIndexList.IndexOf(vertexIndex);
  110. if (foundIndex >= 0)
  111. {
  112. this.VertexIndexList.RemoveAt(i);
  113. remove_vertexIndexList.RemoveAt(foundIndex);
  114. }
  115. }
  116. }
  117. public void Clear()
  118. {
  119. this.VertexIndexList.Clear();
  120. }
  121. }
  122. #region modfication
  123. private readonly List<VertexModifyGroup> vertexModifyGroups = new List<VertexModifyGroup>(4);
  124. private readonly AABB aabb = new AABB();
  125. private void AddVerticesToModifyGroup(GameObject gameObject, IList<int> vertexIndexList)
  126. {
  127. if (gameObject == null)
  128. {
  129. throw new ArgumentNullException("gameObject");
  130. }
  131. if (vertexIndexList == null)
  132. {
  133. throw new ArgumentNullException("vertexIndexList");
  134. }
  135. if (vertexIndexList.Count == 0)
  136. {
  137. return;
  138. }
  139. {
  140. var modifyGroup = vertexModifyGroups.Find(group => group.Target == gameObject);
  141. if (modifyGroup == null)
  142. {
  143. modifyGroup = new VertexModifyGroup(gameObject);
  144. vertexModifyGroups.Add(modifyGroup);
  145. }
  146. modifyGroup.AppendVertices(vertexIndexList);
  147. }
  148. }
  149. private void RemoveVerticesFromModifyGroup(GameObject gameObject, IList<int> vertexIndexList)
  150. {
  151. if (gameObject == null)
  152. {
  153. throw new ArgumentNullException("gameObject");
  154. }
  155. if (vertexIndexList == null)
  156. {
  157. throw new ArgumentNullException("vertexIndexList");
  158. }
  159. if (vertexIndexList.Count == 0)
  160. {
  161. return;
  162. }
  163. {
  164. var modifyGroup = vertexModifyGroups.Find(group => group.Target == gameObject);
  165. if (modifyGroup == null)
  166. {
  167. modifyGroup = new VertexModifyGroup(gameObject);
  168. vertexModifyGroups.Add(modifyGroup);
  169. }
  170. modifyGroup.RemoveVertices(vertexIndexList);
  171. }
  172. }
  173. private void RefreshAABB()
  174. {
  175. aabb.Reset();
  176. for (var i = 0; i < vertexModifyGroups.Count; i++)
  177. {
  178. var modifyGroup = vertexModifyGroups[i];
  179. var gameObject = modifyGroup.Target;
  180. if (!gameObject)
  181. {
  182. continue;
  183. }
  184. var transform = gameObject.transform;
  185. var meshFilter = gameObject.GetComponent<MeshFilter>();
  186. if (!meshFilter)
  187. {
  188. continue;
  189. }
  190. var mesh = meshFilter.sharedMesh;
  191. if (!mesh)
  192. {
  193. continue;
  194. }
  195. var meshVertices = mesh.vertices;
  196. for (int j = 0; j < modifyGroup.VertexIndexList.Count; j++)
  197. {
  198. var vertexIndex = modifyGroup.VertexIndexList[j];
  199. if (vertexIndex >= meshVertices.Length)
  200. {
  201. MTEDebug.LogError(
  202. $"Mesh {mesh.name}'s vertices on GameObject has been changed.");
  203. continue;
  204. }
  205. var vertexPosition = meshVertices[vertexIndex];
  206. var pWorld = transform.TransformPoint(vertexPosition);
  207. aabb.AddPoint(pWorld);
  208. }
  209. }
  210. }
  211. #endregion
  212. #region Parameters
  213. #region Constant
  214. // default
  215. const float DefaultBrushSize = 4;
  216. // min/max
  217. const float MinBrushSize = 0.1f;
  218. const float MaxBrushSize = 10f;
  219. #endregion
  220. private float brushSize;
  221. /// <summary>
  222. /// Brush size (unit: meter)
  223. /// </summary>
  224. public float BrushSize
  225. {
  226. get { return brushSize; }
  227. set
  228. {
  229. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  230. if (!MathEx.AmostEqual(value, brushSize))
  231. {
  232. brushSize = value;
  233. EditorPrefs.SetFloat("MTE_MeshToolbox.brushSize", value);
  234. }
  235. }
  236. }
  237. private float BrushSizeInU3D => BrushSize * Settings.BrushUnit;
  238. #endregion
  239. public MeshToolbox()
  240. {
  241. MTEContext.EnableEvent += (sender, args) =>
  242. {
  243. if (MTEContext.editor == this)
  244. {
  245. LoadSavedParamter();
  246. }
  247. };
  248. MTEContext.EditTypeChangedEvent += (sender, args) =>
  249. {
  250. if (MTEContext.editor == this)
  251. {
  252. LoadSavedParamter();
  253. }
  254. };
  255. brushSize = DefaultBrushSize;
  256. }
  257. public HashSet<Hotkey> DefineHotkeys()
  258. {
  259. return new HashSet<Hotkey>
  260. {
  261. new Hotkey(this, KeyCode.LeftBracket, () =>
  262. {
  263. BrushSize -= 1;
  264. MTEEditorWindow.Instance.Repaint();
  265. }),
  266. new Hotkey(this, KeyCode.RightBracket, () =>
  267. {
  268. BrushSize += 1;
  269. MTEEditorWindow.Instance.Repaint();
  270. })
  271. };
  272. }
  273. private void LoadSavedParamter()
  274. {
  275. // Load parameters from the EditorPrefs
  276. brushSize = EditorPrefs.GetFloat("MTE_MeshToolbox.brushSize", DefaultBrushSize);
  277. }
  278. public string Header{ get { return StringTable.Get(C.MeshMisc_Header); } }
  279. public string Description { get { return StringTable.Get(C.MeshMisc_Description); } }
  280. private static class Styles
  281. {
  282. private static bool unloaded= true;
  283. public static void Init()
  284. {
  285. if (!unloaded) return;
  286. //nothing for now
  287. unloaded = false;
  288. }
  289. }
  290. private bool pressedHotkeyOnEditor;
  291. public void DoArgsGUI()
  292. {
  293. Styles.Init();
  294. if (!Settings.CompactGUI)
  295. {
  296. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  297. }
  298. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  299. if (!Settings.CompactGUI)
  300. {
  301. EditorGUILayout.Space();
  302. GUILayout.Label(StringTable.Get(C.Tools), MTEStyles.SubHeader);
  303. }
  304. mode = (SelectMode)GUILayout.Toolbar((int)mode, MTEStyles.MeshSelectModeContents, Settings.CompactGUI ? GUILayout.Height(32) : GUILayout.Height(48));
  305. if (mode == SelectMode.Vertex)
  306. {
  307. EditorGUILayout.BeginHorizontal();
  308. {
  309. GUI.enabled = CanDelete();
  310. if (GUILayout.Button(StringTable.Get(C.Delete), GUILayout.Width(100), GUILayout.Height(40)))
  311. {
  312. Delete();
  313. }
  314. GUI.enabled = true;
  315. GUILayout.Space(20);
  316. EditorGUILayout.LabelField(StringTable.Get(C.Info_ToolDescription_DeleteVertices), MTEStyles.labelFieldWordwrap);
  317. }
  318. EditorGUILayout.EndHorizontal();
  319. }
  320. else if (mode == SelectMode.Quad)
  321. {
  322. EditorGUILayout.HelpBox(StringTable.Get(C.Info_SwapQuadSplitDirection), MessageType.Info);
  323. if (IsPressedHotkeyForQuadMode(Event.current))
  324. {
  325. pressedHotkeyOnEditor = true;
  326. }
  327. }
  328. }
  329. private EditorState state = EditorState.Selecting;
  330. private SelectMode mode = SelectMode.Vertex;
  331. //debug only data
  332. private static Vector3 pTest;
  333. private static Vector3 pMaxEdgePoint0;
  334. private static Vector3 pMaxEdgePoint1;
  335. //debug only data
  336. public void OnSceneGUI()
  337. {
  338. var e = Event.current;
  339. if (e.commandName == "UndoRedoPerformed")
  340. {
  341. SceneView.RepaintAll();
  342. return;
  343. }
  344. if (mode == SelectMode.Vertex)
  345. {
  346. OnSceneGUIForVertex();
  347. }
  348. else if(mode == SelectMode.Quad)
  349. {
  350. OnSceneGUIForQuad();
  351. if (Settings.DebugMode)
  352. {
  353. var oldHandlesColor = Handles.color;
  354. Handles.color = Color.red;
  355. var size = Utility.GetHandleSize(pTest);
  356. Handles.DotHandleCap(0, pTest, Quaternion.identity, size * Settings.PointSize, EventType.Repaint);
  357. Handles.DrawLine(pMaxEdgePoint0, pMaxEdgePoint1);
  358. if (e.type == EventType.MouseDown && e.button == 1)
  359. {
  360. pMaxEdgePoint1 = pMaxEdgePoint0 = pTest = new Vector3(-9999, -9999, -9999);
  361. }
  362. Handles.color = oldHandlesColor;
  363. }
  364. }
  365. }
  366. private void OnSceneGUIForQuad()
  367. {
  368. var e = Event.current;
  369. if (!(EditorWindow.mouseOverWindow is SceneView))
  370. {
  371. return;
  372. }
  373. if (e.control || e.alt)
  374. return;
  375. HandleUtility.AddDefaultControl(0);
  376. RaycastHit raycastHit;
  377. Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  378. if (Physics.Raycast(ray, out raycastHit,
  379. Mathf.Infinity,
  380. 1 << MTEContext.TargetLayer //only hit target layer
  381. ))
  382. {
  383. //check tag
  384. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  385. {
  386. return;
  387. }
  388. var transform = raycastHit.transform;
  389. var target = transform.gameObject;
  390. var meshFilter = target.GetComponent<MeshFilter>();
  391. if (meshFilter == null)
  392. {
  393. return;
  394. }
  395. var mesh = meshFilter.sharedMesh;
  396. if (mesh == null)
  397. {
  398. return;
  399. }
  400. var triangleIndex0 = raycastHit.triangleIndex * 3 + 0;
  401. var triangleIndex1 = raycastHit.triangleIndex * 3 + 1;
  402. var triangleIndex2 = raycastHit.triangleIndex * 3 + 2;
  403. // highlight hovered quad
  404. if (QuadMap.FindQuad(target, triangleIndex0, triangleIndex1, triangleIndex2, out Quad quad))
  405. {
  406. Handles.color = Settings.FlashAffectedVertex ? Utility.GetFlashingColor(0.583f) : Color.blue;
  407. var meshVertices = mesh.vertices;
  408. int index0 = quad.index[0];
  409. int index1 = quad.index[1];
  410. int index2 = quad.index[2];
  411. var p0 = transform.TransformPoint(meshVertices[index0]);
  412. var p1 = transform.TransformPoint(meshVertices[index1]);
  413. var p2 = transform.TransformPoint(meshVertices[index2]);
  414. int index3 = quad.index[3];
  415. int index4 = quad.index[4];
  416. int index5 = quad.index[5];
  417. var p3 = transform.TransformPoint(meshVertices[index3]);
  418. var p4 = transform.TransformPoint(meshVertices[index4]);
  419. var p5 = transform.TransformPoint(meshVertices[index5]);
  420. var oldHandlesColor = Handles.color;
  421. if (Settings.DebugMode)
  422. {
  423. Handles.color = Color.green;
  424. Handles.DrawPolyLine(p0, p1, p2, p0);
  425. Handles.color = Color.blue;
  426. Handles.DrawPolyLine(p3, p4, p5, p3);
  427. }
  428. else
  429. {
  430. Handles.color = Utility.GetFlashingColor();
  431. //Handles.DrawAAPolyLine(3, 4, p0, p1, p2, p0);
  432. //Handles.DrawAAPolyLine(3, 4, p3, p4, p5, p3);
  433. Handles.DrawPolyLine(p0, p1, p2, p0);
  434. Handles.DrawPolyLine(p3, p4, p5, p3);
  435. }
  436. Handles.color = oldHandlesColor;
  437. }
  438. bool pressed = IsPressedHotkeyForQuadMode(e) || pressedHotkeyOnEditor;
  439. if (pressed)
  440. {
  441. pressedHotkeyOnEditor = false;
  442. ChangeQuadSplitDirection(target, triangleIndex0, triangleIndex1, triangleIndex2);
  443. var meshCollider = target.GetComponent<MeshCollider>();
  444. MTEEditorWindow.Instance.UpdateMeshColliderImmediately(meshCollider);
  445. MTEEditorWindow.Instance.HandleMeshSave();
  446. }
  447. }
  448. SceneView.RepaintAll();
  449. }
  450. private bool IsPressedHotkeyForQuadMode(Event e)
  451. {
  452. return e.isKey && e.type == EventType.KeyUp && e.keyCode == KeyCode.S;
  453. }
  454. private void ChangeQuadSplitDirection(GameObject target, int index0, int index1, int index2)
  455. {
  456. if (!QuadMap.FindQuad(target, index0, index1, index2, out Quad quad))
  457. {
  458. return;
  459. }
  460. Quad newQuad = quad;
  461. newQuad.ChangeSplitDirection();
  462. int firstIndex = -1;
  463. if (index0 % 6 == 0)
  464. {
  465. firstIndex = index0;
  466. }
  467. else if (index0 % 6 == 3)
  468. {
  469. firstIndex = index0 - 3;
  470. }
  471. else
  472. {
  473. throw new MTEEditException("Argument index0 is not a valid first index of triangle");
  474. }
  475. Utility.RecordMeshIndices("Swap Quad Split Direction", target, () => { QuadMap.Rebuild(target); });
  476. QuadMap.ModifyQuad(target, firstIndex, newQuad);
  477. }
  478. private void OnSceneGUIForVertex()
  479. {
  480. var e = Event.current;
  481. // show modifying points
  482. if (vertexModifyGroups.Count != 0)
  483. {
  484. Handles.color = Settings.FlashAffectedVertex ? Utility.GetFlashingColor(0.583f) : Color.blue;
  485. for (var i = 0; i < vertexModifyGroups.Count; i++)
  486. {
  487. var modifyGroup = vertexModifyGroups[i];
  488. if (modifyGroup.VertexIndexList == null || modifyGroup.VertexIndexList.Count == 0) continue;
  489. if (!modifyGroup.Target) continue;
  490. var meshFilter = modifyGroup.Target.GetComponent<MeshFilter>();
  491. if (meshFilter == null) continue;
  492. var mesh = meshFilter.sharedMesh;
  493. if (mesh == null) continue;
  494. var meshVertices = mesh.vertices;
  495. var transform = modifyGroup.Target.transform;
  496. for (int j = 0; j < modifyGroup.VertexIndexList.Count; j++)
  497. {
  498. var vertexIndex = modifyGroup.VertexIndexList[j];
  499. var vertexPosition = meshVertices[vertexIndex];
  500. var p = transform.TransformPoint(vertexPosition);
  501. var size = Utility.GetHandleSize(p);
  502. Handles.DotHandleCap(0, p, Quaternion.identity, size * Settings.PointSize, EventType.Repaint);
  503. }
  504. }
  505. }
  506. if (!(EditorWindow.mouseOverWindow is SceneView))
  507. {
  508. return;
  509. }
  510. if (e.button != 0 || e.control || e.alt)
  511. return;
  512. if (state == EditorState.Selecting)
  513. {
  514. HandleUtility.AddDefaultControl(0);
  515. RaycastHit raycastHit;
  516. Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  517. //Debug.Log(string.Format("mouse at ({0}, {1})", e.mousePosition.x, e.mousePosition.y));
  518. if (Physics.Raycast(ray, out raycastHit,
  519. Mathf.Infinity,
  520. 1 << MTEContext.TargetLayer //only hit target layer
  521. ))
  522. {
  523. //check tag
  524. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  525. {
  526. return;
  527. }
  528. if (Settings.ShowBrushRect)
  529. {
  530. Utility.ShowBrushRect(raycastHit.point, BrushSizeInU3D);
  531. }
  532. if (e.isKey && e.keyCode == KeyCode.Return && vertexModifyGroups.Count != 0)
  533. {
  534. RefreshAABB();
  535. state = EditorState.Action;
  536. return;
  537. }
  538. // collect modify group
  539. foreach (var gameObject in MTEContext.Targets)
  540. {
  541. if (!gameObject)
  542. {
  543. continue;
  544. }
  545. var meshFilter = gameObject.GetComponent<MeshFilter>();
  546. var mesh = meshFilter.sharedMesh;
  547. var hitPointLocal = gameObject.transform.InverseTransformPoint(raycastHit.point);
  548. List<int> vIndex;
  549. List<float> vDistance;
  550. VertexMap.GetAffectedVertex(gameObject, hitPointLocal, this.BrushSizeInU3D,
  551. out vIndex, out vDistance);
  552. if (Settings.ShowAffectedVertex)
  553. {
  554. Utility.ShowAffectedVertices(gameObject, mesh, vIndex);
  555. }
  556. if (e.type == EventType.MouseDown || e.type == EventType.MouseDrag)
  557. {
  558. if (vIndex.Count != 0)
  559. {
  560. if (!e.shift)
  561. {
  562. AddVerticesToModifyGroup(gameObject, vIndex);
  563. }
  564. else
  565. {
  566. RemoveVerticesFromModifyGroup(gameObject, vIndex);
  567. }
  568. }
  569. }
  570. }
  571. }
  572. }
  573. if (state == EditorState.Action)
  574. {
  575. if (e.isKey && e.keyCode == KeyCode.Escape)
  576. {
  577. state = EditorState.Selecting;
  578. return;
  579. }
  580. //record undo operation for targets that to be modified
  581. if (e.type == EventType.MouseDown)
  582. {
  583. Utility.Record("Mesh Toolbox: move",
  584. Vector3.zero, this.BrushSizeInU3D, () =>
  585. {
  586. RefreshAABB();
  587. for (var i = 0; i < vertexModifyGroups.Count; i++)
  588. {
  589. var modifyGroup = vertexModifyGroups[i];
  590. if (!modifyGroup.Target)
  591. {
  592. continue;
  593. }
  594. VertexMap.Rebuild(modifyGroup.Target);
  595. }
  596. });
  597. }
  598. if (e.type == EventType.MouseUp)
  599. {
  600. MTEEditorWindow.Instance.UpdateDirtyMeshCollidersImmediately();
  601. MTEEditorWindow.Instance.HandleMeshSave();
  602. }
  603. // execute the modification
  604. if (vertexModifyGroups.Count != 0)
  605. {
  606. var newPostion = Handles.DoPositionHandle(this.aabb.Center, Quaternion.identity);
  607. var offset = newPostion - this.aabb.Center;
  608. TranslateVertices(offset);
  609. }
  610. }
  611. SceneView.RepaintAll();
  612. }
  613. private void TranslateVertices(Vector3 offset)
  614. {
  615. // check if offset is big enough
  616. if (!(Mathf.Abs(offset.x) > 0.001f) && !(Mathf.Abs(offset.y) > 0.001f) && !(Mathf.Abs(offset.z) > 0.001f)) return;
  617. for (var i = 0; i < vertexModifyGroups.Count; i++)
  618. {
  619. var modifyGroup = vertexModifyGroups[i];
  620. if (modifyGroup.VertexIndexList == null
  621. || modifyGroup.VertexIndexList.Count == 0
  622. || !modifyGroup.Target)
  623. {
  624. continue;
  625. }
  626. var gameObject = modifyGroup.Target;
  627. var transform = gameObject.transform;
  628. var offsetLocal = transform.InverseTransformDirection(offset);
  629. var mesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
  630. var meshCollider = modifyGroup.Target.GetComponent<MeshCollider>();
  631. var vertices = mesh.vertices;//TODO performance improvement: mesh.vertices will copy vertices
  632. for (int j = 0; j < modifyGroup.VertexIndexList.Count; j++)
  633. {
  634. var index = modifyGroup.VertexIndexList[j];
  635. vertices[index] += offsetLocal;
  636. }
  637. mesh.vertices = vertices;
  638. MTEEditorWindow.Instance.SetMeshDirty(gameObject);
  639. MTEEditorWindow.Instance.SetMeshColliderDirty(meshCollider, mesh.vertexCount);
  640. // Rebuild vertex map for this GameObject if its vertices have been translated in xOz.
  641. if (Math.Abs(offset.x) > 0.001 || Math.Abs(offset.z) > 0.001)
  642. {
  643. VertexMap.Rebuild(gameObject);
  644. }
  645. }
  646. RefreshAABB();
  647. }
  648. bool CanDelete()
  649. {
  650. if (vertexModifyGroups.Count == 0)
  651. {
  652. return false;
  653. }
  654. for (var i = 0; i < vertexModifyGroups.Count; i++)
  655. {
  656. var modifyGroup = vertexModifyGroups[i];
  657. if (modifyGroup.VertexIndexList.Count != 0)
  658. {
  659. return true;
  660. }
  661. }
  662. return false;
  663. }
  664. void Delete()
  665. {
  666. using (new Undo.UndoTransaction("Mesh Toolbox: delete"))
  667. {
  668. for (var i = 0; i < vertexModifyGroups.Count; i++)
  669. {
  670. var modifyGroup = vertexModifyGroups[i];
  671. var gameObject = modifyGroup.Target;
  672. if (!gameObject)
  673. {
  674. continue;
  675. }
  676. if (gameObject.GetComponent<VertexColorInitializer>() != null)//ignore vertex colored GameObjects
  677. {
  678. continue;
  679. }
  680. var mesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
  681. var meshCollider = modifyGroup.Target.GetComponent<MeshCollider>();
  682. var meshTriangles = mesh.triangles;
  683. var indexList = modifyGroup.VertexIndexList;
  684. // create new indexes
  685. List<int> newMeshTriangles = new List<int>(meshTriangles);
  686. // remove triangles that contains the removed vertices
  687. for (var j = newMeshTriangles.Count - 1; j >= 0; j -= 3)
  688. {
  689. var index0 = newMeshTriangles[j];
  690. var index1 = newMeshTriangles[j - 1];
  691. var index2 = newMeshTriangles[j - 2];
  692. if (indexList.Contains(index0) || indexList.Contains(index1) || indexList.Contains(index2))
  693. {
  694. newMeshTriangles.RemoveAt(j);
  695. newMeshTriangles.RemoveAt(j - 1);
  696. newMeshTriangles.RemoveAt(j - 2);
  697. }
  698. }
  699. var newTriangles = newMeshTriangles.ToArray();
  700. // record undo operation for targets that to be modified
  701. var newIndices = mesh.triangles;
  702. UndoRedoManager.Instance().Push(a =>
  703. {
  704. mesh.ModifyIndices("Mesh Toolbox: delete", meshCollider, a, () =>
  705. {
  706. VertexMap.Rebuild(gameObject);
  707. });
  708. meshCollider.enabled = false;
  709. meshCollider.enabled = true;
  710. }, newIndices, "Mesh Toolbox: delete");
  711. // assign
  712. mesh.triangles = newTriangles;
  713. MTEEditorWindow.Instance.SetMeshDirty(gameObject);
  714. // rebuild vertex map
  715. VertexMap.Rebuild(gameObject);
  716. modifyGroup.Clear();
  717. }
  718. }
  719. }
  720. }
  721. }