TextureArrayPainter.cs 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using MTE.Undo;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using System.Runtime.CompilerServices;
  9. using static MTE.TextureArrayShaderPropertyNames;
  10. using System.Runtime.InteropServices;
  11. namespace MTE
  12. {
  13. /// <summary>
  14. /// Texture-array-based Mesh-Terrain texture editor.
  15. /// </summary>
  16. internal class TextureArrayPainter : IEditor
  17. {
  18. public int Id { get; } = 10;
  19. public bool Enabled { get; set; } = true;
  20. public string Name { get; } = nameof(TextureArrayPainter);
  21. public Texture Icon { get; } =
  22. EditorGUIUtility.IconContent("TerrainInspector.TerrainToolSplat").image;
  23. public string Header { get { return StringTable.Get(C.TextureArrayPainter_Header); } }
  24. public string Description { get { return StringTable.Get(C.TextureArrayPainter_Description); } }
  25. public bool WantMouseMove { get; } = true;
  26. public bool WillEditMesh { get; } = false;
  27. #region Parameters
  28. #region Constant
  29. // default
  30. const EditorFilterMode DefaultPainterMode
  31. = EditorFilterMode.FilteredGameObjects;
  32. const float DefaultBrushSize = 1;
  33. const float DefaultBrushFlow = 0.5f;
  34. // min/max
  35. const float MinBrushSize = 0.1f;
  36. const float MaxBrushSize = 10f;
  37. const float MinBrushFlow = 0.01f;
  38. const float MaxBrushFlow = 1f;
  39. const int MaxHotkeyNumberForTexture = 8;
  40. #endregion
  41. public int brushIndex;
  42. public float brushSize;
  43. public float brushFlow;
  44. private int selectedTextureIndex;
  45. private EditorFilterMode painterMode;
  46. private EditorFilterMode PainterMode
  47. {
  48. get { return this.painterMode; }
  49. set
  50. {
  51. if (value != this.painterMode)
  52. {
  53. EditorPrefs.SetInt("MTE_SplatPainter.painterMode", (int)value);
  54. this.painterMode = value;
  55. }
  56. }
  57. }
  58. /// <summary>
  59. /// Index of selected texture in the texture list; not the layer index.
  60. /// </summary>
  61. public int SelectedTextureIndex
  62. {
  63. get { return this.selectedTextureIndex; }
  64. set
  65. {
  66. var textureListCount = TextureList.Count;
  67. if (value < textureListCount)
  68. {
  69. this.selectedTextureIndex = value;
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Index of selected brush
  75. /// </summary>
  76. public int BrushIndex
  77. {
  78. get { return brushIndex; }
  79. set
  80. {
  81. if (brushIndex != value)
  82. {
  83. preview.SetPreviewMaskTexture(value);
  84. brushIndex = value;
  85. }
  86. }
  87. }
  88. /// <summary>
  89. /// Brush size (unit: 1 BrushUnit)
  90. /// </summary>
  91. public float BrushSize
  92. {
  93. get { return brushSize; }
  94. set
  95. {
  96. value = Mathf.Clamp(value, MinBrushSize, MaxBrushSize);
  97. if (!MathEx.AmostEqual(brushSize, value))
  98. {
  99. brushSize = value;
  100. EditorPrefs.SetFloat("MTE_TextureArrayPainter.brushSize", value);
  101. if (PainterMode == EditorFilterMode.FilteredGameObjects)
  102. {
  103. preview.SetPreviewSize(BrushSizeInU3D/2);
  104. }
  105. else
  106. {
  107. //preview size for SelectedGameObject mode are set in OnSceneGUI
  108. }
  109. }
  110. }
  111. }
  112. //real brush size
  113. private float BrushSizeInU3D { get { return BrushSize * Settings.BrushUnit; } }
  114. /// <summary>
  115. /// Brush flow
  116. /// </summary>
  117. public float BrushFlow
  118. {
  119. get { return brushFlow; }
  120. set
  121. {
  122. value = Mathf.Clamp(value, MinBrushFlow, MaxBrushFlow);
  123. if (Mathf.Abs(brushFlow - value) > 0.0001f)
  124. {
  125. brushFlow = value;
  126. EditorPrefs.SetFloat("MTE_TextureArrayPainter.brushFlow", value);
  127. }
  128. }
  129. }
  130. #endregion
  131. #region UI
  132. private static readonly GUIContent[] EditorFilterModeContents =
  133. {
  134. new GUIContent(StringTable.Get(C.SplatPainter_Mode_Filtered),
  135. StringTable.Get(C.SplatPainter_Mode_FilteredDescription)),
  136. new GUIContent(StringTable.Get(C.SplatPainter_Mode_Selected),
  137. StringTable.Get(C.SplatPainter_Mode_SelectedDescription)),
  138. };
  139. #endregion
  140. public TextureArrayPainter()
  141. {
  142. MTEContext.EnableEvent += (sender, args) =>
  143. {
  144. if (MTEContext.editor == this)
  145. {
  146. LoadSavedParamter();
  147. LoadTextureList();
  148. if (PainterMode == EditorFilterMode.SelectedGameObject)
  149. {
  150. BuildEditingInfoForLegacyMode(Selection.activeGameObject);
  151. }
  152. if (TextureList.Count != 0)
  153. {
  154. if (SelectedTextureIndex < 0)
  155. {
  156. SelectedTextureIndex = 0;
  157. }
  158. LoadPreview();
  159. }
  160. }
  161. };
  162. MTEContext.EditTypeChangedEvent += (sender, args) =>
  163. {
  164. if (MTEContext.editor == this)
  165. {
  166. LoadSavedParamter();
  167. LoadTextureList();
  168. if (PainterMode == EditorFilterMode.SelectedGameObject)
  169. {
  170. BuildEditingInfoForLegacyMode(Selection.activeGameObject);
  171. }
  172. if (TextureList.Count != 0)
  173. {
  174. if (SelectedTextureIndex < 0 || SelectedTextureIndex > TextureList.Count - 1)
  175. {
  176. SelectedTextureIndex = 0;
  177. }
  178. LoadPreview();
  179. }
  180. }
  181. else
  182. {
  183. if (preview != null)
  184. {
  185. preview.UnLoadPreview();
  186. }
  187. }
  188. };
  189. MTEContext.SelectionChangedEvent += (sender, args) =>
  190. {
  191. if (args.SelectedGameObject)
  192. {
  193. if (PainterMode == EditorFilterMode.SelectedGameObject)
  194. {
  195. BuildEditingInfoForLegacyMode(args.SelectedGameObject);
  196. }
  197. }
  198. };
  199. MTEContext.TextureChangedEvent += (sender, args) =>
  200. {
  201. if (MTEContext.editor == this)
  202. {
  203. LoadTextureList();
  204. if (PainterMode == EditorFilterMode.SelectedGameObject)
  205. {
  206. BuildEditingInfoForLegacyMode(Selection.activeGameObject);
  207. }
  208. }
  209. };
  210. MTEContext.DisableEvent += (sender, args) =>
  211. {
  212. if (preview != null)
  213. {
  214. preview.UnLoadPreview();
  215. }
  216. };
  217. MTEContext.EditTargetsLoadedEvent += (sender, args) =>
  218. {
  219. if (MTEContext.editor == this)
  220. {
  221. LoadTextureList();
  222. }
  223. };
  224. // Load default parameters
  225. painterMode = DefaultPainterMode;
  226. brushSize = DefaultBrushSize;
  227. brushFlow = DefaultBrushFlow;
  228. }
  229. private void LoadPreview()
  230. {
  231. var texture = TextureList[SelectedTextureIndex];
  232. preview.LoadPreview(texture, BrushSizeInU3D, BrushIndex);
  233. }
  234. private void LoadSavedParamter()
  235. {
  236. painterMode = (EditorFilterMode)EditorPrefs.GetInt(
  237. "MTE_TextureArrayPainter.painterMode", (int)DefaultPainterMode);
  238. brushSize = EditorPrefs.GetFloat("MTE_TextureArrayPainter.brushSize", DefaultBrushSize);
  239. brushFlow = EditorPrefs.GetFloat("MTE_TextureArrayPainter.brushFlow", DefaultBrushFlow);
  240. }
  241. private GameObject targetGameObject { get; set; }
  242. private Mesh targetMesh { get; set; }
  243. private Material targetMaterial { get; set; }
  244. private Texture2D[] controlTextures { get; } = new Texture2D[3] {null, null, null};
  245. private void BuildEditingInfoForLegacyMode(GameObject gameObject)
  246. {
  247. //reset
  248. this.TextureList.Clear();
  249. this.targetGameObject = null;
  250. this.targetMaterial = null;
  251. this.targetMesh = null;
  252. //check gameObject
  253. if (!gameObject)
  254. {
  255. return;
  256. }
  257. if (PainterMode != EditorFilterMode.SelectedGameObject)
  258. {
  259. return;
  260. }
  261. var meshFilter = gameObject.GetComponent<MeshFilter>();
  262. if (!meshFilter)
  263. {
  264. return;
  265. }
  266. var meshRenderer = gameObject.GetComponent<MeshRenderer>();
  267. if (!meshRenderer)
  268. {
  269. return;
  270. }
  271. var material = meshRenderer.sharedMaterial;
  272. if (!material)
  273. {
  274. return;
  275. }
  276. if (!MTEShaders.IsMTETextureArrayShader(material.shader))
  277. {
  278. return;
  279. }
  280. //collect targets info
  281. this.targetGameObject = gameObject;
  282. this.targetMaterial = material;
  283. this.targetMesh = meshFilter.sharedMesh;
  284. // Texture
  285. LoadTextureList();
  286. LoadControlTextures();
  287. // Preview
  288. if (TextureList.Count != 0)
  289. {
  290. if (SelectedTextureIndex < 0 || SelectedTextureIndex > TextureList.Count - 1)
  291. {
  292. SelectedTextureIndex = 0;
  293. }
  294. preview.LoadPreview(TextureList[SelectedTextureIndex],
  295. BrushSizeInU3D,
  296. BrushIndex);
  297. }
  298. }
  299. private static class Styles
  300. {
  301. public static string NoGameObjectSelectedHintText;
  302. private static bool unloaded= true;
  303. public static void Init()
  304. {
  305. if (!unloaded) return;
  306. NoGameObjectSelectedHintText
  307. = StringTable.Get(C.Info_PleaseSelectAGameObjectWithVaildMesh);
  308. unloaded = false;
  309. }
  310. }
  311. public void DoArgsGUI()
  312. {
  313. Styles.Init();
  314. EditorGUI.BeginChangeCheck();
  315. this.PainterMode = (EditorFilterMode)GUILayout.Toolbar(
  316. (int)this.PainterMode, EditorFilterModeContents);
  317. if (EditorGUI.EndChangeCheck())
  318. {
  319. LoadTextureList();
  320. if (PainterMode == EditorFilterMode.SelectedGameObject)
  321. {
  322. BuildEditingInfoForLegacyMode(Selection.activeGameObject);
  323. }
  324. LoadPreview();
  325. }
  326. if (PainterMode == EditorFilterMode.SelectedGameObject
  327. && Selection.activeGameObject == null)
  328. {
  329. EditorGUILayout.HelpBox(Styles.NoGameObjectSelectedHintText, MessageType.Warning);
  330. return;
  331. }
  332. BrushIndex = Utility.ShowBrushes(BrushIndex);
  333. // Splat-textures
  334. if (!Settings.CompactGUI)
  335. {
  336. GUILayout.Label(StringTable.Get(C.Textures), MTEStyles.SubHeader);
  337. }
  338. EditorGUILayout.BeginVertical("box");
  339. {
  340. var textureListCount = TextureList.Count;
  341. if (textureListCount == 0)
  342. {
  343. if (PainterMode == EditorFilterMode.FilteredGameObjects)
  344. {
  345. EditorGUILayout.LabelField(
  346. StringTable.Get(C.Info_SplatPainter_NoSplatTextureFound),
  347. GUILayout.Height(64));
  348. //TODO use texture-array version message
  349. }
  350. else
  351. {
  352. EditorGUILayout.LabelField(
  353. StringTable.Get(C.Info_TextureArrayPainter_NoSplatTextureFoundOnSelectedObject),
  354. GUILayout.Height(64));
  355. }
  356. }
  357. else
  358. {
  359. for (int i = 0; i < textureListCount; i += 4)
  360. {
  361. EditorGUILayout.BeginHorizontal();
  362. {
  363. var oldBgColor = GUI.backgroundColor;
  364. for (int j = 0; j < 4; j++)
  365. {
  366. if (i + j >= textureListCount) break;
  367. EditorGUILayout.BeginVertical();
  368. var texture = TextureList[i + j];
  369. bool toggleOn = SelectedTextureIndex == i + j;
  370. if (toggleOn)
  371. {
  372. GUI.backgroundColor = new Color(62 / 255.0f, 125 / 255.0f, 231 / 255.0f);
  373. }
  374. GUIContent toggleContent;
  375. if (i + j + 1 <= MaxHotkeyNumberForTexture)
  376. {
  377. toggleContent = new GUIContent(texture,
  378. StringTable.Get(C.Hotkey) + ':' + StringTable.Get(C.NumPad) + (i + j + 1));
  379. }
  380. else
  381. {
  382. toggleContent = new GUIContent(texture);
  383. }
  384. var new_toggleOn = GUILayout.Toggle(toggleOn,
  385. toggleContent, GUI.skin.button,
  386. GUILayout.Width(64), GUILayout.Height(64));
  387. GUI.backgroundColor = oldBgColor;
  388. if (new_toggleOn && !toggleOn)
  389. {
  390. SelectedTextureIndex = i + j;
  391. // reload the preview
  392. if (PainterMode == EditorFilterMode.SelectedGameObject)
  393. {
  394. preview.LoadPreviewFromObject(texture, BrushSizeInU3D, BrushIndex, targetGameObject);
  395. }
  396. else
  397. {
  398. preview.LoadPreview(texture, BrushSizeInU3D, BrushIndex);
  399. }
  400. }
  401. EditorGUILayout.EndVertical();
  402. }
  403. }
  404. EditorGUILayout.EndHorizontal();
  405. }
  406. }
  407. }
  408. EditorGUILayout.EndVertical();
  409. //Settings
  410. if (!Settings.CompactGUI)
  411. {
  412. EditorGUILayout.Space();
  413. GUILayout.Label(StringTable.Get(C.Settings), MTEStyles.SubHeader);
  414. }
  415. BrushSize = EditorGUILayoutEx.Slider(StringTable.Get(C.Size), "-", "+", BrushSize, MinBrushSize, MaxBrushSize);
  416. BrushFlow = EditorGUILayoutEx.SliderLog10(StringTable.Get(C.Flow), "[", "]", BrushFlow, MinBrushFlow, MaxBrushFlow);
  417. //Tools
  418. if (!Settings.CompactGUI)
  419. {
  420. EditorGUILayout.Space();
  421. GUILayout.Label(StringTable.Get(C.Tools), MTEStyles.SubHeader);
  422. }
  423. EditorGUILayout.BeginVertical();
  424. {
  425. EditorGUILayout.BeginHorizontal();
  426. {
  427. if (GUILayout.Button(StringTable.Get(C.CreateTextureArraySettings),
  428. GUILayout.Width(100), GUILayout.Height(40)))
  429. {
  430. EditorApplication.ExecuteMenuItem(
  431. $"Assets/Create/Mesh Terrain Editor/{nameof(TextureArraySettings)}");
  432. }
  433. GUILayout.Space(20);
  434. EditorGUILayout.LabelField(
  435. StringTable.Get(C.Info_ToolDescription_CreateTextureArraySettings),
  436. MTEStyles.labelFieldWordwrap);
  437. }
  438. EditorGUILayout.EndHorizontal();
  439. }
  440. EditorGUILayout.EndVertical();
  441. GUILayout.FlexibleSpace();
  442. EditorGUILayout.HelpBox(StringTable.Get(C.Info_WillBeSavedInstantly),
  443. MessageType.Info, true);
  444. }
  445. public HashSet<Hotkey> DefineHotkeys()
  446. {
  447. var hashSet = new HashSet<Hotkey>
  448. {
  449. new Hotkey(this, KeyCode.Minus, () =>
  450. {
  451. BrushFlow -= 0.01f;
  452. MTEEditorWindow.Instance.Repaint();
  453. }),
  454. new Hotkey(this, KeyCode.Equals, () =>
  455. {
  456. BrushFlow += 0.01f;
  457. MTEEditorWindow.Instance.Repaint();
  458. }),
  459. new Hotkey(this, KeyCode.LeftBracket, () =>
  460. {
  461. BrushSize -= 1;
  462. MTEEditorWindow.Instance.Repaint();
  463. }),
  464. new Hotkey(this, KeyCode.RightBracket, () =>
  465. {
  466. BrushSize += 1;
  467. MTEEditorWindow.Instance.Repaint();
  468. }),
  469. };
  470. for (int i = 0; i < MaxHotkeyNumberForTexture; i++)
  471. {
  472. int index = i;
  473. var hotkey = new Hotkey(this, KeyCode.Keypad0+index+1, () =>
  474. {
  475. SelectedTextureIndex = index;
  476. LoadPreview();
  477. });
  478. hashSet.Add(hotkey);
  479. }
  480. return hashSet;
  481. }
  482. // buffers of editing helpers
  483. private readonly List<TextureModifyGroup> modifyGroups = new List<TextureModifyGroup>(4);
  484. private float[] BrushStrength = new float[1024 * 1024];//buffer for brush blending to forbid re-allocate big array every frame when painting.
  485. private readonly List<Color[]> modifyingSections = new List<Color[]>(3);
  486. private UndoTransaction currentUndoTransaction;
  487. public void OnSceneGUI()
  488. {
  489. var e = Event.current;
  490. if (preview == null || !preview.IsReady || TextureList.Count == 0)
  491. {
  492. return;
  493. }
  494. if (e.commandName == "UndoRedoPerformed")
  495. {
  496. SceneView.RepaintAll();
  497. return;
  498. }
  499. if (!(EditorWindow.mouseOverWindow is SceneView))
  500. {
  501. return;
  502. }
  503. // do nothing when mouse middle/right button, control/alt key is pressed
  504. if (e.button != 0 || e.control || e.alt)
  505. return;
  506. HandleUtility.AddDefaultControl(0);
  507. var ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
  508. RaycastHit raycastHit;
  509. if (PainterMode == EditorFilterMode.SelectedGameObject)
  510. {
  511. if (!targetGameObject || !targetMaterial || !targetMesh)
  512. {
  513. return;
  514. }
  515. if (!Physics.Raycast(ray, out raycastHit, Mathf.Infinity, ~targetGameObject.layer))
  516. {
  517. return;
  518. }
  519. if (targetGameObject != raycastHit.transform.gameObject)
  520. {
  521. return;
  522. }
  523. var currentBrushSize = BrushSizeInU3D/2;
  524. if (Settings.ShowBrushRect)
  525. {
  526. Utility.ShowBrushRect(raycastHit.point, currentBrushSize);
  527. }
  528. var controlIndex = SelectedTextureIndex / 4;
  529. Debug.Assert(0 <= controlIndex && controlIndex <= 3);
  530. var controlTexture = controlTextures[controlIndex];
  531. if (controlTexture == null)
  532. {
  533. throw new MTEEditException("The control texture at index {controlIndex} is null.");
  534. }
  535. var controlWidth = controlTexture.width;
  536. var controlHeight = controlTexture.height;
  537. var meshSize = targetGameObject.GetComponent<MeshRenderer>().bounds.size.x;
  538. var brushSizeInTexel = (int) Mathf.Round(BrushSizeInU3D/meshSize*controlWidth);
  539. preview.SetNormalizedBrushSize(BrushSizeInU3D/meshSize);
  540. preview.SetNormalizedBrushCenter(raycastHit.textureCoord);
  541. preview.SetPreviewSize(BrushSizeInU3D/2);
  542. preview.MoveTo(raycastHit.point);
  543. SceneView.RepaintAll();
  544. if ((e.type == EventType.MouseDrag && e.alt == false && e.shift == false && e.button == 0) ||
  545. (e.type == EventType.MouseDown && e.shift == false && e.alt == false && e.button == 0))
  546. {
  547. // 1. Collect all sections to be modified
  548. var sections = new List<Color[]>();
  549. var pixelUV = raycastHit.textureCoord;
  550. var pX = Mathf.FloorToInt(pixelUV.x * controlWidth);
  551. var pY = Mathf.FloorToInt(pixelUV.y * controlHeight);
  552. var x = Mathf.Clamp(pX - brushSizeInTexel / 2, 0, controlWidth - 1);
  553. var y = Mathf.Clamp(pY - brushSizeInTexel / 2, 0, controlHeight - 1);
  554. var width = Mathf.Clamp((pX + brushSizeInTexel / 2), 0, controlWidth) - x;
  555. var height = Mathf.Clamp((pY + brushSizeInTexel / 2), 0, controlHeight) - y;
  556. for (var i = 0; i < controlTextures.Length; i++)
  557. {
  558. var texture = controlTextures[i];
  559. if (texture == null) continue;
  560. sections.Add(texture.GetPixels(x, y, width, height, 0));
  561. }
  562. // 2. Modify target
  563. var replaced = sections[controlIndex];
  564. var maskTexture = (Texture2D) MTEStyles.brushTextures[BrushIndex];
  565. BrushStrength = new float[brushSizeInTexel * brushSizeInTexel];
  566. for (var i = 0; i < brushSizeInTexel; i++)
  567. {
  568. for (var j = 0; j < brushSizeInTexel; j++)
  569. {
  570. BrushStrength[j * brushSizeInTexel + i] =
  571. maskTexture.GetPixelBilinear(((float) i) / brushSizeInTexel,
  572. ((float) j) / brushSizeInTexel).a;
  573. }
  574. }
  575. var controlColor = new Color();
  576. controlColor[SelectedTextureIndex % 4] = 1.0f;
  577. for (var i = 0; i < height; i++)
  578. {
  579. for (var j = 0; j < width; j++)
  580. {
  581. var index = (i * width) + j;
  582. var Stronger =
  583. BrushStrength[
  584. Mathf.Clamp((y + i) - (pY - brushSizeInTexel / 2), 0,
  585. brushSizeInTexel - 1) *
  586. brushSizeInTexel +
  587. Mathf.Clamp((x + j) - (pX - brushSizeInTexel / 2), 0,
  588. brushSizeInTexel - 1)] *
  589. BrushFlow;
  590. replaced[index] = Color.Lerp(replaced[index], controlColor, Stronger);
  591. }
  592. }
  593. if (e.type == EventType.MouseDown)
  594. {
  595. using (new UndoTransaction())
  596. {
  597. var material = targetMaterial;
  598. if (material.HasProperty(ControlTexturePropertyNames[0]))
  599. {
  600. Texture2D texture = (Texture2D) material.GetTexture(ControlTexturePropertyNames[0]);
  601. if (texture != null)
  602. {
  603. var originalColors = texture.GetPixels();
  604. UndoRedoManager.Instance().Push(a =>
  605. {
  606. texture.ModifyPixels(a);
  607. texture.Apply();
  608. Save(texture);
  609. }, originalColors, "Paint control texture");
  610. }
  611. }
  612. if (material.HasProperty(ControlTexturePropertyNames[1]))
  613. {
  614. Texture2D texture = (Texture2D) material.GetTexture(ControlTexturePropertyNames[1]);
  615. if (texture != null)
  616. {
  617. var originalColors = texture.GetPixels();
  618. UndoRedoManager.Instance().Push(a =>
  619. {
  620. texture.ModifyPixels(a);
  621. texture.Apply();
  622. Save(texture);
  623. }, originalColors, "Paint control texture");
  624. }
  625. }
  626. if (material.HasProperty(ControlTexturePropertyNames[2]))
  627. {
  628. Texture2D texture = (Texture2D) material.GetTexture(ControlTexturePropertyNames[2]);
  629. if (texture != null)
  630. {
  631. var originalColors = texture.GetPixels();
  632. UndoRedoManager.Instance().Push(a =>
  633. {
  634. texture.ModifyPixels(a);
  635. texture.Apply();
  636. Save(texture);
  637. }, originalColors, "Paint control texture");
  638. }
  639. }
  640. }
  641. }
  642. controlTexture.SetPixels(x, y, width, height, replaced);
  643. controlTexture.Apply();
  644. // 3. Normalize other control textures
  645. NormalizeWeightsLegacy(sections);
  646. for (var i = 0; i < controlTextures.Length; i++)
  647. {
  648. var texture = controlTextures[i];
  649. if (texture == null)
  650. {
  651. continue;
  652. }
  653. if (texture == controlTexture)
  654. {
  655. continue;
  656. }
  657. texture.SetPixels(x, y, width, height, sections[i]);
  658. texture.Apply();
  659. }
  660. }
  661. else if (e.type == EventType.MouseUp && e.alt == false && e.button == 0)
  662. {
  663. foreach (var texture in controlTextures)
  664. {
  665. if (texture)
  666. {
  667. Save(texture);
  668. }
  669. }
  670. }
  671. }
  672. else
  673. {
  674. if(Physics.Raycast(ray, out raycastHit,
  675. Mathf.Infinity,
  676. 1 << MTEContext.TargetLayer//only hit target layer
  677. ))
  678. {
  679. //check tag
  680. if (!raycastHit.transform.CompareTag(MTEContext.TargetTag))
  681. {
  682. return;
  683. }
  684. var currentBrushSize = BrushSizeInU3D;
  685. if (Settings.ShowBrushRect)
  686. {
  687. Utility.ShowBrushRect(raycastHit.point, currentBrushSize/2);
  688. }
  689. var hitPoint = raycastHit.point;
  690. preview.MoveTo(hitPoint);
  691. float meshSize = 1.0f;
  692. // collect modify group
  693. modifyGroups.Clear();
  694. foreach (var target in MTEContext.Targets)
  695. {
  696. //MTEDebug.Log("Check if we can paint on target.");
  697. var meshRenderer = target.GetComponent<MeshRenderer>();
  698. if (meshRenderer == null) continue;
  699. var meshFilter = target.GetComponent<MeshFilter>();
  700. if (meshFilter == null) continue;
  701. var mesh = meshFilter.sharedMesh;
  702. if (mesh == null) continue;
  703. Vector2 textureUVMin;//min texture uv that is to be modified
  704. Vector2 textureUVMax;//max texture uv that is to be modified
  705. Vector2 brushUVMin;//min brush mask uv that will be used
  706. Vector2 brushUVMax;//max brush mask uv that will be used
  707. {
  708. //MTEDebug.Log("Start: Check if they intersect with each other.");
  709. // check if the brush rect intersects with the `Mesh.bounds` of this target
  710. var hitPointLocal = target.transform.InverseTransformPoint(hitPoint);//convert hit point from world space to target mesh space
  711. Bounds brushBounds = new Bounds(center: new Vector3(hitPointLocal.x, 0, hitPointLocal.z), size: new Vector3(currentBrushSize, 99999, currentBrushSize));
  712. Bounds meshBounds = mesh.bounds;//TODO rename this
  713. Bounds paintingBounds;
  714. var intersected = meshBounds.Intersect(brushBounds, out paintingBounds);
  715. if(!intersected) continue;
  716. Vector2 paintingBounds2D_min = new Vector2(paintingBounds.min.x, paintingBounds.min.z);
  717. Vector2 paintingBounds2D_max = new Vector2(paintingBounds.max.x, paintingBounds.max.z);
  718. //calculate which part of control texture should be modified
  719. Vector2 meshRendererBounds2D_min = new Vector2(meshBounds.min.x, meshBounds.min.z);
  720. Vector2 meshRendererBounds2D_max = new Vector2(meshBounds.max.x, meshBounds.max.z);
  721. textureUVMin = MathEx.NormalizeTo01(rangeMin: meshRendererBounds2D_min, rangeMax: meshRendererBounds2D_max, value: paintingBounds2D_min);
  722. textureUVMax = MathEx.NormalizeTo01(rangeMin: meshRendererBounds2D_min, rangeMax: meshRendererBounds2D_max, value: paintingBounds2D_max);
  723. if (target.transform == raycastHit.transform)
  724. {
  725. meshSize = meshBounds.size.x;
  726. }
  727. //calculate which part of brush mask texture should be used
  728. Vector2 brushBounds2D_min = new Vector2(brushBounds.min.x, brushBounds.min.z);
  729. Vector2 brushBounds2D_max = new Vector2(brushBounds.max.x, brushBounds.max.z);
  730. brushUVMin = MathEx.NormalizeTo01(rangeMin: brushBounds2D_min, rangeMax: brushBounds2D_max, value: paintingBounds2D_min);
  731. brushUVMax = MathEx.NormalizeTo01(rangeMin: brushBounds2D_min, rangeMax: brushBounds2D_max, value: paintingBounds2D_max);
  732. if (Settings.DebugMode)
  733. {
  734. Handles.color = Color.blue;
  735. HandlesEx.DrawRectangle(paintingBounds2D_min, paintingBounds2D_max);
  736. Handles.color = new Color(255, 128, 166);
  737. HandlesEx.DrawRectangle(meshRendererBounds2D_min, meshRendererBounds2D_max);
  738. Handles.color = Color.green;
  739. HandlesEx.DrawRectangle(brushBounds2D_min, brushBounds2D_max);
  740. }
  741. //MTEDebug.Log("End: Check if they intersect with each other.");
  742. }
  743. if (e.button == 0 && (e.type == EventType.MouseDown || e.type == EventType.MouseDrag))
  744. {
  745. //MTEDebug.Log("Start handling mouse down.");
  746. // find the splat-texture in the material, get the X (splatIndex) from `_SplatX`
  747. var selectedTexture = TextureList[SelectedTextureIndex];
  748. var material = meshRenderer.sharedMaterial;
  749. if (material == null)
  750. {
  751. MTEDebug.LogError("Failed to find material on target GameObject's MeshRenderer. " +
  752. "The first material on the MeshRenderer should be editable by MTE.");
  753. return;
  754. }
  755. //MTEDebug.Log("Finding the selected texture in the material.");
  756. //Convention: for texture array, the selected texture index is the layer index
  757. if (!material.HasProperty(AlbedoArrayPropertyName))
  758. {
  759. //zwcloud/MeshTerrainEditor-issues#218
  760. var relativePath = AssetDatabase.GetAssetPath(material);
  761. MTEDebug.LogError(
  762. $"Material<{material.name}> at <{relativePath}> using shader <{material.shader.name}> doesn't have a texture property" +
  763. $" '{AlbedoArrayPropertyName}'. Please capture a screenshot of the material editor, and report this issue with it.");
  764. return;
  765. }
  766. Texture2DArray textureArray =
  767. material.GetTexture(AlbedoArrayPropertyName) as Texture2DArray;
  768. var layerIndex = TextureArrayManager.Instance.GetTextureSliceIndex(textureArray,
  769. selectedTexture);
  770. if (layerIndex < 0)
  771. {
  772. continue;
  773. }
  774. //MTEDebug.Log("get number of layer-textures in the material.");
  775. int splatTotal = GetLayerTextureNumber(material);
  776. //MTEDebug.Log("check control textures.");
  777. // fetch control textures from material, TODO refator to merge duplicate code below
  778. Texture2D controlTexture0 = null, controlTexture1 = null, controlTexture2 = null;
  779. if (splatTotal > 0)//controlTexture0 should exists
  780. {
  781. if (!material.HasProperty(ControlTexturePropertyNames[0]))
  782. {//impossible if using a builtin TextureArray shader
  783. throw new MTEEditException($"Property {ControlTexturePropertyNames[0]} " +
  784. $"doesn't exist in material<{material.name}>.");
  785. }
  786. var tex = material.GetTexture(ControlTexturePropertyNames[0]);
  787. if (tex != null)
  788. {
  789. controlTexture0 = (Texture2D)tex;
  790. }
  791. else
  792. {
  793. throw new MTEEditException($"{ControlTexturePropertyNames[0]} is" +
  794. $" not assigned or existing in material<{material.name}>.");
  795. }
  796. }
  797. if (splatTotal > 4)//controlTexture1 should exists
  798. {
  799. if (!material.HasProperty(ControlTexturePropertyNames[1]))
  800. {//impossible if using a builtin TextureArray shader
  801. throw new MTEEditException($"Property {ControlTexturePropertyNames[1]} " +
  802. $"doesn't exist in material<{material.name}>.");
  803. }
  804. var tex = material.GetTexture(ControlTexturePropertyNames[1]);
  805. if (tex == null)
  806. {
  807. throw new MTEEditException($"Property {ControlTexturePropertyNames[1]} " +
  808. $"is not assigned in material<{material.name}>.");
  809. }
  810. controlTexture1 = (Texture2D)tex;
  811. }
  812. if (splatTotal > 8)//controlTexture2 should exists
  813. {
  814. if (!material.HasProperty(ControlTexturePropertyNames[2]))
  815. {//impossible if using a builtin TextureArray shader
  816. throw new MTEEditException($"Property {ControlTexturePropertyNames[2]} " +
  817. $"doesn't exist in material<{material.name}>.");
  818. }
  819. var tex = material.GetTexture(ControlTexturePropertyNames[2]);
  820. if (tex == null)
  821. {
  822. throw new MTEEditException($"{ControlTexturePropertyNames[2]} " +
  823. $"is not assigned in material<{material.name}>.");
  824. }
  825. controlTexture2 = (Texture2D)tex;
  826. }
  827. // check which control texture is to be modified
  828. Texture2D controlTexture = controlTexture0;
  829. if (layerIndex >= 4)
  830. {
  831. controlTexture = controlTexture1;
  832. }
  833. if (layerIndex >= 8)
  834. {
  835. controlTexture = controlTexture2;
  836. }
  837. if (controlTexture1 != null)
  838. {
  839. if (controlTexture0.width != controlTexture1.width)
  840. {
  841. throw new MTEEditException(
  842. $"Size of {controlTexture0.name} is different from other control textures." +
  843. "Make sure all control textures is the same size.");
  844. }
  845. }
  846. if (controlTexture2 != null)
  847. {
  848. if (controlTexture0.width != controlTexture2.width)
  849. {
  850. throw new MTEEditException(
  851. $"Size of {controlTexture2.name} is different from other control textures." +
  852. "Make sure all control textures is the same size.");
  853. }
  854. }
  855. System.Diagnostics.Debug.Assert(controlTexture != null, "controlTexture != null");
  856. //get modifying texel rect of the control texture
  857. int x = (int)Mathf.Clamp(textureUVMin.x * (controlTexture.width - 1), 0, controlTexture.width - 1);
  858. int y = (int)Mathf.Clamp(textureUVMin.y * (controlTexture.height - 1), 0, controlTexture.height - 1);
  859. int width = Mathf.Clamp(Mathf.FloorToInt(textureUVMax.x * controlTexture.width) - x, 0, controlTexture.width - x);
  860. int height = Mathf.Clamp(Mathf.FloorToInt(textureUVMax.y * controlTexture.height) - y, 0, controlTexture.height - y);
  861. var texelRect = new Rect(x, y, width, height);
  862. modifyGroups.Add(new TextureModifyGroup(target, layerIndex, splatTotal,
  863. controlTexture0, controlTexture1, controlTexture2,
  864. texelRect, brushUVMin, brushUVMax));
  865. //MTEDebug.Log("End handling mouse down.");
  866. }
  867. }
  868. preview.SetNormalizedBrushSize(BrushSizeInU3D/meshSize);
  869. preview.SetNormalizedBrushCenter(raycastHit.textureCoord);
  870. //record undo operation for targets that to be modified
  871. if (e.button == 0 && e.type == EventType.MouseDown)
  872. {
  873. currentUndoTransaction = new UndoTransaction("Paint Texture");
  874. }
  875. if (currentUndoTransaction != null &&
  876. e.button == 0 && e.type== EventType.MouseDown)
  877. {
  878. //record values before modification for undo
  879. foreach (var modifyGroup in modifyGroups)
  880. {
  881. var gameObject = modifyGroup.gameObject;
  882. var material = gameObject.GetComponent<MeshRenderer>().sharedMaterial;
  883. if (material.HasProperty(ControlTexturePropertyNames[0]))
  884. {
  885. Texture2D texture = (Texture2D)material.GetTexture(ControlTexturePropertyNames[0]);
  886. if (texture != null)
  887. {
  888. var originalColors = texture.GetPixels();
  889. UndoRedoManager.Instance().Push(a =>
  890. {
  891. texture.ModifyPixels(a);
  892. texture.Apply();
  893. Save(texture);
  894. }, originalColors, "Paint control texture");
  895. }
  896. }
  897. if (material.HasProperty(ControlTexturePropertyNames[1]))
  898. {
  899. Texture2D texture = (Texture2D) material.GetTexture(ControlTexturePropertyNames[1]);
  900. if (texture != null)
  901. {
  902. var originalColors = texture.GetPixels();
  903. UndoRedoManager.Instance().Push(a =>
  904. {
  905. texture.ModifyPixels(a);
  906. texture.Apply();
  907. Save(texture);
  908. }, originalColors, "Paint control texture");
  909. }
  910. }
  911. if (material.HasProperty(ControlTexturePropertyNames[2]))
  912. {
  913. Texture2D texture = (Texture2D) material.GetTexture(ControlTexturePropertyNames[2]);
  914. if (texture != null)
  915. {
  916. var originalColors = texture.GetPixels();
  917. UndoRedoManager.Instance().Push(a =>
  918. {
  919. texture.ModifyPixels(a);
  920. texture.Apply();
  921. Save(texture);
  922. }, originalColors, "Paint control texture");
  923. }
  924. }
  925. }
  926. }
  927. if (e.button == 0 && e.type == EventType.MouseUp)
  928. {
  929. Debug.Assert(currentUndoTransaction != null);
  930. currentUndoTransaction.Dispose();
  931. }
  932. // execute the modification
  933. if (modifyGroups.Count != 0)
  934. {
  935. for (int i = 0; i < modifyGroups.Count; i++)
  936. {
  937. var modifyGroup = modifyGroups[i];
  938. var gameObject = modifyGroup.gameObject;
  939. var material = gameObject.GetComponent<MeshRenderer>().sharedMaterial;
  940. //set all control textures readable, just in case
  941. Utility.SetTextureReadable(material.GetTexture(ControlTexturePropertyNames[0]));
  942. if (material.HasProperty(ControlTexturePropertyNames[1]))
  943. {
  944. Utility.SetTextureReadable(material.GetTexture(ControlTexturePropertyNames[1]));
  945. }
  946. if (material.HasProperty(ControlTexturePropertyNames[2]))
  947. {
  948. Utility.SetTextureReadable(material.GetTexture(ControlTexturePropertyNames[2]));
  949. }
  950. PaintTexture(modifyGroup.controlTexture0,
  951. modifyGroup.controlTexture1,
  952. modifyGroup.controlTexture2,
  953. modifyGroup.splatIndex,
  954. modifyGroup.splatTotal,
  955. modifyGroup.texelRect,
  956. modifyGroup.minUV, modifyGroup.maxUV);
  957. }
  958. }
  959. // auto save when mouse up
  960. if (e.type == EventType.MouseUp && e.button == 0)
  961. {
  962. foreach (var texture2D in DirtyTextureSet)
  963. {
  964. Save(texture2D);
  965. }
  966. DirtyTextureSet.Clear();
  967. }
  968. }
  969. }
  970. SceneView.RepaintAll();
  971. }
  972. private void PaintTexture(Texture2D controlTexture0, Texture2D controlTexture1,
  973. Texture2D controlTexture2, int splatIndex, int splatTotal, Rect texelRect,
  974. Vector2 minUV, Vector2 maxUV)
  975. {
  976. // check parameters
  977. if (splatTotal > 0 && controlTexture0 == null)
  978. {
  979. throw new System.ArgumentException(
  980. $"[MTE] {nameof(controlTexture0)} is null.",
  981. nameof(controlTexture0));
  982. }
  983. if (splatTotal > 4 && controlTexture1 == null)
  984. {
  985. throw new System.ArgumentException(
  986. $"[MTE] splatIndex is 4/5/6/7 but {nameof(controlTexture1)} is null.",
  987. nameof(controlTexture1));
  988. }
  989. if (splatTotal > 8 && controlTexture2 == null)
  990. {
  991. throw new System.ArgumentException(
  992. $"[MTE] splatIndex is 8/9/10/11 but {nameof(controlTexture2)} is null.",
  993. nameof(controlTexture2));
  994. }
  995. if (splatIndex < 0 || splatIndex > 11)
  996. {
  997. throw new System.ArgumentOutOfRangeException(nameof(splatIndex), splatIndex,
  998. "[MTE] splatIndex should be [0, 11].");
  999. }
  1000. // collect the pixel sections to modify
  1001. modifyingSections.Clear();
  1002. int x = (int)texelRect.x;
  1003. int y = (int)texelRect.y;
  1004. int width = (int)texelRect.width;
  1005. int height = (int)texelRect.height;
  1006. modifyingSections.Add(controlTexture0.GetPixels(x, y, width, height, 0));
  1007. if (controlTexture1 != null)
  1008. {
  1009. modifyingSections.Add(controlTexture1.GetPixels(x, y, width, height, 0));
  1010. }
  1011. else
  1012. {
  1013. modifyingSections.Add(Array.Empty<Color>());
  1014. }
  1015. if (controlTexture2 != null)
  1016. {
  1017. modifyingSections.Add(controlTexture2.GetPixels(x, y, width, height, 0));
  1018. }
  1019. else
  1020. {
  1021. modifyingSections.Add(Array.Empty<Color>());
  1022. }
  1023. // sample brush strength from the mask texture
  1024. var maskTexture = (Texture2D) MTEStyles.brushTextures[BrushIndex];
  1025. if (BrushStrength.Length < width*height)//enlarge buffer if it is not big enough
  1026. {
  1027. BrushStrength = new float[width * height];
  1028. }
  1029. var unitUV_u = (maxUV.x - minUV.x)/(width-1);
  1030. if (width == 1)
  1031. {
  1032. unitUV_u = maxUV.x - minUV.x;
  1033. }
  1034. var unitUV_v = (maxUV.y - minUV.y)/(height-1);
  1035. if (height == 1)
  1036. {
  1037. unitUV_v = maxUV.y - minUV.y;
  1038. }
  1039. for (var i = 0; i < height; i++)
  1040. {
  1041. float v = minUV.y + i * unitUV_v;
  1042. for (var j = 0; j < width; j++)
  1043. {
  1044. var pixelIndex = i * width + j;
  1045. float u = minUV.x + j * unitUV_u;
  1046. BrushStrength[pixelIndex] = maskTexture.GetPixelBilinear(u, v).a;
  1047. }
  1048. }
  1049. // blend the pixel section
  1050. Utility.BlendPixelSections(BrushFlow, BrushStrength, modifyingSections,
  1051. splatIndex, splatTotal, height, width);
  1052. // modify the control texture
  1053. if (splatTotal > 8)
  1054. {
  1055. controlTexture0.SetPixels(x, y, width, height, modifyingSections[0]);
  1056. controlTexture0.Apply();
  1057. System.Diagnostics.Debug.Assert(controlTexture1 != null);
  1058. System.Diagnostics.Debug.Assert(modifyingSections[1].Length != 0);
  1059. controlTexture1.SetPixels(x, y, width, height, modifyingSections[1]);
  1060. controlTexture1.Apply();
  1061. System.Diagnostics.Debug.Assert(controlTexture2 != null);
  1062. System.Diagnostics.Debug.Assert(modifyingSections[2].Length != 0);
  1063. controlTexture2.SetPixels(x, y, width, height, modifyingSections[2]);
  1064. controlTexture2.Apply();
  1065. DirtyTextureSet.Add(controlTexture0);
  1066. DirtyTextureSet.Add(controlTexture1);
  1067. DirtyTextureSet.Add(controlTexture2);
  1068. }
  1069. else if(splatTotal > 4)
  1070. {
  1071. controlTexture0.SetPixels(x, y, width, height, modifyingSections[0]);
  1072. controlTexture0.Apply();
  1073. System.Diagnostics.Debug.Assert(controlTexture1 != null);
  1074. System.Diagnostics.Debug.Assert(modifyingSections[1].Length != 0);
  1075. controlTexture1.SetPixels(x, y, width, height, modifyingSections[1]);
  1076. controlTexture1.Apply();
  1077. DirtyTextureSet.Add(controlTexture0);
  1078. DirtyTextureSet.Add(controlTexture1);
  1079. }
  1080. else
  1081. {
  1082. controlTexture0.SetPixels(x, y, width, height, modifyingSections[0]);
  1083. controlTexture0.Apply();
  1084. DirtyTextureSet.Add(controlTexture0);
  1085. }
  1086. }
  1087. private void NormalizeWeightsLegacy(List<Color[]> sections)
  1088. {
  1089. var colorCount = sections[0].Length;
  1090. for (var i = 0; i < colorCount; i++)
  1091. {
  1092. var total = 0f;
  1093. for (var j = 0; j < sections.Count; j++)
  1094. {
  1095. var color = sections[j][i];
  1096. total += color[0] + color[1] + color[2] + color[3];
  1097. if(j == SelectedTextureIndex/4)
  1098. {
  1099. total -= color[SelectedTextureIndex%4];
  1100. }
  1101. }
  1102. if(total > 0.01)
  1103. {
  1104. var a = sections[SelectedTextureIndex/4][i][SelectedTextureIndex%4];
  1105. var k = (1 - a)/total;
  1106. for (var j = 0; j < sections.Count; j++)
  1107. {
  1108. for (var l = 0; l < 4; l++)
  1109. {
  1110. if(!(j == SelectedTextureIndex/4 && l == SelectedTextureIndex%4))
  1111. {
  1112. sections[j][i][l] *= k;
  1113. }
  1114. }
  1115. }
  1116. }
  1117. else
  1118. {
  1119. for (var j = 0; j < sections.Count; j++)
  1120. {
  1121. sections[j][i][SelectedTextureIndex%4] = (j != SelectedTextureIndex/4) ? 0 : 1;
  1122. }
  1123. }
  1124. }
  1125. }
  1126. public static readonly HashSet<Texture2D> DirtyTextureSet = new HashSet<Texture2D>();
  1127. private static void Save(Texture2D texture)
  1128. {
  1129. if(texture == null)
  1130. {
  1131. throw new System.ArgumentNullException("texture");
  1132. }
  1133. var path = AssetDatabase.GetAssetPath(texture);
  1134. var bytes = texture.EncodeToPNG();
  1135. if(bytes == null || bytes.Length == 0)
  1136. {
  1137. throw new System.Exception("[MTE] Failed to save texture to png file.");
  1138. }
  1139. File.WriteAllBytes(path, bytes);
  1140. MTEDebug.LogFormat("Texture<{0}> saved to <{1}>.", texture.name, path);
  1141. }
  1142. private Preview preview = new Preview(isArray: true);
  1143. //Don't modify this field, it's used by MTE editors internally
  1144. public List<Texture> TextureList = new List<Texture>(16);
  1145. /// <summary>
  1146. /// load all splat textures form targets
  1147. /// </summary>
  1148. private void LoadTextureList()
  1149. {
  1150. TextureList.Clear();
  1151. if (painterMode == EditorFilterMode.SelectedGameObject)
  1152. {
  1153. MTEDebug.Log("Loading layer textures on selected GameObject...");
  1154. LoadTargetTextures(targetGameObject);
  1155. }
  1156. else
  1157. {
  1158. MTEDebug.Log("Loading layer textures on target GameObject(s)...");
  1159. foreach (var target in MTEContext.Targets)
  1160. {
  1161. LoadTargetTextures(target);
  1162. }
  1163. }
  1164. // make collected splat textures readable
  1165. Utility.SetTextureReadable(TextureList, true);
  1166. MTEDebug.LogFormat("{0} layer textures loaded.", TextureList.Count);
  1167. }
  1168. private void LoadTargetTextures(GameObject target)
  1169. {
  1170. if (!target)
  1171. {
  1172. return;
  1173. }
  1174. var meshRenderer = target.GetComponent<MeshRenderer>();
  1175. if (meshRenderer == null)
  1176. {
  1177. return;
  1178. }
  1179. var material = meshRenderer.sharedMaterial;
  1180. if (!material)
  1181. {
  1182. return;
  1183. }
  1184. if (!CheckIfMaterialAssetPathAvailable(material))
  1185. {
  1186. return;
  1187. }
  1188. Shader shader = material.shader;
  1189. if (shader == null)
  1190. {
  1191. MTEDebug.LogWarning($"Material<{material.name}> doesn't use a valid shader!");
  1192. return;
  1193. }
  1194. if (!MTEShaders.IsMTETextureArrayShader(shader))
  1195. {
  1196. MTEDebug.LogWarning(
  1197. $"Material<{material.name}> doesn't use a MTE TextureArray shader!");
  1198. return;
  1199. }
  1200. RuntimeTextureArrayLoader runtimeTextureArrayLoader =
  1201. meshRenderer.GetComponent<RuntimeTextureArrayLoader>();
  1202. TextureArraySettings settings = null;
  1203. if (runtimeTextureArrayLoader)
  1204. {
  1205. //create and assign texture array to material
  1206. runtimeTextureArrayLoader.LoadInEditor();
  1207. settings = runtimeTextureArrayLoader.settings;
  1208. }
  1209. var textureArray = material.GetTexture(AlbedoArrayPropertyName) as Texture2DArray;
  1210. if (textureArray == null || textureArray.depth <= 0)
  1211. {
  1212. MTEDebug.LogWarning(
  1213. $"Material<{material.name}>'s {AlbedoArrayPropertyName} property is empty.");
  1214. return;
  1215. }
  1216. TextureArrayManager.Instance.AddOrUpdate(textureArray, settings);
  1217. TextureArrayManager.Instance.GetTextures(textureArray, out var textures);
  1218. var propertyCount = ShaderUtil.GetPropertyCount(shader);
  1219. for (int j = 0; j < propertyCount; j++)
  1220. {
  1221. if (ShaderUtil.GetPropertyType(shader, j) != ShaderUtil.ShaderPropertyType.TexEnv)
  1222. {
  1223. continue;
  1224. }
  1225. var propertyName = ShaderUtil.GetPropertyName(shader, j);
  1226. if (propertyName != AlbedoArrayPropertyName)
  1227. {
  1228. continue;
  1229. }
  1230. foreach (var texture in textures)
  1231. {
  1232. if (!TextureList.Contains(texture))
  1233. {
  1234. TextureList.Add(texture);
  1235. }
  1236. }
  1237. }
  1238. }
  1239. private void LoadControlTextures()
  1240. {
  1241. if (!targetMaterial)
  1242. {
  1243. return;
  1244. }
  1245. int splatTotal = GetLayerTextureNumber(targetMaterial);
  1246. int weightMapTotal = splatTotal / 4;
  1247. Debug.Assert(weightMapTotal <= ControlTexturePropertyNames.Length);
  1248. int width = -1, height = -1;
  1249. for (int weightMapIndex = 0; weightMapIndex < weightMapTotal; weightMapIndex++)
  1250. {
  1251. var controlPropertyName = ControlTexturePropertyNames[weightMapIndex];
  1252. var controlTexture = targetMaterial.GetTexture(controlPropertyName) as Texture2D;
  1253. if (controlTexture == null )
  1254. {
  1255. throw new MTEEditException($"Property {controlPropertyName} isn't assigned " +
  1256. $"or doesn't exist in material<{targetMaterial.name}>.");
  1257. }
  1258. var controlTextureWidth = controlTexture.width;
  1259. var controlTextureHeight = controlTexture.height;
  1260. if (controlTextureWidth != controlTextureHeight)
  1261. {
  1262. throw new MTEEditException($"{controlPropertyName} texture is not square.");
  1263. }
  1264. if (width < 0 && height < 0)
  1265. {
  1266. width = controlTextureWidth;
  1267. height = controlTextureHeight;
  1268. }
  1269. if (width != controlTextureWidth || height != controlTextureHeight)
  1270. {
  1271. throw new MTEEditException(
  1272. $"Size of {controlPropertyName} is different from others.");
  1273. }
  1274. controlTextures[weightMapIndex] = controlTexture;
  1275. }
  1276. }
  1277. private static bool CheckIfMaterialAssetPathAvailable(Material material)
  1278. {
  1279. var relativePathOfMaterial = AssetDatabase.GetAssetPath(material);
  1280. if (relativePathOfMaterial.StartsWith("Resources"))
  1281. {//built-in material
  1282. return false;
  1283. }
  1284. return true;
  1285. }
  1286. private static int GetLayerTextureNumber(Material material)
  1287. {
  1288. if (material.IsKeywordEnabled(KeyWords.HasWeightMap2))
  1289. {
  1290. return 12;
  1291. }
  1292. if (material.IsKeywordEnabled(KeyWords.HasWeightMap1))
  1293. {
  1294. return 8;
  1295. }
  1296. return 4;
  1297. }
  1298. }
  1299. }