Preview.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. using UnityEditor;
  2. using UnityEngine;
  3. namespace MTE
  4. {
  5. internal class Preview
  6. {
  7. public bool IsReady = false;
  8. private bool IsArray = false;
  9. private Shader shader;
  10. private RenderPipeline renderPipeline = RenderPipeline.NotDetermined;
  11. private GameObject previewObj;
  12. private Texture2D UpDirectionNormalTexture;
  13. public Preview(bool isArray)
  14. {
  15. this.IsArray = isArray;
  16. }
  17. /// <summary>
  18. /// Load preview from target GameObjects
  19. /// </summary>
  20. public void LoadPreview(Texture texture, float brushSizeInU3D, int brushIndex)
  21. {
  22. LoadShader();
  23. if (IsArray)
  24. {
  25. Material material = null;
  26. foreach (var target in MTEContext.Targets)
  27. {
  28. material = FindMaterialInObject(target, texture, out _);
  29. if(material)
  30. {
  31. break;
  32. }
  33. }
  34. if (material == null)
  35. {
  36. throw new MTEEditException(
  37. "Failed to load texture in to preview: " +
  38. "selected texture isn't a source texture slice of any texture array used in any targets' material. " +
  39. "Please refresh the filter to reload the texture list.\n\n" +
  40. $"Note: MTE finds source texture slice via the {nameof(TextureArraySettings)} asset next to the texture array being used.");
  41. }
  42. UnLoadPreview();
  43. CreatePreviewObject();
  44. previewObj.hideFlags = HideFlags.HideAndDontSave;
  45. var textureScale = GetPreviewSplatTextureScale(material, brushIndex);
  46. //TODO consider normal roughness and AO
  47. SetPreviewTexture(textureScale, texture, null);
  48. SetPreviewSize(brushSizeInU3D / 2);
  49. SetPreviewMaskTexture(brushIndex);
  50. }
  51. else
  52. {
  53. int splatIndex = -1;
  54. Material material = null;
  55. foreach (var target in MTEContext.Targets)
  56. {
  57. material = FindMaterialInObject(target, texture, out _);
  58. if (material)
  59. {
  60. break;
  61. }
  62. }
  63. if (material == null)
  64. {
  65. throw new MTEEditException(
  66. "Failed to load texture in to preview: " +
  67. "selected texture isn't used in any GameObject's material. " +
  68. "Please refresh the filter to reload the texture list.");
  69. }
  70. UnLoadPreview();
  71. CreatePreviewObject();
  72. previewObj.hideFlags = HideFlags.HideAndDontSave;
  73. var textureScale = GetPreviewSplatTextureScale(material, splatIndex);
  74. Texture normalTexture = null;
  75. if (material.HasProperty("_Normal" + splatIndex))
  76. {
  77. normalTexture = material.GetTexture("_Normal" + splatIndex);
  78. }
  79. SetPreviewTexture(textureScale, texture, normalTexture);
  80. SetPreviewSize(brushSizeInU3D / 2);
  81. SetPreviewMaskTexture(brushIndex);
  82. }
  83. IsReady = true;
  84. }
  85. /// <summary>
  86. /// Load preview from a GameObject
  87. /// </summary>
  88. public void LoadPreviewFromObject(Texture texture, float brushSizeInU3D,
  89. int brushIndex, GameObject obj)
  90. {
  91. if (obj == null)
  92. {
  93. MTEDebug.LogError("Cannot load preview from an invalid GameObject.");
  94. return;
  95. }
  96. LoadShader();
  97. if (IsArray)
  98. {
  99. Material material = null;
  100. var target = obj;
  101. material = FindMaterialInObject(target, texture, out _);
  102. if (material == null)
  103. {
  104. throw new MTEEditException(
  105. "Failed to load texture in to preview: " +
  106. "selected texture isn't a source texture slice of any texture array used in any targets' material. " +
  107. "Please refresh the filter to reload the texture list.\n\n" +
  108. $"Note: MTE finds source texture slice via the {nameof(TextureArraySettings)} asset next to the texture array being used.");
  109. }
  110. UnLoadPreview();
  111. CreatePreviewObject();
  112. previewObj.hideFlags = HideFlags.HideAndDontSave;
  113. var textureScale = GetPreviewSplatTextureScale(material, brushIndex);
  114. //TODO consider normal roughness and AO
  115. SetPreviewTexture(textureScale, texture, null);
  116. SetPreviewSize(brushSizeInU3D / 2);
  117. SetPreviewMaskTexture(brushIndex);
  118. }
  119. else
  120. {
  121. int splatIndex = -1;
  122. Material material = null;
  123. var target = obj;
  124. material = FindMaterialInObject(target, texture, out splatIndex);
  125. if (material == null)
  126. {
  127. throw new MTEEditException(
  128. "Failed to load texture in to preview: " +
  129. "selected texture isn't used in any GameObject's material. " +
  130. "Please refresh the filter to reload the texture list.");
  131. }
  132. UnLoadPreview();
  133. CreatePreviewObject();
  134. previewObj.hideFlags = HideFlags.HideAndDontSave;
  135. var textureScale = GetPreviewSplatTextureScale(material, splatIndex);
  136. Texture normalTexture = null;
  137. if (material.HasProperty("_Normal" + splatIndex))
  138. {
  139. normalTexture = material.GetTexture("_Normal" + splatIndex);
  140. }
  141. SetPreviewTexture(textureScale, texture, normalTexture);
  142. SetPreviewSize(brushSizeInU3D / 2);
  143. SetPreviewMaskTexture(brushIndex);
  144. }
  145. IsReady = true;
  146. }
  147. /// <summary>
  148. /// Destory the preview.
  149. /// </summary>
  150. public void UnLoadPreview()
  151. {
  152. if (previewObj != null)
  153. {
  154. UnityEngine.Object.DestroyImmediate(previewObj);
  155. previewObj = null;
  156. }
  157. IsReady = false;
  158. }
  159. public void SetPreviewTexture(Vector2 textureScale,
  160. Texture texture, Texture normalTexture)
  161. {
  162. if(!previewObj)
  163. {
  164. return;
  165. }
  166. if(renderPipeline != RenderPipeline.Builtin)
  167. {
  168. var renderer = previewObj.GetComponent<MeshRenderer>();
  169. renderer.sharedMaterial.SetTexture("_MainTex", texture);
  170. renderer.sharedMaterial.SetTextureScale("_MainTex", textureScale);
  171. if (!normalTexture)
  172. {//Default "bump" direction is (0, 0, 1), encoded as(0.5, 0.5, 1.0);
  173. // not expected up-direction (0, 1, 0), encoded as(0.5, 1.0, 0.5).
  174. //So a default up-direction normal texture is created here lazily to replaced the "bump".
  175. if (!UpDirectionNormalTexture)
  176. {
  177. UpDirectionNormalTexture = new Texture2D(1, 1, TextureFormat.RGB24, false);
  178. UpDirectionNormalTexture.SetPixel(0, 0, new Color(0.5f, 1.0f, 0.5f));
  179. UpDirectionNormalTexture.Apply();
  180. }
  181. normalTexture = UpDirectionNormalTexture;
  182. }
  183. renderer.sharedMaterial.SetTexture("_NormalTex", normalTexture);
  184. }
  185. else
  186. {
  187. var projector = previewObj.GetComponent<Projector>();
  188. projector.material.SetTexture("_MainTex", texture);
  189. projector.material.SetTextureScale("_MainTex", textureScale);
  190. projector.material.SetTexture("_NormalTex", normalTexture);
  191. }
  192. SceneView.RepaintAll();
  193. }
  194. public void SetPreviewMaskTexture(int maskIndex)
  195. {
  196. if(!previewObj)
  197. {
  198. return;
  199. }
  200. if (renderPipeline == RenderPipeline.Builtin)
  201. {
  202. var projector = previewObj.GetComponent<Projector>();
  203. projector.material.SetTexture("_MaskTex", MTEStyles.brushTextures[maskIndex]);
  204. projector.material.SetTextureScale("_MaskTex", Vector2.one);
  205. }
  206. else
  207. {
  208. var renderer = previewObj.GetComponent<MeshRenderer>();
  209. renderer.sharedMaterial.SetTexture("_MaskTex", MTEStyles.brushTextures[maskIndex]);
  210. renderer.sharedMaterial.SetTextureScale("_MaskTex", Vector2.one);
  211. }
  212. SceneView.RepaintAll();
  213. }
  214. public void SetPreviewSize(float value)
  215. {
  216. if(!previewObj)
  217. {
  218. return;
  219. }
  220. if (renderPipeline == RenderPipeline.Builtin)
  221. {
  222. var projector = previewObj.GetComponent<Projector>();
  223. projector.orthographicSize = value;
  224. }
  225. else
  226. {
  227. var halfBrushSizeInUnityUnit = value;
  228. previewObj.transform.localScale = new Vector3(
  229. halfBrushSizeInUnityUnit*2,
  230. halfBrushSizeInUnityUnit*2, 10000);
  231. }
  232. SceneView.RepaintAll();
  233. }
  234. public void MoveTo(Vector3 worldPosition)
  235. {
  236. if(!previewObj)
  237. {
  238. return;
  239. }
  240. previewObj.transform.position = worldPosition;
  241. }
  242. public void SetNormalizedBrushCenter(Vector2 normalizedBrushCenter)
  243. {
  244. if (renderPipeline != RenderPipeline.Builtin)
  245. {
  246. var renderer = previewObj.GetComponent<MeshRenderer>();
  247. renderer.sharedMaterial.SetVector("_BrushCenter", normalizedBrushCenter);
  248. }
  249. else
  250. {
  251. //nothing
  252. }
  253. }
  254. public void SetNormalizedBrushSize(float normalizeBrushSize)
  255. {
  256. if (renderPipeline != RenderPipeline.Builtin)
  257. {
  258. var renderer = previewObj.GetComponent<MeshRenderer>();
  259. renderer.sharedMaterial.SetFloat("_NormalizedBrushSize", normalizeBrushSize);
  260. }
  261. else
  262. {
  263. //nothing
  264. }
  265. }
  266. private void LoadShader()
  267. {
  268. if (shader != null && RenderPipelineUtil.Current == renderPipeline)
  269. {
  270. return;
  271. }
  272. renderPipeline = RenderPipelineUtil.Current;
  273. var urpShaderRelativePath = Utility.GetUnityPath(Res.ShaderDir + "PaintTexturePreview_URP.shader");
  274. switch (RenderPipelineUtil.Current)
  275. {
  276. case RenderPipeline.Builtin:
  277. shader = Shader.Find("Hidden/MTE/PaintTexturePreview");
  278. break;
  279. case RenderPipeline.URP:
  280. this.shader = AssetDatabase.LoadAssetAtPath<Shader>(urpShaderRelativePath);
  281. if (shader == null)
  282. {
  283. MTEDebug.LogError("MTE Preview shader for URP is not found.");
  284. }
  285. else
  286. {
  287. MTEDebug.Log("Loaded Preview shader for URP.");
  288. }
  289. break;
  290. //fallback to URP
  291. case RenderPipeline.HDRP://HDRP is not supported yet.
  292. default:
  293. this.shader = AssetDatabase.LoadAssetAtPath<Shader>(urpShaderRelativePath);
  294. if (shader == null)
  295. {
  296. MTEDebug.LogError("MTE Preview shader for URP (fallback) is not found.");
  297. }
  298. else
  299. {
  300. MTEDebug.Log("Loaded Preview shader for URP (fallback).");
  301. }
  302. break;
  303. }
  304. }
  305. private void CreatePreviewObject()
  306. {
  307. if (renderPipeline != RenderPipeline.Builtin)
  308. {
  309. previewObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
  310. var boxCollider = previewObj.GetComponent<BoxCollider>();
  311. Object.DestroyImmediate(boxCollider);
  312. previewObj.name = "MTEPreview";
  313. var meshRenderer = previewObj.GetComponent<MeshRenderer>();
  314. var material = new Material(shader);
  315. meshRenderer.sharedMaterial = material;
  316. previewObj.transform.eulerAngles = new Vector3(90, 0, 0);
  317. }
  318. else
  319. {
  320. previewObj = new GameObject("MTEPreview");
  321. var projector = previewObj.AddComponent<Projector>();
  322. projector.material = new Material(shader);
  323. projector.orthographic = true;
  324. projector.nearClipPlane = -1000;
  325. projector.farClipPlane = 1000;
  326. projector.transform.Rotate(90, 0, 0);
  327. }
  328. }
  329. private Material FindMaterialInObject(GameObject obj, Texture texture, out int layerIndexBuiltinOnly)
  330. {
  331. layerIndexBuiltinOnly = -1;
  332. if (IsArray)
  333. {
  334. var meshRenderer = obj.GetComponent<MeshRenderer>();
  335. if (meshRenderer == null)
  336. {
  337. return null;
  338. }
  339. var m = meshRenderer.sharedMaterial;
  340. if (m == null)
  341. {
  342. return null;
  343. }
  344. var runtimeTextureArrayLoader = meshRenderer.GetComponent<RuntimeTextureArrayLoader>();
  345. if (runtimeTextureArrayLoader)
  346. {
  347. runtimeTextureArrayLoader.LoadInEditor();
  348. var settings = runtimeTextureArrayLoader.settings;
  349. var texturePropertyValue = m.GetTexture(
  350. TextureArrayShaderPropertyNames.AlbedoArrayPropertyName);
  351. if (!texturePropertyValue)
  352. {
  353. return null;
  354. }
  355. var textureArray = texturePropertyValue as Texture2DArray;
  356. if (textureArray == null)
  357. {
  358. return null;
  359. }
  360. TextureArrayManager.Instance.AddOrUpdate(textureArray, settings);
  361. }
  362. else
  363. {
  364. var texturePropertyValue = m.GetTexture(
  365. TextureArrayShaderPropertyNames.AlbedoArrayPropertyName);
  366. if (!texturePropertyValue)
  367. {
  368. return null;
  369. }
  370. var textureArray = texturePropertyValue as Texture2DArray;
  371. if (textureArray == null)
  372. {
  373. return null;
  374. }
  375. if (!TextureArrayManager.Instance.IsCached(textureArray))
  376. {
  377. return null;
  378. }
  379. if (TextureArrayManager.Instance.GetTextureSliceIndex(textureArray, texture) < 0)
  380. {
  381. return null;
  382. }
  383. }
  384. return m;
  385. }
  386. else
  387. {
  388. var meshRenderer = obj.GetComponent<MeshRenderer>();
  389. if (meshRenderer == null)
  390. {
  391. return null;
  392. }
  393. var m = meshRenderer.sharedMaterial;
  394. if (m == null)
  395. {
  396. return null;
  397. }
  398. layerIndexBuiltinOnly = m.FindSplatTexture(texture);
  399. if (layerIndexBuiltinOnly < 0)
  400. {
  401. return null;
  402. }
  403. return m;
  404. }
  405. }
  406. private Vector2 GetPreviewSplatTextureScale(Material material, int splatIndex)
  407. {
  408. if(IsArray)
  409. {
  410. //We use unique uv scale offset for all layers in array shaders.
  411. //So splatIndex is only valid for non-array shaders.
  412. var UVScaleOffset = TextureArrayShaderPropertyNames.UVScaleOffsetPropertyName;
  413. if (material.HasProperty(UVScaleOffset))
  414. {
  415. var scaleOffset = material.GetVector(UVScaleOffset);
  416. return new Vector2(scaleOffset.x, scaleOffset.y);
  417. }
  418. MTEDebug.LogWarning($"No {UVScaleOffset} property found in texture array shader " +
  419. material.shader.name);
  420. return new Vector2(15, 15);
  421. }
  422. else
  423. {
  424. var splatXName = "_Splat" + splatIndex;
  425. if (material.HasProperty(splatXName))
  426. {
  427. return material.GetTextureScale(splatXName);
  428. }
  429. if (0 <= splatIndex && splatIndex <= 3)
  430. {
  431. var packedSplatName = "_PackedSplat0";
  432. if (material.HasProperty(packedSplatName))
  433. {
  434. return material.GetTextureScale(packedSplatName);
  435. }
  436. }
  437. else if (4 <= splatIndex && splatIndex <= 7)
  438. {
  439. var packedSplatName = "_PackedSplat3";
  440. if (material.HasProperty(packedSplatName))
  441. {
  442. return material.GetTextureScale(packedSplatName);
  443. }
  444. }
  445. return new Vector2(10, 10);
  446. }
  447. }
  448. }
  449. }