FlowPainter.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. using MTE.Undo;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using System.IO;
  6. namespace MTE
  7. {
  8. internal class FlowPainter : IEditor
  9. {
  10. public int Id { get; } = 8;
  11. public bool Enabled { get; set; } = true;
  12. public string Name { get; } = "FlowPainter";
  13. public Texture Icon { get; } = MTEStyles.PaintFlowToolIcon;
  14. public bool WantMouseMove { get; } = false;
  15. public bool WillEditMesh { get; } = false;
  16. public enum PaintMode
  17. {
  18. Fixed,
  19. Movement,
  20. PinchInflate,
  21. Vortex,
  22. }
  23. #region Parameters
  24. #region Constant
  25. // default
  26. const PaintMode DefaultMode = PaintMode.Fixed;
  27. const int DefaultBrushIndex = 0;
  28. const float DefaultBrushSize = 1f;
  29. const float DefaultStrength = 0.5f;
  30. const float DefaultSpeed = 0.05f;
  31. const float DefaultDirection = 0f;
  32. const bool DefaultPinching = true;
  33. const bool DefaultVortexRotationClockWise = true;
  34. // min/max
  35. const float MinBrushSize = 0.1f;
  36. const float MaxBrushSize = 50f;
  37. const float MinSpeed = 0.01f;
  38. const float MaxSpeed = 1f;
  39. #endregion
  40. private PaintMode mode;
  41. private int brushIndex;
  42. private float strength;
  43. private float direction;
  44. private float brushSize;
  45. private float speed;
  46. private bool pinching;
  47. private bool vortexRotationClockWise;
  48. public PaintMode Mode
  49. {
  50. get { return mode; }
  51. set
  52. {
  53. if(mode != value)
  54. {
  55. EditorPrefs.SetInt("MTE_FlowPainter.mode", (int)value);
  56. mode = value;
  57. }
  58. }
  59. }
  60. /// <summary>
  61. /// Brush index
  62. /// </summary>
  63. public int BrushIndex
  64. {
  65. get { return brushIndex; }
  66. set
  67. {
  68. if (brushIndex != value)
  69. {
  70. //Update preview
  71. //if (previewObj != null)
  72. //{
  73. // SetPreviewMaskTexture(value);
  74. //}
  75. EditorPrefs.SetInt("MTE_FlowPainter.brushIndex", value);
  76. brushIndex = value;
  77. }
  78. }
  79. }
  80. /// <summary>
  81. /// Brush size (unit: 1 BrushUnit)
  82. /// </summary>
  83. public float BrushSize
  84. {
  85. get { return brushSize; }
  86. set
  87. {
  88. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  89. if (!MathEx.AmostEqual(brushSize, value))
  90. {
  91. brushSize = value;
  92. EditorPrefs.SetFloat("MTE_FlowPainter.brushSize", value);
  93. //if (previewObj != null)
  94. //{
  95. // SetPreviewSize(BrushSizeInU3D / 2);
  96. //}
  97. }
  98. }
  99. }
  100. private float BrushSizeInU3D { get { return BrushSize * Settings.BrushUnit; } }
  101. /// <summary>
  102. /// Speed
  103. /// </summary>
  104. public float Speed
  105. {
  106. get { return speed; }
  107. set
  108. {
  109. value = Mathf.Clamp(value, MinSpeed, MaxSpeed);
  110. if (!MathEx.AmostEqual(value, speed))
  111. {
  112. speed = value;
  113. EditorPrefs.SetFloat("MTE_FlowPainter.speed", value);
  114. }
  115. }
  116. }
  117. /// <summary>
  118. /// Flow direction, angle to north(+u)
  119. /// </summary>
  120. public float Direction
  121. {
  122. get
  123. {
  124. return direction;
  125. }
  126. set
  127. {
  128. value = Mathf.Clamp(value, 0, 2 * Mathf.PI);
  129. if (!MathEx.AmostEqual(value, direction))
  130. {
  131. EditorPrefs.SetFloat("MTE_FlowPainter.direction", direction);
  132. direction = value;
  133. }
  134. }
  135. }
  136. public float Strength
  137. {
  138. get { return strength; }
  139. set
  140. {
  141. value = Mathf.Clamp(value, 0, 1);
  142. if (value != strength)
  143. {
  144. EditorPrefs.SetFloat("MTE_FlowPainter.strength", value);
  145. strength = value;
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// the color
  151. /// </summary>
  152. public Color TheColor
  153. {
  154. get
  155. {
  156. var r = 0.5f - Strength * Mathf.Cos(direction) * 0.5f;
  157. var g = 0.5f + Strength * Mathf.Sin(direction) * 0.5f;
  158. return new Color(r, g, 0);
  159. }
  160. }
  161. public bool Pinching
  162. {
  163. get { return pinching; }
  164. set
  165. {
  166. if (value != pinching)
  167. {
  168. pinching = value;
  169. EditorPrefs.SetBool("MTE_FlowPainter.pinching", value);
  170. }
  171. }
  172. }
  173. public bool VortexRotationClockWise
  174. {
  175. get { return vortexRotationClockWise; }
  176. set
  177. {
  178. if (value != vortexRotationClockWise)
  179. {
  180. vortexRotationClockWise = value;
  181. EditorPrefs.SetBool("MTE_FlowPainter.vortexRotationClockWise", value);
  182. }
  183. }
  184. }
  185. #endregion
  186. public FlowPainter()
  187. {
  188. MTEContext.EnableEvent += (sender, args) =>
  189. {
  190. if (MTEContext.editor == this)
  191. {
  192. LoadSavedParamter();
  193. //LoadPreview();
  194. }
  195. };
  196. MTEContext.EditTypeChangedEvent += (sender, args) =>
  197. {
  198. if (MTEContext.editor == this)
  199. {
  200. LoadSavedParamter();
  201. //LoadPreview();
  202. }
  203. else
  204. {
  205. //UnLoadPreview();
  206. }
  207. };
  208. //MTEContext.DisableEvent += (sender, args) =>
  209. //{
  210. // UnLoadPreview();
  211. //};
  212. // Load default parameters
  213. brushIndex = DefaultBrushIndex;
  214. brushSize = DefaultBrushSize;
  215. speed = DefaultSpeed;
  216. }
  217. private void LoadSavedParamter()
  218. {
  219. // Load parameters from the EditorPrefs
  220. mode = (PaintMode)EditorPrefs.GetInt("MTE_FlowPainter.mode", (int)DefaultMode);
  221. brushIndex = EditorPrefs.GetInt("MTE_FlowPainter.brushIndex", DefaultBrushIndex);
  222. brushSize = EditorPrefs.GetFloat("MTE_FlowPainter.brushSize", DefaultBrushSize);
  223. strength = EditorPrefs.GetFloat("MTE_FlowPainter.strength", DefaultStrength);
  224. speed = EditorPrefs.GetFloat("MTE_FlowPainter.speed", DefaultSpeed);
  225. direction = EditorPrefs.GetFloat("MTE_FlowPainter.direction", DefaultDirection);
  226. pinching = EditorPrefs.GetBool("MTE_FlowPainter.pinching", DefaultPinching);
  227. vortexRotationClockWise = EditorPrefs.GetBool("MTE_FlowPainter.vortexRotationClockWise", DefaultVortexRotationClockWise);
  228. }
  229. public HashSet<Hotkey> DefineHotkeys()
  230. {
  231. return new HashSet<Hotkey>
  232. {
  233. new Hotkey(this, KeyCode.LeftBracket, () =>
  234. {
  235. BrushSize -= 1;
  236. MTEEditorWindow.Instance.Repaint();
  237. }),
  238. new Hotkey(this, KeyCode.RightBracket, () =>
  239. {
  240. BrushSize += 1;
  241. MTEEditorWindow.Instance.Repaint();
  242. }),
  243. new Hotkey(this, KeyCode.Minus, () =>
  244. {
  245. Speed -= 0.01f;
  246. MTEEditorWindow.Instance.Repaint();
  247. }),
  248. new Hotkey(this, KeyCode.Equals, () =>
  249. {
  250. Speed += 0.01f;
  251. MTEEditorWindow.Instance.Repaint();
  252. }),
  253. new Hotkey(this, KeyCode.Space, () =>
  254. {
  255. if (Mode == PaintMode.PinchInflate)
  256. {
  257. //toggle Pinching/Inflate
  258. Pinching = !Pinching;
  259. MTEEditorWindow.Instance.Repaint();
  260. }
  261. else if (Mode == PaintMode.Vortex)
  262. {
  263. //toggle clockwise/counter-clockwise vortex
  264. VortexRotationClockWise = !VortexRotationClockWise;
  265. MTEEditorWindow.Instance.Repaint();
  266. }
  267. })
  268. };
  269. }
  270. public string Header { get { return StringTable.Get(C.PaintFlow_Header); } }
  271. public string Description { get { return StringTable.Get(C.PaintFlow_Description); } }
  272. bool IsGUILoaded = false;
  273. #region GUI contents
  274. GUIContent[] ModeTextContents;
  275. #endregion
  276. Vector2 po;
  277. public void DoArgsGUI()
  278. {
  279. if (!IsGUILoaded)
  280. {
  281. ModeTextContents = new []
  282. {
  283. new GUIContent(MTEStyles.flowToolTexture_Fixed),
  284. new GUIContent(MTEStyles.flowToolTexture_Movement),
  285. new GUIContent(MTEStyles.flowToolTexture_Pinch),
  286. new GUIContent(MTEStyles.flowToolTexture_Vortex),
  287. };
  288. IsGUILoaded = true;
  289. }
  290. // Settings
  291. if (!Settings.CompactGUI)
  292. {
  293. GUILayout.Label(StringTable.Get(C.Mode), MTEStyles.SubHeader);
  294. }
  295. Mode = (PaintMode)GUILayout.Toolbar((int)Mode, ModeTextContents, Settings.CompactGUI ? GUILayout.Height(32) : GUILayout.Height(64));
  296. if(mode == PaintMode.Fixed)
  297. {
  298. if (!Settings.CompactGUI)
  299. {
  300. EditorGUILayout.HelpBox(StringTable.Get(C.Info_ModeDescription_Fixed), MessageType.Info);
  301. GUILayout.Label(StringTable.Get(C.Direction), MTEStyles.SubHeader);
  302. }
  303. EditorGUILayout.BeginHorizontal("box", GUILayout.Height(130));
  304. {
  305. Rect aRect = GUILayoutUtility.GetRect(128, 128, 128, 128, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
  306. po = aRect.center + new Vector2(Mathf.Cos(direction), -Mathf.Sin(direction)) * strength * 55;//55 is the outer radius of *flowDirectionSelectorTexture*
  307. GUI.DrawTexture(aRect, MTEStyles.flowDirectionSelectorTexture);
  308. if (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag)
  309. {
  310. if (aRect.Contains(Event.current.mousePosition))
  311. {
  312. po = Event.current.mousePosition;
  313. var dir = (po - aRect.center);
  314. Strength = dir.magnitude / 55;
  315. Direction = MathEx.AngleFromTo(dir, Vector2.right, new Vector3(0, 0, 1)) * Mathf.Deg2Rad;
  316. MTEEditorWindow.Instance.Repaint();
  317. SceneView.RepaintAll();
  318. }
  319. }
  320. if ((aRect.center - po).sqrMagnitude > 0.01f)
  321. {
  322. Handles.BeginGUI();
  323. GUIEx.DrawLine(new Vector3(aRect.center.x, aRect.center.y), new Vector3(po.x, po.y), 2);
  324. EditorGUI.DrawRect(new Rect(po.x - 2.5f, po.y - 2.5f, 5, 5), Color.blue);
  325. Handles.EndGUI();
  326. }
  327. EditorGUILayout.BeginVertical();
  328. {
  329. GUILayout.FlexibleSpace();
  330. var old = EditorGUIUtility.labelWidth;
  331. EditorGUIUtility.labelWidth = 60;
  332. Strength = EditorGUILayout.FloatField(StringTable.Get(C.Strength), Strength);
  333. EditorGUILayout.Space();
  334. var directionAngle = EditorGUILayout.FloatField(StringTable.Get(C.AngleInDegrees), Direction * Mathf.Rad2Deg);
  335. Direction = directionAngle * Mathf.Deg2Rad;
  336. EditorGUIUtility.labelWidth = old;
  337. GUILayout.FlexibleSpace();
  338. }
  339. EditorGUILayout.EndVertical();
  340. }
  341. EditorGUILayout.EndHorizontal();
  342. EditorGUILayout.BeginVertical("box");
  343. {
  344. EditorGUILayout.BeginHorizontal();
  345. GUILayout.FlexibleSpace();
  346. bool NW = GUILayout.Button("↖", GUILayout.Width(32), GUILayout.Height(32));
  347. bool N = GUILayout.Button("↑", GUILayout.Width(32), GUILayout.Height(32));
  348. bool NE = GUILayout.Button("↗", GUILayout.Width(32), GUILayout.Height(32));
  349. GUILayout.FlexibleSpace();
  350. EditorGUILayout.EndHorizontal();
  351. EditorGUILayout.BeginHorizontal();
  352. GUILayout.FlexibleSpace();
  353. bool W = GUILayout.Button("←", GUILayout.Width(32), GUILayout.Height(32));
  354. bool NOMOVE = GUILayout.Button("C", GUILayout.Width(32), GUILayout.Height(32));
  355. bool E = GUILayout.Button("→", GUILayout.Width(32), GUILayout.Height(32));
  356. GUILayout.FlexibleSpace();
  357. EditorGUILayout.EndHorizontal();
  358. EditorGUILayout.BeginHorizontal();
  359. GUILayout.FlexibleSpace();
  360. bool SW = GUILayout.Button("↙", GUILayout.Width(32), GUILayout.Height(32));
  361. bool S = GUILayout.Button("↓", GUILayout.Width(32), GUILayout.Height(32));
  362. bool SE = GUILayout.Button("↘", GUILayout.Width(32), GUILayout.Height(32));
  363. GUILayout.FlexibleSpace();
  364. EditorGUILayout.EndHorizontal();
  365. if (NW) { Strength = 1; Direction = 0.75f * Mathf.PI; }
  366. if (N) { Strength = 1; Direction = 0.5f * Mathf.PI; }
  367. if (NE) { Strength = 1; Direction = 0.25f * Mathf.PI; }
  368. if (W) { Strength = 1; Direction = Mathf.PI; }
  369. if (NOMOVE) { Strength = 0; Direction = -1; }
  370. if (E) { Strength = 1; Direction = 0; }
  371. if (SW) { Strength = 1; Direction = 1.25f * Mathf.PI; }
  372. if (S) { Strength = 1; Direction = 1.5f * Mathf.PI; }
  373. if (SE) { Strength = 1; Direction = 1.75f * Mathf.PI; }
  374. }
  375. EditorGUILayout.EndVertical();
  376. }
  377. else if (mode == PaintMode.Movement)
  378. {
  379. if (!Settings.CompactGUI)
  380. {
  381. EditorGUILayout.HelpBox(StringTable.Get(C.Info_ModeDescription_Movement), MessageType.Info);
  382. }
  383. Strength = EditorGUILayoutEx.Slider(StringTable.Get(C.Strength), Strength, 0, 1);
  384. }
  385. else if (mode == PaintMode.PinchInflate)
  386. {
  387. if (!Settings.CompactGUI)
  388. {
  389. EditorGUILayout.HelpBox(StringTable.Get(C.Info_ModeDescription_Pinch), MessageType.Info);
  390. }
  391. Strength = EditorGUILayoutEx.Slider(StringTable.Get(C.Strength), Strength, 0, 1);
  392. }
  393. else if (mode == PaintMode.Vortex)
  394. {
  395. if (!Settings.CompactGUI)
  396. {
  397. EditorGUILayout.HelpBox(StringTable.Get(C.Info_ModeDescription_Vortex), MessageType.Info);
  398. }
  399. Strength = EditorGUILayoutEx.Slider(StringTable.Get(C.Strength), Strength, 0, 1);
  400. }
  401. // Shared settings
  402. if (!Settings.CompactGUI)
  403. {
  404. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  405. }
  406. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  407. Speed = EditorGUILayoutEx.Slider(StringTable.Get(C.Speed), "[", "]", Speed, MinSpeed, MaxSpeed);
  408. // Tools
  409. if (!Settings.CompactGUI)
  410. {
  411. EditorGUILayout.Space();
  412. GUILayout.Label(StringTable.Get(C.Tools), MTEStyles.SubHeader);
  413. }
  414. EditorGUILayout.BeginVertical();
  415. {
  416. if(Mode == PaintMode.Fixed)
  417. {
  418. EditorGUILayout.BeginHorizontal();
  419. {
  420. if (GUILayout.Button(StringTable.Get(C.CreateFlowMap), GUILayout.Width(100), GUILayout.Height(40)))
  421. {
  422. CreateFlowMap(TheColor);
  423. }
  424. GUILayout.Space(20);
  425. EditorGUILayout.LabelField(StringTable.Get(C.Info_ToolDescription_CreateFlowMap), MTEStyles.labelFieldWordwrap);
  426. GUI.enabled = true;
  427. }
  428. EditorGUILayout.EndHorizontal();
  429. }
  430. }
  431. EditorGUILayout.EndVertical();
  432. GUILayout.FlexibleSpace();
  433. EditorGUILayout.HelpBox(StringTable.Get(C.Info_WillBeSavedInstantly),
  434. MessageType.Info, true);
  435. }
  436. internal struct TextureModifyGroup
  437. {
  438. public GameObject gameObject;
  439. public Texture2D texture;
  440. public Rect texelRect;
  441. public Vector2 center;
  442. public TextureModifyGroup(GameObject gameObject, Texture2D texture, Rect texelRect, Vector2 center)
  443. {
  444. this.gameObject = gameObject;
  445. this.texture = texture;
  446. this.texelRect = texelRect;
  447. this.center = center;
  448. }
  449. }
  450. // buffers of editing helpers
  451. private readonly List<TextureModifyGroup> modifyGroups = new List<TextureModifyGroup>(4);
  452. private readonly List<Color[]> modifyingSections = new List<Color[]>(2);
  453. Vector2 mouseDelta;
  454. public void OnSceneGUI()
  455. {
  456. var e = Event.current;
  457. mouseDelta = e.delta;//used by PaintMode.Movement
  458. if (e.commandName == "UndoRedoPerformed")
  459. {
  460. SceneView.RepaintAll();
  461. return;
  462. }
  463. if (!(EditorWindow.mouseOverWindow is SceneView))
  464. {
  465. return;
  466. }
  467. if (Mode == PaintMode.Fixed)
  468. {
  469. RaycastHit hit;
  470. Ray ray1 = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  471. if (Physics.Raycast(ray1, out hit,
  472. Mathf.Infinity,
  473. 1 << MTEContext.TargetLayer//only hit target layer
  474. ))
  475. {
  476. //check tag
  477. if (!hit.transform.CompareTag(MTEContext.TargetTag))
  478. {
  479. return;
  480. }
  481. if (strength > 0.01f)
  482. {
  483. Handles.ArrowHandleCap(0, hit.point, Quaternion.Euler(0, -(Direction - 0.5f * Mathf.PI) * Mathf.Rad2Deg, 0), BrushSizeInU3D + Strength * Settings.PointSize, EventType.Repaint);
  484. }
  485. else
  486. {
  487. Handles.color = Color.red;
  488. Handles.SphereHandleCap(0, hit.point, Quaternion.identity, Settings.PointSize, EventType.Repaint);
  489. }
  490. }
  491. }
  492. // do nothing when mouse middle/right button, alt key is pressed
  493. if (e.button != 0 || e.alt)
  494. return;
  495. if(Mode == PaintMode.Fixed)
  496. {
  497. // hold control key and scroll wheel to change flow direction
  498. if (e.control && !e.isKey && e.type == EventType.ScrollWheel)
  499. {
  500. float oldDirection = Direction;
  501. float direction = oldDirection;
  502. ChangeDirection(e.delta.y, ref direction);
  503. if (Mathf.Abs(direction - oldDirection) > Mathf.Epsilon)
  504. {
  505. MTEEditorWindow.Instance.Repaint();
  506. Direction = direction;
  507. }
  508. e.Use();
  509. }
  510. }
  511. if(Mode == PaintMode.Movement)
  512. {
  513. this.delta[Time.frameCount % smoothing] = new Vector3(-this.mouseDelta.x, 0f, -this.mouseDelta.y);
  514. this.vector = Vector3.zero;
  515. foreach (Vector3 b in this.delta)
  516. {
  517. this.vector += b;
  518. }
  519. this.vector /= (float)smoothing;
  520. this.vector.y = 0f;
  521. this.vector *= Time.fixedDeltaTime;
  522. this.vector.x = this.vector.x * 0.5f + 0.5f;
  523. this.vector.z = this.vector.z * 0.5f + 0.5f;
  524. }
  525. HandleUtility.AddDefaultControl(0);
  526. var ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  527. RaycastHit raycastHit;
  528. if (Physics.Raycast(ray, out raycastHit,
  529. Mathf.Infinity,
  530. 1 << MTEContext.TargetLayer//only hit target layer
  531. ))
  532. {
  533. //check tag
  534. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  535. {
  536. return;
  537. }
  538. var currentBrushSize = BrushSizeInU3D;
  539. if (Settings.ShowBrushRect)
  540. {
  541. Utility.ShowBrushRect(raycastHit.point, currentBrushSize);
  542. }
  543. var hitPoint = raycastHit.point;
  544. if (Settings.FlashAffectedVertex)
  545. {
  546. Handles.color = Utility.GetFlashingColor();
  547. }
  548. else
  549. {
  550. Handles.color = Color.green;
  551. }
  552. Handles.CircleHandleCap(0, hitPoint, Quaternion.LookRotation(Vector3.up), BrushSizeInU3D/2, EventType.Repaint);
  553. // collect modifiy group
  554. modifyGroups.Clear();
  555. foreach (var target in MTEContext.Targets)
  556. {
  557. // check if we can paint on target
  558. var meshRenderer = target.GetComponent<MeshRenderer>();
  559. if (meshRenderer == null) continue;
  560. var meshFilter = target.GetComponent<MeshFilter>();
  561. if (meshFilter == null) continue;
  562. var mesh = meshFilter.sharedMesh;
  563. if (mesh == null) continue;
  564. var material = meshRenderer.sharedMaterial;
  565. if (!material.HasProperty("_FlowMap"))
  566. {
  567. continue;
  568. }
  569. Vector2 textureUVMin;//min texture uv that is to be modified
  570. Vector2 textureUVMax;//max texture uv that is to be modified
  571. Vector2 centerUV;
  572. {
  573. // check if the brush rect intersects with the `Mesh.bounds` of this target
  574. var hitPointLocal = target.transform.InverseTransformPoint(hitPoint);//convert hit point from world space to target mesh space
  575. Bounds brushBounds = new Bounds(center: new Vector3(hitPointLocal.x, 0, hitPointLocal.z), size: new Vector3(currentBrushSize, 99999, currentBrushSize));
  576. Bounds meshBounds = mesh.bounds;//TODO rename this
  577. centerUV = new Vector2(
  578. MathEx.NormalizeTo01(meshBounds.min.x, meshBounds.max.x, hitPointLocal.x),
  579. MathEx.NormalizeTo01(meshBounds.min.z, meshBounds.max.z, hitPointLocal.z));//FIXME should we use a coordinate in world space?
  580. Bounds paintingBounds;
  581. var intersected = meshBounds.Intersect(brushBounds, out paintingBounds);
  582. if (!intersected) continue;
  583. Vector2 paintingBounds2D_min = new Vector2(paintingBounds.min.x, paintingBounds.min.z);
  584. Vector2 paintingBounds2D_max = new Vector2(paintingBounds.max.x, paintingBounds.max.z);
  585. Vector2 meshRendererBounds2D_min = new Vector2(meshBounds.min.x, meshBounds.min.z);
  586. Vector2 meshRendererBounds2D_max = new Vector2(meshBounds.max.x, meshBounds.max.z);
  587. //calculate which part of texture should be modified
  588. textureUVMin = MathEx.NormalizeTo01(rangeMin: meshRendererBounds2D_min, rangeMax: meshRendererBounds2D_max, value: paintingBounds2D_min);
  589. textureUVMax = MathEx.NormalizeTo01(rangeMin: meshRendererBounds2D_min, rangeMax: meshRendererBounds2D_max, value: paintingBounds2D_max);
  590. if (Settings.DebugMode)
  591. {
  592. Handles.color = Color.blue;
  593. HandlesEx.DrawRectangle(paintingBounds2D_min, paintingBounds2D_max);
  594. Handles.color = new Color(255, 128, 166);
  595. HandlesEx.DrawRectangle(meshRendererBounds2D_min, meshRendererBounds2D_max);
  596. }
  597. }
  598. if (e.button == 0 && (e.type == EventType.MouseDown || e.type == EventType.MouseDrag))
  599. {
  600. Texture2D targetTexture = null;
  601. if (material.GetTexture("_FlowMap") != null)
  602. {
  603. targetTexture = (Texture2D)material.GetTexture("_FlowMap");
  604. }
  605. else
  606. {
  607. MTEDebug.LogWarningFormat("\"_FlowMap\" is not assigned or existing in material<{0}>.", material.name);
  608. return;
  609. }
  610. //get modifying texel rect of the control texture
  611. var x = textureUVMin.x * targetTexture.width;
  612. var y = textureUVMin.y * targetTexture.height;
  613. var width = (textureUVMax.x - textureUVMin.x) * targetTexture.width;
  614. var height = (textureUVMax.y - textureUVMin.y) * targetTexture.height;
  615. var texelRect = new Rect(x, y, width, height);
  616. Vector2 center = new Vector2(centerUV.x * targetTexture.width, centerUV.y * targetTexture.height);
  617. modifyGroups.Add(new TextureModifyGroup(target, targetTexture, texelRect, center));
  618. }
  619. if (Settings.DebugMode)
  620. {
  621. Handles.BeginGUI();
  622. GUILayout.Button(string.Format("center uv: ({0},{1})", centerUV.x, centerUV.y));
  623. Handles.EndGUI();
  624. }
  625. }
  626. //record undo operation for targets that to be modified
  627. if (e.type == EventType.MouseDown)
  628. {
  629. using (new Undo.UndoTransaction("Paint Flow"))
  630. {
  631. foreach (var target in MTEContext.Targets)
  632. {
  633. var material = target.GetComponent<MeshRenderer>().sharedMaterial;
  634. if (material.HasProperty("_FlowMap"))
  635. {
  636. Texture2D texture = (Texture2D)material.GetTexture("_FlowMap");
  637. if (texture != null)
  638. {
  639. var originalColors = texture.GetPixels();
  640. UndoRedoManager.Instance().Push(a =>
  641. {
  642. texture.ModifyPixels(a);
  643. texture.Apply();
  644. Save(texture);
  645. }, originalColors, "Paint flow map");
  646. }
  647. }
  648. }
  649. }
  650. }
  651. // execute the modification
  652. if (modifyGroups.Count != 0)
  653. {
  654. for (int i = 0; i < modifyGroups.Count; i++)
  655. {
  656. var modifyGroup = modifyGroups[i];
  657. var gameObject = modifyGroup.gameObject;
  658. var material = gameObject.GetComponent<MeshRenderer>().sharedMaterial;
  659. if (material.HasProperty("_FlowMap"))
  660. {
  661. Utility.SetTextureReadable(material.GetTexture("_FlowMap"));
  662. if (mode == PaintMode.Fixed)
  663. {
  664. PaintTextureFixed(modifyGroup);
  665. }
  666. else if(mode == PaintMode.Movement)
  667. {
  668. PaintTextureMovement(modifyGroup.texture, modifyGroup.texelRect);
  669. }
  670. else if (mode == PaintMode.PinchInflate)
  671. {
  672. PaintTexturePinchInflate(modifyGroup.texture, modifyGroup.texelRect);
  673. }
  674. else if (mode == PaintMode.Vortex)
  675. {
  676. PaintTextureVortex(modifyGroup.texture, modifyGroup.texelRect);
  677. }
  678. }
  679. }
  680. }
  681. // auto save when mouse up
  682. if (e.type == EventType.MouseUp && e.button == 0)
  683. {
  684. foreach (var target in MTEContext.Targets)
  685. {
  686. var material = target.GetComponent<MeshRenderer>().sharedMaterial;
  687. if (material.HasProperty("_FlowMap"))
  688. {
  689. Texture2D texture = (Texture2D)material.GetTexture("_FlowMap");
  690. if (texture != null)
  691. {
  692. Save(texture);
  693. }
  694. }
  695. }
  696. }
  697. }
  698. SceneView.RepaintAll();
  699. }
  700. private static void Save(Texture2D texture)
  701. {
  702. if (texture == null)
  703. {
  704. throw new System.ArgumentNullException("texture");
  705. }
  706. var path = AssetDatabase.GetAssetPath(texture);
  707. var bytes = texture.EncodeToPNG();
  708. File.WriteAllBytes(path, bytes);
  709. }
  710. private void ChangeDirection(float delta, ref float direction)
  711. {
  712. if(delta > 0)
  713. {
  714. direction -= Mathf.PI / 12;
  715. }
  716. else if(delta < 0)
  717. {
  718. direction += Mathf.PI / 12;
  719. }
  720. if(direction < 0)
  721. {
  722. direction += 2*Mathf.PI;
  723. }
  724. if (direction > 2*Mathf.PI)
  725. {
  726. direction -= 2*Mathf.PI;
  727. }
  728. }
  729. private static void CreateFlowMap(Color flowColor)
  730. {
  731. var texture = new Texture2D(512, 512, TextureFormat.ARGB32, true);
  732. var colors = new Color[512 * 512];
  733. for (var t = 0; t < colors.Length; t++)
  734. {
  735. colors[t] = flowColor;
  736. }
  737. texture.SetPixels(colors);
  738. var relativePath = "Assets/FlowMap.png";
  739. System.IO.File.WriteAllBytes(relativePath, texture.EncodeToPNG());
  740. AssetDatabase.ImportAsset(relativePath, ImportAssetOptions.ForceUpdate);
  741. var textureImporter = (TextureImporter)AssetImporter.GetAtPath(relativePath);
  742. textureImporter.isReadable = true;
  743. textureImporter.SaveAndReimport();
  744. texture = AssetDatabase.LoadAssetAtPath<Texture>(relativePath) as Texture2D;
  745. Selection.activeObject = texture;
  746. }
  747. #region GUI
  748. const int LabelWidth = 90;
  749. #endregion
  750. #region Preview (not used)
  751. public GameObject previewObj;
  752. internal void LoadPreview()
  753. {
  754. UnLoadPreview();
  755. previewObj = new GameObject("MTEPreview");
  756. previewObj.SetActive(false);
  757. var projector = previewObj.AddComponent<Projector>();
  758. previewObj.hideFlags = HideFlags.HideAndDontSave;
  759. projector.material = new Material(Shader.Find("Hidden/MTE/PaintTexturePreview"));
  760. projector.orthographic = true;
  761. projector.nearClipPlane = -1000;
  762. projector.farClipPlane = 1000;
  763. projector.transform.Rotate(90, 0, 0);
  764. SetPreviewTexture(Vector2.one, MTEStyles.flowPainterMaskTextures[BrushIndex], null);
  765. SetPreviewSize(BrushSizeInU3D / 2);
  766. SetPreviewMaskTexture(BrushIndex);
  767. }
  768. internal void UnLoadPreview()
  769. {
  770. if (previewObj != null)
  771. {
  772. UnityEngine.Object.DestroyImmediate(previewObj);
  773. }
  774. }
  775. private void SetPreviewTexture(Vector2 textureScale, Texture texture, Texture normalTexture)
  776. {
  777. var projector = previewObj.GetComponent<Projector>();
  778. projector.material.SetTexture("_MainTex", texture);
  779. projector.material.SetTextureScale("_MainTex", textureScale);
  780. projector.material.SetTexture("_NormalTex", normalTexture);
  781. SceneView.RepaintAll();
  782. }
  783. private void SetPreviewMaskTexture(int maskIndex)
  784. {
  785. var projector = previewObj.GetComponent<Projector>();
  786. projector.material.SetTexture("_MaskTex", MTEStyles.flowPainterMaskTextures[maskIndex]);
  787. projector.material.SetTextureScale("_MaskTex", Vector2.one);
  788. SceneView.RepaintAll();
  789. }
  790. private void SetPreviewSize(float value)
  791. {
  792. var projector = previewObj.GetComponent<Projector>();
  793. projector.orthographicSize = value;
  794. SceneView.RepaintAll();
  795. }
  796. #endregion
  797. private const int smoothing = 10;
  798. private Vector3[] delta = new Vector3[smoothing];
  799. private Vector3 vector;
  800. private AnimationCurve falloff = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
  801. private void PaintTextureFixed(TextureModifyGroup modifyGroup)
  802. {
  803. Texture2D texture = modifyGroup.texture;
  804. Rect texelRect = modifyGroup.texelRect;
  805. // clear modifying sections
  806. modifyingSections.Clear();
  807. // get accurate texel rect
  808. int x = Mathf.Clamp(Mathf.RoundToInt(texelRect.x), 0, texture.width);
  809. int y = Mathf.Clamp(Mathf.RoundToInt(texelRect.y + 0.5f), 0, texture.height);
  810. int width = Mathf.Clamp(Mathf.RoundToInt(texelRect.width), 1, texture.width - x);
  811. int height = Mathf.Clamp(Mathf.RoundToInt(texelRect.height), 1, texture.height - y);
  812. // modify target
  813. var offset = texelRect.position;
  814. var replaced = texture.GetPixels(x, y, width, height, 0);
  815. var r = texelRect.width * 0.5f;
  816. var center = modifyGroup.center;
  817. // blend the pixel section
  818. for (var i = 0; i < height; i++)
  819. {
  820. for (var j = 0; j < width; j++)
  821. {
  822. var pixelIndex = i * width + j;
  823. var point = new Vector2(j, i);
  824. var distanceToCenter = Vector2.Distance(offset + point, center);
  825. if (distanceToCenter > r)
  826. {
  827. continue;
  828. }
  829. replaced[pixelIndex] = Color.Lerp(replaced[pixelIndex], TheColor, Speed);
  830. }
  831. }
  832. // modify the control texture
  833. texture.SetPixels(x, y, width, height, replaced);
  834. texture.Apply();
  835. }
  836. private void PaintTextureMovement(Texture2D texture, Rect texelRect)
  837. {
  838. // check parameters
  839. if (texture == null)
  840. {
  841. throw new System.ArgumentNullException("texture");
  842. }
  843. // clear modifying sections
  844. modifyingSections.Clear();
  845. int x = (int)texelRect.x;
  846. int y = (int)texelRect.y;
  847. int width = (int)texelRect.width;
  848. int height = (int)texelRect.height;
  849. // modify target
  850. var replaced = texture.GetPixels(x, y, width, height, 0);
  851. var r = width*0.5f;
  852. // blend the pixel section
  853. for (var i = 0; i < height; i++)
  854. {
  855. for (var j = 0; j < width; j++)
  856. {
  857. var pixelIndex = i * width + j;
  858. var distanceToCenter = Mathf.Sqrt((i - r) * (i - r) + (j - r) * (j - r));
  859. if(distanceToCenter > r)
  860. {
  861. continue;
  862. }
  863. var k = distanceToCenter / r;
  864. float t = Mathf.Clamp01(this.falloff.Evaluate(k)) * Strength;
  865. var oldColor = replaced[pixelIndex];
  866. var newColor = new Color(
  867. Mathf.Lerp(oldColor.r, this.vector.x, t),
  868. Mathf.Lerp(oldColor.g, this.vector.z, t),
  869. oldColor.b);
  870. replaced[pixelIndex] = Color.Lerp(oldColor, newColor, Speed);
  871. }
  872. }
  873. // modify the control texture
  874. texture.SetPixels(x, y, width, height, replaced);
  875. texture.Apply();
  876. }
  877. private void PaintTexturePinchInflate(Texture2D texture, Rect texelRect)
  878. {
  879. // check parameters
  880. if (texture == null)
  881. {
  882. throw new System.ArgumentNullException("texture");
  883. }
  884. // clear modifying sections
  885. modifyingSections.Clear();
  886. int x = (int)texelRect.x;
  887. int y = (int)texelRect.y;
  888. int width = (int)texelRect.width;
  889. int height = (int)texelRect.height;
  890. // modify target
  891. var replaced = texture.GetPixels(x, y, width, height, 0);
  892. var r = width * 0.5f;
  893. var center = new Vector2(r, r);
  894. var dir = Vector2.zero;
  895. // blend the pixel section
  896. for (var i = 0; i < height; i++)
  897. {
  898. for (var j = 0; j < width; j++)
  899. {
  900. var pixelIndex = i * width + j;
  901. var point = new Vector2(j, i);
  902. var distanceToCenter = Vector2.Distance(point, center);
  903. if (distanceToCenter > r)
  904. {
  905. continue;
  906. }
  907. var k = distanceToCenter / r;
  908. float t = Mathf.Clamp01(this.falloff.Evaluate(k)) * Strength;
  909. if (this.Pinching)
  910. {
  911. dir = (center - point) / r;
  912. }
  913. else
  914. {
  915. dir = (point - center) / r;
  916. }
  917. dir *= 2 * (Mathf.Cos(Mathf.PI * k) * 0.5f + 0.5f) * Strength;
  918. var oldColor = replaced[pixelIndex];
  919. var newColor = new Color(
  920. Mathf.Lerp(oldColor.r, dir.x * 0.5f + 0.5f, t),
  921. Mathf.Lerp(oldColor.g, -dir.y * 0.5f + 0.5f, t),
  922. oldColor.b);
  923. replaced[pixelIndex] = Color.Lerp(oldColor, newColor, Speed);
  924. }
  925. }
  926. // modify the control texture
  927. texture.SetPixels(x, y, width, height, replaced);
  928. texture.Apply();
  929. }
  930. private void PaintTextureVortex(Texture2D texture, Rect texelRect)
  931. {
  932. // check parameters
  933. if (texture == null)
  934. {
  935. throw new System.ArgumentNullException("texture");
  936. }
  937. // clear modifying sections
  938. modifyingSections.Clear();
  939. int x = (int)texelRect.x;
  940. int y = (int)texelRect.y;
  941. int width = (int)texelRect.width;
  942. int height = (int)texelRect.height;
  943. // modify target
  944. var replaced = texture.GetPixels(x, y, width, height, 0);
  945. var r = width * 0.5f;
  946. var center = new Vector2(r, r);
  947. var dir = Vector2.zero;
  948. // blend the pixel section
  949. for (var i = 0; i < height; i++)
  950. {
  951. for (var j = 0; j < width; j++)
  952. {
  953. var pixelIndex = i * width + j;
  954. var point = new Vector2(j, i);
  955. var distanceToCenter = Vector2.Distance(point, center);
  956. if (distanceToCenter > r)
  957. {
  958. continue;
  959. }
  960. var k = distanceToCenter / r;
  961. float t = Mathf.Clamp01(this.falloff.Evaluate(k)) * Strength;
  962. if (this.VortexRotationClockWise)
  963. {
  964. dir = (center - point) / r;
  965. }
  966. else
  967. {
  968. dir = (point - center) / r;
  969. }
  970. dir *= 2 * (Mathf.Cos(Mathf.PI * k) * 0.5f + 0.5f) * Strength;
  971. var oldColor = replaced[pixelIndex];
  972. var newColor = new Color(
  973. Mathf.Lerp(oldColor.r, dir.y * 0.5f + 0.5f, t),
  974. Mathf.Lerp(oldColor.g, dir.x * 0.5f + 0.5f, t),
  975. oldColor.b);
  976. replaced[pixelIndex] = Color.Lerp(oldColor, newColor, Speed);
  977. }
  978. }
  979. // modify the control texture
  980. texture.SetPixels(x, y, width, height, replaced);
  981. texture.Apply();
  982. }
  983. }
  984. }