material.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. 'use strict';
  2. const { updateElementReadonly, updateElementInvalid } = require('../../utils/assets');
  3. const { join, dirname } = require('path');
  4. exports.template = /* html */`
  5. <div class="container">
  6. <ui-prop>
  7. <ui-label slot="label" value="i18n:ENGINE.assets.fbx.GlTFUserData.dumpMaterials.name" tooltip="i18n:ENGINE.assets.fbx.GlTFUserData.dumpMaterials.title"></ui-label>
  8. <ui-checkbox slot="content" class="dumpMaterials-checkbox"></ui-checkbox>
  9. </ui-prop>
  10. <ui-prop class="materialDumpDir-prop">
  11. <ui-label slot="label" value="i18n:ENGINE.assets.fbx.GlTFUserData.materialDumpDir.name" tooltip="i18n:ENGINE.assets.fbx.GlTFUserData.materialDumpDir.title"></ui-label>
  12. <ui-file slot="content" class="materialDumpDir-file" type="directory"></ui-file>
  13. </ui-prop>
  14. <ui-prop>
  15. <ui-label slot="label" value="i18n:ENGINE.assets.fbx.GlTFUserData.useVertexColors.name" tooltip="i18n:ENGINE.assets.fbx.GlTFUserData.useVertexColors.title"></ui-label>
  16. <ui-checkbox slot="content" class="useVertexColors-checkbox"></ui-checkbox>
  17. </ui-prop>
  18. <ui-prop>
  19. <ui-label slot="label" value="i18n:ENGINE.assets.fbx.GlTFUserData.depthWriteInAlphaModeBlend.name" tooltip="i18n:ENGINE.assets.fbx.GlTFUserData.depthWriteInAlphaModeBlend.title"></ui-label>
  20. <ui-checkbox slot="content" class="depthWriteInAlphaModeBlend-checkbox"></ui-checkbox>
  21. </ui-prop>
  22. <div class="images"></div>
  23. </div>
  24. `;
  25. exports.style = /* css */`
  26. ui-prop { margin-right: 4px; }
  27. ui-section.config { margin-right: 0; }
  28. .images {
  29. margin: 4px 0;
  30. }
  31. .images > ui-section > .sub-item {
  32. padding-top: 10px;
  33. padding-left: 10px;
  34. }
  35. .images > ui-section > .sub-item ui-prop {
  36. --left-width: calc(35% - 10px);
  37. }
  38. .images > ui-section > .sub-item ui-asset {
  39. width: 100%;
  40. }
  41. .images > ui-section > .sub-item .img {
  42. max-width: 100px;
  43. max-height: 100px;
  44. min-width: 14px;
  45. }
  46. .images > ui-section > .sub-item .image {
  47. margin-top: 5px;
  48. max-height: 400px;
  49. }
  50. `;
  51. exports.$ = {
  52. container: '.container',
  53. dumpMaterialsCheckbox: '.dumpMaterials-checkbox',
  54. materialDumpDirProp: '.materialDumpDir-prop',
  55. materialDumpDirFile: '.materialDumpDir-file',
  56. useVertexColorsCheckbox: '.useVertexColors-checkbox',
  57. depthWriteInAlphaModeBlendCheckbox: '.depthWriteInAlphaModeBlend-checkbox',
  58. images: '.images',
  59. };
  60. const Elements = {
  61. dumpMaterials: {
  62. ready() {
  63. const panel = this;
  64. panel.$.dumpMaterialsCheckbox.addEventListener('change', (event) => {
  65. panel.setProp('dumpMaterials', event);
  66. Elements.materialDumpDir.update.bind(panel)();
  67. });
  68. panel.$.dumpMaterialsCheckbox.addEventListener('confirm', () => {
  69. panel.dispatch('snapshot');
  70. });
  71. },
  72. update() {
  73. const panel = this;
  74. panel.$.dumpMaterialsCheckbox.value = panel.getDefault(panel.meta.userData.dumpMaterials, false);
  75. updateElementInvalid.call(panel, panel.$.dumpMaterialsCheckbox, 'dumpMaterials');
  76. updateElementReadonly.call(panel, panel.$.dumpMaterialsCheckbox);
  77. },
  78. },
  79. materialDumpDir: {
  80. ready() {
  81. const panel = this;
  82. panel.$.materialDumpDirFile.addEventListener('change', async (event) => {
  83. const value = event.target.value;
  84. panel.materialDumpDir = value;
  85. // Restricted values are in the assets directory
  86. const projectAssets = join(Editor.Project.path, 'assets');
  87. if (!Editor.Utils.Path.contains(projectAssets, value)) {
  88. await Editor.Dialog.warn(Editor.I18n.t('ENGINE.assets.fbx.limitMaterialDumpDir'), {
  89. title: Editor.I18n.t('scene.messages.warning'),
  90. });
  91. panel.materialDumpDir = panel.materialDumpDirDefault;
  92. Elements.materialDumpDir.update.bind(panel)();
  93. return;
  94. }
  95. const dir = await Editor.Message.request('asset-db', 'query-url', panel.materialDumpDir);
  96. panel.metaList.forEach((meta) => {
  97. meta.userData.materialDumpDir = dir;
  98. });
  99. panel.dispatch('change');
  100. });
  101. },
  102. async update() {
  103. const panel = this;
  104. panel.$.materialDumpDirProp.style.display = panel.$.dumpMaterialsCheckbox.value ? 'block' : 'none';
  105. if (!panel.$.dumpMaterialsCheckbox.value) {
  106. return;
  107. }
  108. // Same resource in a parent folder
  109. const filePath = (await Editor.Message.request('asset-db', 'query-path', panel.asset.path)) || '';
  110. panel.materialDumpDirDefault = dirname(filePath);
  111. // Same resource in a parent folder
  112. let materialDumpDir = panel.materialDumpDirDefault;
  113. if (panel.meta.userData.materialDumpDir) {
  114. const dataDir = await Editor.Message.request('asset-db', 'query-path', panel.meta.userData.materialDumpDir);
  115. if (dataDir) {
  116. materialDumpDir = dataDir;
  117. }
  118. }
  119. panel.$.materialDumpDirFile.value = materialDumpDir;
  120. updateElementInvalid.call(panel, panel.$.materialDumpDirFile, 'materialDumpDir');
  121. updateElementReadonly.call(panel, panel.$.materialDumpDirFile);
  122. },
  123. },
  124. useVertexColors: {
  125. ready() {
  126. const panel = this;
  127. panel.$.useVertexColorsCheckbox.addEventListener('change', panel.setProp.bind(panel, 'useVertexColors'));
  128. panel.$.useVertexColorsCheckbox.addEventListener('confirm', () => {
  129. panel.dispatch('snapshot');
  130. });
  131. },
  132. update() {
  133. const panel = this;
  134. panel.$.useVertexColorsCheckbox.value = panel.getDefault(panel.meta.userData.useVertexColors, false);
  135. updateElementInvalid.call(panel, panel.$.useVertexColorsCheckbox, 'useVertexColors');
  136. updateElementReadonly.call(panel, panel.$.useVertexColorsCheckbox);
  137. },
  138. },
  139. depthWriteInAlphaModeBlend: {
  140. ready() {
  141. const panel = this;
  142. panel.$.depthWriteInAlphaModeBlendCheckbox.addEventListener('change', panel.setProp.bind(panel, 'depthWriteInAlphaModeBlend'));
  143. panel.$.depthWriteInAlphaModeBlendCheckbox.addEventListener('confirm', () => {
  144. panel.dispatch('snapshot');
  145. });
  146. },
  147. update() {
  148. const panel = this;
  149. panel.$.depthWriteInAlphaModeBlendCheckbox.value = panel.getDefault(panel.meta.userData.depthWriteInAlphaModeBlend, false);
  150. updateElementInvalid.call(panel, panel.$.depthWriteInAlphaModeBlendCheckbox, 'depthWriteInAlphaModeBlend');
  151. updateElementReadonly.call(panel, panel.$.depthWriteInAlphaModeBlendCheckbox);
  152. },
  153. },
  154. images: {
  155. async update() {
  156. const panel = this;
  157. panel.$.images.innerText = '';
  158. if (panel.assetList.length !== 1) {
  159. return;
  160. }
  161. if (!panel.meta || !panel.meta.userData || !panel.meta.userData.imageMetas) {
  162. return;
  163. }
  164. const images = [];
  165. const subNames = Object.keys(panel.meta.userData.imageMetas);
  166. for (let index = 0; index < subNames.length; index++) {
  167. const name = subNames[index];
  168. const item = panel.meta.userData.imageMetas[name];
  169. const imageUuid = item.uri;
  170. // Processing image display
  171. let src = '';
  172. // may be base64 images.
  173. if (item.uri.startsWith('data:')) {
  174. src = item.uri;
  175. } else {
  176. // Handling the case of uuid
  177. const imageAsset = await Editor.Message.request('asset-db', 'query-asset-info', imageUuid);
  178. if (imageAsset && imageAsset.library) {
  179. const key = Object.keys(imageAsset.library).find((key) => key !== '.json');
  180. if (key) {
  181. src = imageAsset.library[key];
  182. }
  183. }
  184. }
  185. let remapUuid = '';
  186. // URL format
  187. if (item.remap) {
  188. remapUuid = await Editor.Message.request('asset-db', 'query-uuid', item.remap);
  189. }
  190. images.push({
  191. name: item.name || name,
  192. src,
  193. imageUuid,
  194. remapUuid,
  195. remap: item.remap,
  196. index,
  197. });
  198. }
  199. // Generate dom
  200. images.forEach((image, index) => {
  201. const section = document.createElement('ui-section');
  202. section.setAttribute('expand', '');
  203. panel.$.images.appendChild(section);
  204. const header = document.createElement('div');
  205. header.setAttribute('slot', 'header');
  206. header.innerText = image.name;
  207. section.appendChild(header);
  208. const content = document.createElement('div');
  209. content.setAttribute('class', 'sub-item');
  210. section.appendChild(content);
  211. const remapAsProp = document.createElement('ui-prop');
  212. content.appendChild(remapAsProp);
  213. const remapAsPropLabel = document.createElement('ui-label');
  214. remapAsPropLabel.setAttribute('slot', 'label');
  215. remapAsPropLabel.setAttribute('value', 'i18n:ENGINE.assets.fbx.ImageRemap.remapAs');
  216. remapAsProp.appendChild(remapAsPropLabel);
  217. const remapAsPropContent = document.createElement('div');
  218. remapAsPropContent.setAttribute('slot', 'content');
  219. remapAsProp.appendChild(remapAsPropContent);
  220. const remapAsPropContentAsset = document.createElement('ui-asset');
  221. remapAsPropContentAsset.setAttribute('droppable', 'cc.ImageAsset');
  222. remapAsPropContent.appendChild(remapAsPropContentAsset);
  223. remapAsPropContentAsset.addEventListener('confirm', async (event) => {
  224. const uuid = event.target.value;
  225. let url = '';
  226. if (uuid) {
  227. const asset = await Editor.Message.request('asset-db', 'query-asset-info', uuid);
  228. if (!asset) {
  229. return;
  230. }
  231. url = asset.source;
  232. }
  233. image.remapUuid = uuid;
  234. image.remap = url;
  235. panel.meta.userData.imageMetas[index].remap = uuid;
  236. const remapAsPropContentImg = remapAsPropContent.querySelector('.image');
  237. if (remapAsPropContentImg) {
  238. remapAsPropContentImg.setAttribute('value', image.remap);
  239. } else {
  240. panel.appendImage(remapAsPropContent, image.remap);
  241. }
  242. this.dispatch('change');
  243. this.dispatch('snapshot');
  244. });
  245. if (image.remap) {
  246. remapAsPropContentAsset.setAttribute('value', image.remap);
  247. panel.appendImage(remapAsPropContent, image.remap);
  248. }
  249. //
  250. const originalProp = document.createElement('ui-prop');
  251. content.appendChild(originalProp);
  252. const originalPropLabel = document.createElement('ui-label');
  253. originalPropLabel.setAttribute('slot', 'label');
  254. originalPropLabel.setAttribute('value', 'i18n:ENGINE.assets.fbx.ImageRemap.original');
  255. originalProp.appendChild(originalPropLabel);
  256. const originalPropContent = document.createElement('div');
  257. originalPropContent.setAttribute('slot', 'content');
  258. originalProp.appendChild(originalPropContent);
  259. if (image.src) {
  260. const originalPropContentImg = document.createElement('img');
  261. originalPropContentImg.setAttribute('src', image.src);
  262. originalPropContentImg.setAttribute('title', image.imageUuid);
  263. originalPropContentImg.setAttribute('class', 'img');
  264. originalPropContent.appendChild(originalPropContentImg);
  265. } else {
  266. originalPropContent.innerText = `<span>${image.imageUuid}</span>`;
  267. }
  268. });
  269. },
  270. },
  271. };
  272. exports.methods = {
  273. setProp(prop, event) {
  274. this.metaList.forEach((meta) => {
  275. meta.userData[prop] = event.target.value;
  276. });
  277. this.dispatch('change');
  278. this.dispatch('track', { tab: 'material', prop, value: event.target.value });
  279. },
  280. getDefault(value, def, prop) {
  281. if (value === undefined) {
  282. return def;
  283. }
  284. if (prop) {
  285. value = value[prop];
  286. }
  287. if (value === undefined) {
  288. return def;
  289. }
  290. return value;
  291. },
  292. appendImage(parent, value) {
  293. const image = document.createElement('ui-image');
  294. image.setAttribute('class', 'image');
  295. image.setAttribute('value', value);
  296. image.setAttribute('slot', 'content');
  297. parent.appendChild(image);
  298. },
  299. };
  300. exports.ready = function() {
  301. for (const prop in Elements) {
  302. const element = Elements[prop];
  303. if (element.ready) {
  304. element.ready.call(this);
  305. }
  306. }
  307. };
  308. exports.update = function(assetList, metaList) {
  309. this.assetList = assetList;
  310. this.metaList = metaList;
  311. this.asset = assetList[0];
  312. this.meta = metaList[0];
  313. for (const prop in Elements) {
  314. const element = Elements[prop];
  315. if (element.update) {
  316. element.update.call(this);
  317. }
  318. }
  319. };
  320. exports.close = function() {
  321. for (const prop in Elements) {
  322. const element = Elements[prop];
  323. if (element.close) {
  324. element.close.call(this);
  325. }
  326. }
  327. };