MaterialInspector.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System.Collections.Generic;
  4. using BansheeEngine;
  5. namespace BansheeEditor
  6. {
  7. /** @addtogroup Inspectors
  8. * @{
  9. */
  10. /// <summary>
  11. /// Renders an inspector for the <see cref="Material"/> resource.
  12. /// </summary>
  13. [CustomInspector(typeof (Material))]
  14. internal class MaterialInspector : Inspector
  15. {
  16. private MaterialParamGUI[] guiParams;
  17. private GUIResourceField shaderField;
  18. private GUIEnumField builtinShaderField;
  19. /// <inheritdoc/>
  20. protected internal override void Initialize()
  21. {
  22. Material material = InspectedObject as Material;
  23. if (material == null)
  24. return;
  25. Shader activeShader = material.Shader;
  26. BuiltinShader builtinType = ShaderToBuiltin(activeShader);
  27. builtinShaderField = new GUIEnumField(typeof(BuiltinShader), new LocEdString("Shader"));
  28. builtinShaderField.Value = (ulong) builtinType;
  29. builtinShaderField.OnSelectionChanged += x =>
  30. {
  31. BuiltinShader newBuiltinType = (BuiltinShader) x;
  32. material.Shader = Builtin.GetShader(newBuiltinType);
  33. EditorApplication.SetDirty(material);
  34. RebuildParamGUI(material);
  35. bool newIsCustom = newBuiltinType == BuiltinShader.Custom;
  36. shaderField.Active = newIsCustom;
  37. };
  38. shaderField = new GUIResourceField(typeof(Shader), new LocEdString("Shader file"));
  39. shaderField.Value = material.Shader;
  40. shaderField.OnChanged += (x) =>
  41. {
  42. Shader shader = Resources.Load<Shader>(x);
  43. material.Shader = shader;
  44. EditorApplication.SetDirty(material);
  45. RebuildParamGUI(material);
  46. };
  47. bool isCustom = builtinType == BuiltinShader.Custom;
  48. shaderField.Active = isCustom;
  49. Layout.AddElement(builtinShaderField);
  50. Layout.AddElement(shaderField);
  51. RebuildParamGUI(material);
  52. }
  53. /// <inheritdoc/>
  54. protected internal override InspectableState Refresh()
  55. {
  56. Material material = InspectedObject as Material;
  57. if (material == null)
  58. return InspectableState.NotModified;
  59. if (material.Shader != shaderField.Value)
  60. {
  61. shaderField.Value = material.Shader;
  62. RebuildParamGUI(material);
  63. }
  64. if (guiParams != null)
  65. {
  66. foreach (var param in guiParams)
  67. param.Refresh(material);
  68. }
  69. return InspectableState.NotModified;
  70. }
  71. /// <summary>
  72. /// Recreates GUI elements for all material parameters.
  73. /// </summary>
  74. /// <param name="material">Material to create parameters for</param>
  75. private void RebuildParamGUI(Material material)
  76. {
  77. if (guiParams != null)
  78. {
  79. foreach (var param in guiParams)
  80. param.Destroy();
  81. guiParams = null;
  82. }
  83. if (material != null && material.Shader != null)
  84. guiParams = CreateMaterialGUI(material, Layout);
  85. }
  86. /// <summary>
  87. /// Converts a shader resource into a builtin shader.
  88. /// </summary>
  89. /// <param name="shader">Shader resource to convert.</param>
  90. /// <returns>Type of builtin shader, if any.</returns>
  91. private BuiltinShader ShaderToBuiltin(Shader shader)
  92. {
  93. // Note: Need a better way to detect the builtin shader perhaps (store it in Material?)
  94. Shader standardShader = Builtin.GetShader(BuiltinShader.Standard);
  95. if(standardShader == shader)
  96. return BuiltinShader.Standard;
  97. return BuiltinShader.Custom;
  98. }
  99. /// <summary>
  100. /// Creates a set of objects in which each object represents a GUI for a material parameter.
  101. /// </summary>
  102. /// <param name="mat">Material for whose parameters to create GUI for.</param>
  103. /// <param name="layout">Layout to add the parameter GUI elements to.</param>
  104. /// <returns>A material parameter GUI object for each supported material parameter.</returns>
  105. static internal MaterialParamGUI[] CreateMaterialGUI(Material mat, GUILayout layout)
  106. {
  107. Shader shader = mat.Shader;
  108. if (shader == null)
  109. return new MaterialParamGUI[0];
  110. List<MaterialParamGUI> guiParams = new List<MaterialParamGUI>();
  111. ShaderParameter[] shaderParams = shader.Parameters;
  112. foreach (var param in shaderParams)
  113. {
  114. if (param.Internal)
  115. continue;
  116. switch (param.Type)
  117. {
  118. case ShaderParameterType.Float:
  119. layout.AddSpace(5);
  120. guiParams.Add(new MaterialParamFloatGUI(param, mat, layout));
  121. break;
  122. case ShaderParameterType.Vector2:
  123. layout.AddSpace(5);
  124. guiParams.Add(new MaterialParamVec2GUI(param, mat, layout));
  125. break;
  126. case ShaderParameterType.Vector3:
  127. layout.AddSpace(5);
  128. guiParams.Add(new MaterialParamVec3GUI(param, mat, layout));
  129. break;
  130. case ShaderParameterType.Vector4:
  131. layout.AddSpace(5);
  132. guiParams.Add(new MaterialParamVec4GUI(param, mat, layout));
  133. break;
  134. case ShaderParameterType.Matrix3:
  135. layout.AddSpace(5);
  136. guiParams.Add(new MaterialParamMat3GUI(param, mat, layout));
  137. break;
  138. case ShaderParameterType.Matrix4:
  139. layout.AddSpace(5);
  140. guiParams.Add(new MaterialParamMat4GUI(param, mat, layout));
  141. break;
  142. case ShaderParameterType.Color:
  143. layout.AddSpace(5);
  144. guiParams.Add(new MaterialParamColorGUI(param, mat, layout));
  145. break;
  146. case ShaderParameterType.Texture2D:
  147. case ShaderParameterType.Texture3D:
  148. case ShaderParameterType.TextureCube:
  149. layout.AddSpace(5);
  150. guiParams.Add(new MaterialParamTextureGUI(param, mat, layout));
  151. break;
  152. }
  153. }
  154. return guiParams.ToArray();
  155. }
  156. }
  157. /// <summary>
  158. /// Contains GUI element(s) for a single parameter in a <see cref="Material"/>.
  159. /// </summary>
  160. internal abstract class MaterialParamGUI
  161. {
  162. protected ShaderParameter shaderParam;
  163. /// <summary>
  164. /// Creates a new material parameter GUI.
  165. /// </summary>
  166. /// <param name="shaderParam">Shader parameter to create the GUI for.</param>
  167. protected MaterialParamGUI(ShaderParameter shaderParam)
  168. {
  169. this.shaderParam = shaderParam;
  170. }
  171. /// <summary>
  172. /// Checks if the data stored in GUI and in the material matches, and updates the GUI if it doesn't.
  173. /// </summary>
  174. /// <param name="material">Material whose data to check.</param>
  175. internal abstract void Refresh(Material material);
  176. /// <summary>
  177. /// Destroys the internal GUI elements.
  178. /// </summary>
  179. internal abstract void Destroy();
  180. }
  181. /// <summary>
  182. /// Contains GUI element(s) for a single floating point parameter in a <see cref="Material"/>.
  183. /// </summary>
  184. internal class MaterialParamFloatGUI : MaterialParamGUI
  185. {
  186. private GUIFloatField guiElem;
  187. /// <summary>
  188. /// Creates a new material parameter GUI.
  189. /// </summary>
  190. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of floating point type.</param>
  191. /// <param name="material">Material the parameter is a part of.</param>
  192. /// <param name="layout">Layout to append the GUI elements to.</param>
  193. internal MaterialParamFloatGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  194. : base(shaderParam)
  195. {
  196. LocString title = new LocEdString(shaderParam.Name);
  197. guiElem = new GUIFloatField(title);
  198. guiElem.OnChanged += (x) =>
  199. {
  200. material.SetFloat(shaderParam.Name, x);
  201. EditorApplication.SetDirty(material);
  202. };
  203. layout.AddElement(guiElem);
  204. }
  205. /// <inheritdoc/>
  206. internal override void Refresh(Material material)
  207. {
  208. guiElem.Value = material.GetFloat(shaderParam.Name);
  209. }
  210. /// <inheritdoc/>
  211. internal override void Destroy()
  212. {
  213. guiElem.Destroy();
  214. }
  215. }
  216. /// <summary>
  217. /// Contains GUI element(s) for a single 2D vector parameter in a <see cref="Material"/>.
  218. /// </summary>
  219. internal class MaterialParamVec2GUI : MaterialParamGUI
  220. {
  221. private GUIVector2Field guiElem;
  222. /// <summary>
  223. /// Creates a new material parameter GUI.
  224. /// </summary>
  225. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of 2D vector type.</param>
  226. /// <param name="material">Material the parameter is a part of.</param>
  227. /// <param name="layout">Layout to append the GUI elements to.</param>
  228. internal MaterialParamVec2GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  229. : base(shaderParam)
  230. {
  231. LocString title = new LocEdString(shaderParam.Name);
  232. guiElem = new GUIVector2Field(title);
  233. guiElem.OnChanged += (x) =>
  234. {
  235. material.SetVector2(shaderParam.Name, x);
  236. EditorApplication.SetDirty(material);
  237. };
  238. layout.AddElement(guiElem);
  239. }
  240. /// <inheritdoc/>
  241. internal override void Refresh(Material material)
  242. {
  243. guiElem.Value = material.GetVector2(shaderParam.Name);
  244. }
  245. /// <inheritdoc/>
  246. internal override void Destroy()
  247. {
  248. guiElem.Destroy();
  249. }
  250. }
  251. /// <summary>
  252. /// Contains GUI element(s) for a single 3D vector parameter in a <see cref="Material"/>.
  253. /// </summary>
  254. internal class MaterialParamVec3GUI : MaterialParamGUI
  255. {
  256. private GUIVector3Field guiElem;
  257. /// <summary>
  258. /// Creates a new material parameter GUI.
  259. /// </summary>
  260. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of 3D vector type.</param>
  261. /// <param name="material">Material the parameter is a part of.</param>
  262. /// <param name="layout">Layout to append the GUI elements to.</param>
  263. internal MaterialParamVec3GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  264. : base(shaderParam)
  265. {
  266. LocString title = new LocEdString(shaderParam.Name);
  267. guiElem = new GUIVector3Field(title);
  268. guiElem.OnChanged += (x) =>
  269. {
  270. material.SetVector3(shaderParam.Name, x);
  271. EditorApplication.SetDirty(material);
  272. };
  273. layout.AddElement(guiElem);
  274. }
  275. /// <inheritdoc/>
  276. internal override void Refresh(Material material)
  277. {
  278. guiElem.Value = material.GetVector3(shaderParam.Name);
  279. }
  280. /// <inheritdoc/>
  281. internal override void Destroy()
  282. {
  283. guiElem.Destroy();
  284. }
  285. }
  286. /// <summary>
  287. /// Contains GUI element(s) for a single 4D vector parameter in a <see cref="Material"/>.
  288. /// </summary>
  289. internal class MaterialParamVec4GUI : MaterialParamGUI
  290. {
  291. private GUIVector4Field guiElem;
  292. /// <summary>
  293. /// Creates a new material parameter GUI.
  294. /// </summary>
  295. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of 4D vector type.</param>
  296. /// <param name="material">Material the parameter is a part of.</param>
  297. /// <param name="layout">Layout to append the GUI elements to.</param>
  298. internal MaterialParamVec4GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  299. : base(shaderParam)
  300. {
  301. LocString title = new LocEdString(shaderParam.Name);
  302. guiElem = new GUIVector4Field(title);
  303. guiElem.OnChanged += (x) =>
  304. {
  305. material.SetVector4(shaderParam.Name, x);
  306. EditorApplication.SetDirty(material);
  307. };
  308. layout.AddElement(guiElem);
  309. }
  310. /// <inheritdoc/>
  311. internal override void Refresh(Material material)
  312. {
  313. guiElem.Value = material.GetVector4(shaderParam.Name);
  314. }
  315. /// <inheritdoc/>
  316. internal override void Destroy()
  317. {
  318. guiElem.Destroy();
  319. }
  320. }
  321. /// <summary>
  322. /// Contains GUI element(s) for a single 3x3 matrix parameter in a <see cref="Material"/>.
  323. /// </summary>
  324. internal class MaterialParamMat3GUI : MaterialParamGUI
  325. {
  326. private const int MAT_SIZE = 3;
  327. private GUILayout mainLayout;
  328. private GUIFloatField[] guiMatFields = new GUIFloatField[MAT_SIZE * MAT_SIZE];
  329. /// <summary>
  330. /// Creates a new material parameter GUI.
  331. /// </summary>
  332. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of 3x3 matrix type.</param>
  333. /// <param name="material">Material the parameter is a part of.</param>
  334. /// <param name="layout">Layout to append the GUI elements to.</param>
  335. internal MaterialParamMat3GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  336. : base(shaderParam)
  337. {
  338. LocString title = new LocEdString(shaderParam.Name);
  339. GUILabel guiTitle = new GUILabel(title, GUIOption.FixedWidth(100));
  340. mainLayout = layout.AddLayoutY();
  341. GUILayoutX titleLayout = mainLayout.AddLayoutX();
  342. titleLayout.AddElement(guiTitle);
  343. titleLayout.AddFlexibleSpace();
  344. GUILayoutY contentLayout = mainLayout.AddLayoutY();
  345. GUILayoutX[] rows = new GUILayoutX[MAT_SIZE];
  346. for (int i = 0; i < rows.Length; i++)
  347. rows[i] = contentLayout.AddLayoutX();
  348. for (int row = 0; row < MAT_SIZE; row++)
  349. {
  350. for (int col = 0; col < MAT_SIZE; col++)
  351. {
  352. int index = row * MAT_SIZE + col;
  353. guiMatFields[index] = new GUIFloatField(row + "," + col, 20, "", GUIOption.FixedWidth(80));
  354. GUIFloatField field = guiMatFields[index];
  355. rows[row].AddElement(field);
  356. rows[row].AddSpace(5);
  357. int hoistedRow = row;
  358. int hoistedCol = col;
  359. field.OnChanged += (x) =>
  360. {
  361. Matrix3 value = material.GetMatrix3(shaderParam.Name);
  362. value[hoistedRow, hoistedCol] = x;
  363. material.SetMatrix3(shaderParam.Name, value);
  364. EditorApplication.SetDirty(material);
  365. };
  366. }
  367. }
  368. }
  369. /// <inheritdoc/>
  370. internal override void Refresh(Material material)
  371. {
  372. Matrix3 value = material.GetMatrix3(shaderParam.Name);
  373. for (int row = 0; row < MAT_SIZE; row++)
  374. {
  375. for (int col = 0; col < MAT_SIZE; col++)
  376. {
  377. int index = row * MAT_SIZE + col;
  378. guiMatFields[index].Value = value[row, col];
  379. }
  380. }
  381. }
  382. /// <inheritdoc/>
  383. internal override void Destroy()
  384. {
  385. mainLayout.Destroy();
  386. }
  387. }
  388. /// <summary>
  389. /// Contains GUI element(s) for a single 4x4 matrix parameter in a <see cref="Material"/>.
  390. /// </summary>
  391. internal class MaterialParamMat4GUI : MaterialParamGUI
  392. {
  393. private const int MAT_SIZE = 4;
  394. private GUILayout mainLayout;
  395. private GUIFloatField[] guiMatFields = new GUIFloatField[MAT_SIZE * MAT_SIZE];
  396. /// <summary>
  397. /// Creates a new material parameter GUI.
  398. /// </summary>
  399. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of 4x4 matrix type.</param>
  400. /// <param name="material">Material the parameter is a part of.</param>
  401. /// <param name="layout">Layout to append the GUI elements to.</param>
  402. internal MaterialParamMat4GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  403. : base(shaderParam)
  404. {
  405. LocString title = new LocEdString(shaderParam.Name);
  406. GUILabel guiTitle = new GUILabel(title, GUIOption.FixedWidth(100));
  407. mainLayout = layout.AddLayoutY();
  408. GUILayoutX titleLayout = mainLayout.AddLayoutX();
  409. titleLayout.AddElement(guiTitle);
  410. titleLayout.AddFlexibleSpace();
  411. GUILayoutY contentLayout = mainLayout.AddLayoutY();
  412. GUILayoutX[] rows = new GUILayoutX[MAT_SIZE];
  413. for (int i = 0; i < rows.Length; i++)
  414. rows[i] = contentLayout.AddLayoutX();
  415. for (int row = 0; row < MAT_SIZE; row++)
  416. {
  417. for (int col = 0; col < MAT_SIZE; col++)
  418. {
  419. int index = row * MAT_SIZE + col;
  420. guiMatFields[index] = new GUIFloatField(row + "," + col, 20, "", GUIOption.FixedWidth(80));
  421. GUIFloatField field = guiMatFields[index];
  422. rows[row].AddElement(field);
  423. rows[row].AddSpace(5);
  424. int hoistedRow = row;
  425. int hoistedCol = col;
  426. field.OnChanged += (x) =>
  427. {
  428. Matrix4 value = material.GetMatrix4(shaderParam.Name);
  429. value[hoistedRow, hoistedCol] = x;
  430. material.SetMatrix4(shaderParam.Name, value);
  431. EditorApplication.SetDirty(material);
  432. };
  433. }
  434. }
  435. }
  436. /// <inheritdoc/>
  437. internal override void Refresh(Material material)
  438. {
  439. Matrix4 value = material.GetMatrix4(shaderParam.Name);
  440. for (int row = 0; row < MAT_SIZE; row++)
  441. {
  442. for (int col = 0; col < MAT_SIZE; col++)
  443. {
  444. int index = row * MAT_SIZE + col;
  445. guiMatFields[index].Value = value[row, col];
  446. }
  447. }
  448. }
  449. /// <inheritdoc/>
  450. internal override void Destroy()
  451. {
  452. mainLayout.Destroy();
  453. }
  454. }
  455. /// <summary>
  456. /// Contains GUI element(s) for a single color parameter in a <see cref="Material"/>.
  457. /// </summary>
  458. internal class MaterialParamColorGUI : MaterialParamGUI
  459. {
  460. private GUIColorField guiElem;
  461. /// <summary>
  462. /// Creates a new material parameter GUI.
  463. /// </summary>
  464. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of color type.</param>
  465. /// <param name="material">Material the parameter is a part of.</param>
  466. /// <param name="layout">Layout to append the GUI elements to.</param>
  467. internal MaterialParamColorGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  468. : base(shaderParam)
  469. {
  470. LocString title = new LocEdString(shaderParam.Name);
  471. guiElem = new GUIColorField(title);
  472. guiElem.OnChanged += (x) =>
  473. {
  474. material.SetColor(shaderParam.Name, x);
  475. EditorApplication.SetDirty(material);
  476. };
  477. layout.AddElement(guiElem);
  478. }
  479. /// <inheritdoc/>
  480. internal override void Refresh(Material material)
  481. {
  482. guiElem.Value = material.GetColor(shaderParam.Name);
  483. }
  484. /// <inheritdoc/>
  485. internal override void Destroy()
  486. {
  487. guiElem.Destroy();
  488. }
  489. }
  490. /// <summary>
  491. /// Contains GUI element(s) for a single texture parameter in a <see cref="Material"/>.
  492. /// </summary>
  493. internal class MaterialParamTextureGUI : MaterialParamGUI
  494. {
  495. private GUITextureField guiElem;
  496. /// <summary>
  497. /// Creates a new material parameter GUI.
  498. /// </summary>
  499. /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of texture type.</param>
  500. /// <param name="material">Material the parameter is a part of.</param>
  501. /// <param name="layout">Layout to append the GUI elements to.</param>
  502. internal MaterialParamTextureGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
  503. : base(shaderParam)
  504. {
  505. LocString title = new LocEdString(shaderParam.Name);
  506. guiElem = new GUITextureField(title);
  507. switch (shaderParam.Type)
  508. {
  509. case ShaderParameterType.Texture2D:
  510. guiElem.OnChanged += (x) =>
  511. {
  512. Texture2D texture = Resources.Load<Texture2D>(x);
  513. material.SetTexture2D(shaderParam.Name, texture);
  514. EditorApplication.SetDirty(material);
  515. };
  516. break;
  517. case ShaderParameterType.Texture3D:
  518. guiElem.OnChanged += (x) =>
  519. {
  520. Texture3D texture = Resources.Load<Texture3D>(x);
  521. material.SetTexture3D(shaderParam.Name, texture);
  522. EditorApplication.SetDirty(material);
  523. };
  524. break;
  525. case ShaderParameterType.TextureCube:
  526. guiElem.OnChanged += (x) =>
  527. {
  528. TextureCube texture = Resources.Load<TextureCube>(x);
  529. material.SetTextureCube(shaderParam.Name, texture);
  530. EditorApplication.SetDirty(material);
  531. };
  532. break;
  533. }
  534. layout.AddElement(guiElem);
  535. }
  536. /// <inheritdoc/>
  537. internal override void Refresh(Material material)
  538. {
  539. Texture value = null;
  540. switch (shaderParam.Type)
  541. {
  542. case ShaderParameterType.Texture2D:
  543. value = material.GetTexture2D(shaderParam.Name);
  544. break;
  545. case ShaderParameterType.Texture3D:
  546. value = material.GetTexture3D(shaderParam.Name);
  547. break;
  548. case ShaderParameterType.TextureCube:
  549. value = material.GetTextureCube(shaderParam.Name);
  550. break;
  551. }
  552. guiElem.Value = value;
  553. }
  554. /// <inheritdoc/>
  555. internal override void Destroy()
  556. {
  557. guiElem.Destroy();
  558. }
  559. }
  560. /** @} */
  561. }