erp-texture-cube.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. 'use strict';
  2. const { updateElementReadonly } = require('../utils/assets');
  3. exports.template = /* html */ `
  4. <section class="asset-erp-texture-cube">
  5. <div class="content">
  6. <ui-prop>
  7. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.anisotropy" tooltip="i18n:ENGINE.assets.erpTextureCube.anisotropyTip"></ui-label>
  8. <ui-num-input slot="content" id="anisotropy"></ui-num-input>
  9. </ui-prop>
  10. <ui-prop>
  11. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.faceSize.name" tooltip="i18n:ENGINE.assets.erpTextureCube.faceSize.title"></ui-label>
  12. <ui-num-input slot="content" id="faceSize"></ui-num-input>
  13. </ui-prop>
  14. <ui-prop>
  15. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.filterMode" tooltip="i18n:ENGINE.assets.erpTextureCube.filterModeTip"></ui-label>
  16. <ui-select slot="content" id="filterMode"></ui-select>
  17. </ui-prop>
  18. <section id="filterAdvancedSection">
  19. <ui-prop>
  20. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.minFilter" tooltip="i18n:ENGINE.assets.erpTextureCube.minFilterTip"></ui-label>
  21. <ui-select slot="content" id="minfilter"></ui-select>
  22. </ui-prop>
  23. <ui-prop>
  24. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.magFilter" tooltip="i18n:ENGINE.assets.erpTextureCube.magFilterTip"></ui-label>
  25. <ui-select slot="content" id="magfilter"></ui-select>
  26. </ui-prop>
  27. <ui-prop>
  28. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.generateMipmaps" tooltip="i18n:ENGINE.assets.erpTextureCube.generateMipmapsTip"></ui-label>
  29. <ui-checkbox slot="content" id="generateMipmaps"></ui-checkbox>
  30. </ui-prop>
  31. <section id="generateMipmapsSection">
  32. <ui-prop>
  33. <ui-label slot="label" tooltip="i18n:ENGINE.assets.erpTextureCube.mipFilterTip" value="i18n:ENGINE.assets.erpTextureCube.mipFilter"></ui-label>
  34. <ui-select slot="content" id="mipfilter"></ui-select>
  35. </ui-prop>
  36. </section>
  37. </section>
  38. <ui-prop>
  39. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.wrapMode" tooltip="i18n:ENGINE.assets.erpTextureCube.wrapModeTip"></ui-label>
  40. <ui-select slot="content" id="wrapMode"></ui-select>
  41. </ui-prop>
  42. <section id="wrapAdvancedSection">
  43. <ui-prop>
  44. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.wrapModeS" tooltip="i18n:ENGINE.assets.erpTextureCube.wrapModeSTip"></ui-label>
  45. <ui-select slot="content" id="wrapModeS"></ui-select>
  46. </ui-prop>
  47. <ui-prop>
  48. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.wrapModeT" tooltip="i18n:ENGINE.assets.erpTextureCube.wrapModeTTip"></ui-label>
  49. <ui-select slot="content" id="wrapModeT"></ui-select>
  50. </ui-prop>
  51. </section>
  52. <ui-prop>
  53. <ui-label slot="label" value="i18n:ENGINE.assets.erpTextureCube.bakeReflectionConvolution" tooltip="i18n:ENGINE.assets.erpTextureCube.bakeReflectionConvolution"></ui-label>
  54. <ui-checkbox id="mipBakeMode" slot="content" value="false"></ui-checkbox>
  55. </ui-prop>
  56. </div>
  57. </section>
  58. `;
  59. exports.style = /* css */`
  60. .asset-erp-texture-cube ui-prop{
  61. margin-right: 4px;
  62. }
  63. .asset-erp-texture-cube #filterAdvancedSection,
  64. .asset-erp-texture-cube #wrapAdvancedSection,
  65. .asset-erp-texture-cube #generateMipmapsSection {
  66. margin-left: 1em;
  67. display: none;
  68. }
  69. `;
  70. exports.$ = {
  71. anisotropy: '#anisotropy',
  72. faceSize: '#faceSize',
  73. filterMode: '#filterMode',
  74. filterAdvancedSection: '#filterAdvancedSection',
  75. minfilter: '#minfilter',
  76. magfilter: '#magfilter',
  77. generateMipmaps: '#generateMipmaps',
  78. generateMipmapsSection: '#generateMipmapsSection',
  79. mipfilter: '#mipfilter',
  80. wrapMode: '#wrapMode',
  81. wrapAdvancedSection: '#wrapAdvancedSection',
  82. wrapModeS: '#wrapModeS',
  83. wrapModeT: '#wrapModeT',
  84. mipBakeMode: '#mipBakeMode',
  85. };
  86. const ModeMap = {
  87. filter: {
  88. 'Nearest (None)': {
  89. minfilter: 'nearest',
  90. magfilter: 'nearest',
  91. mipfilter: 'none',
  92. },
  93. Bilinear: {
  94. minfilter: 'linear',
  95. magfilter: 'linear',
  96. mipfilter: 'none',
  97. },
  98. 'Bilinear with Mipmaps': {
  99. minfilter: 'linear',
  100. magfilter: 'linear',
  101. mipfilter: 'nearest',
  102. },
  103. 'Trilinear with Mipmaps': {
  104. minfilter: 'linear',
  105. magfilter: 'linear',
  106. mipfilter: 'linear',
  107. },
  108. },
  109. wrap: {
  110. Repeat: {
  111. wrapModeS: 'repeat',
  112. wrapModeT: 'repeat',
  113. },
  114. Clamp: {
  115. wrapModeS: 'clamp-to-edge',
  116. wrapModeT: 'clamp-to-edge',
  117. },
  118. Mirror: {
  119. wrapModeS: 'mirrored-repeat',
  120. wrapModeT: 'mirrored-repeat',
  121. },
  122. },
  123. };
  124. const Elements = {
  125. anisotropy: {
  126. ready() {
  127. this.$.anisotropy.addEventListener('change', this.change.bind(this, 'anisotropy'));
  128. this.$.anisotropy.addEventListener('confirm', () => {
  129. this.dispatch('snapshot');
  130. });
  131. },
  132. update() {
  133. this.$.anisotropy.value = this.meta.userData.anisotropy;
  134. this.updateInvalid(this.$.anisotropy, 'anisotropy');
  135. updateElementReadonly.call(this, this.$.anisotropy);
  136. },
  137. },
  138. faceSize: {
  139. ready() {
  140. this.$.faceSize.addEventListener('change', this.change.bind(this, 'faceSize'));
  141. this.$.faceSize.addEventListener('confirm', () => {
  142. this.dispatch('snapshot');
  143. });
  144. },
  145. update() {
  146. this.$.faceSize.value = this.meta.userData.faceSize;
  147. this.updateInvalid(this.$.faceSize, 'faceSize');
  148. updateElementReadonly.call(this, this.$.faceSize);
  149. },
  150. },
  151. filterMode: {
  152. ready() {
  153. this.$.filterMode.addEventListener('change', this.change.bind(this, 'filterMode'));
  154. this.$.filterMode.addEventListener('confirm', () => {
  155. this.dispatch('snapshot');
  156. });
  157. },
  158. update() {
  159. let optionsHtml = '';
  160. // FilterMode 选项
  161. const types = Object.keys(ModeMap.filter).concat('Advanced');
  162. types.forEach((type) => {
  163. optionsHtml += `<option value="${type}">${type}</option>`;
  164. });
  165. this.$.filterMode.innerHTML = optionsHtml;
  166. // 匹配 filterMode 值,没有匹配到组合,则为自定义 Advanced
  167. let value = 'Advanced';
  168. for (const filterKey of Object.keys(ModeMap.filter)) {
  169. const filterItem = ModeMap.filter[filterKey];
  170. let flag = true;
  171. for (const key of Object.keys(filterItem)) {
  172. if (this.meta.userData[key] !== filterItem[key]) {
  173. flag = false;
  174. break;
  175. }
  176. }
  177. if (flag) {
  178. value = filterKey;
  179. break;
  180. }
  181. }
  182. this.$.filterMode.value = value;
  183. // 更新时需要判断是否显示自定义项
  184. value === 'Advanced'
  185. ? (this.$.filterAdvancedSection.style.display = 'block')
  186. : (this.$.filterAdvancedSection.style.display = 'none');
  187. this.updateInvalid(this.$.filterMode, 'filterMode');
  188. updateElementReadonly.call(this, this.$.filterMode);
  189. },
  190. },
  191. minfilter: {
  192. ready() {
  193. this.$.minfilter.addEventListener('change', this.change.bind(this, 'minfilter'));
  194. this.$.minfilter.addEventListener('confirm', () => {
  195. this.dispatch('snapshot');
  196. });
  197. },
  198. update() {
  199. let optionsHtml = '';
  200. const types = ['nearest', 'linear'];
  201. types.forEach((type) => {
  202. optionsHtml += `<option value="${type}">${type.toUpperCase()}</option>`;
  203. });
  204. this.$.minfilter.innerHTML = optionsHtml;
  205. this.$.minfilter.value = this.meta.userData.minfilte || 'nearest';
  206. this.updateInvalid(this.$.minfilter, 'minfilter');
  207. updateElementReadonly.call(this, this.$.minfilter);
  208. },
  209. },
  210. magfilter: {
  211. ready() {
  212. this.$.magfilter.addEventListener('change', this.change.bind(this, 'magfilter'));
  213. this.$.magfilter.addEventListener('confirm', () => {
  214. this.dispatch('snapshot');
  215. });
  216. },
  217. update() {
  218. let optionsHtml = '';
  219. const types = ['nearest', 'linear'];
  220. types.forEach((type) => {
  221. optionsHtml += `<option value="${type}">${type.toUpperCase()}</option>`;
  222. });
  223. this.$.magfilter.innerHTML = optionsHtml;
  224. this.$.magfilter.value = this.meta.userData.magfilter || 'nearest';
  225. this.updateInvalid(this.$.magfilter, 'magfilter');
  226. updateElementReadonly.call(this, this.$.magfilter);
  227. },
  228. },
  229. generateMipmaps: {
  230. ready() {
  231. this.$.generateMipmaps.addEventListener('change', this.change.bind(this, 'generateMipmaps'));
  232. this.$.generateMipmaps.addEventListener('confirm', () => {
  233. this.dispatch('snapshot');
  234. });
  235. },
  236. update() {
  237. this.$.generateMipmaps.value = this.meta.userData.mipfilter ? this.meta.userData.mipfilter !== 'none' : false;
  238. // 更新时判断是否显示 mipfilter 选项
  239. this.$.generateMipmaps.value
  240. ? (this.$.generateMipmapsSection.style.display = 'block')
  241. : (this.$.generateMipmapsSection.style.display = 'none');
  242. this.updateInvalid(this.$.generateMipmaps, 'generateMipmaps');
  243. updateElementReadonly.call(this, this.$.generateMipmaps);
  244. },
  245. },
  246. mipfilter: {
  247. ready() {
  248. this.$.mipfilter.addEventListener('change', this.change.bind(this, 'mipfilter'));
  249. this.$.mipfilter.addEventListener('confirm', () => {
  250. this.dispatch('snapshot');
  251. });
  252. },
  253. update() {
  254. let optionsHtml = '';
  255. const types = ['nearest', 'linear'];
  256. types.forEach((type) => {
  257. optionsHtml += `<option value="${type}">${type.toUpperCase()}</option>`;
  258. });
  259. this.$.mipfilter.innerHTML = optionsHtml;
  260. this.$.mipfilter.value = this.meta.userData.mipfilter || 'nearest';
  261. this.metaList && this.metaList.forEach((meta) => {
  262. Editor.Profile.setTemp('inspector', `${meta.uuid}.texture.mipfilter`, this.meta.userData.mipfilter);
  263. });
  264. this.updateInvalid(this.$.mipfilter, 'mipfilter');
  265. updateElementReadonly.call(this, this.$.mipfilter);
  266. },
  267. },
  268. wrapMode: {
  269. ready() {
  270. this.$.wrapMode.addEventListener('change', this.change.bind(this, 'wrapMode'));
  271. this.$.wrapMode.addEventListener('confirm', () => {
  272. this.dispatch('snapshot');
  273. });
  274. },
  275. update() {
  276. let optionsHtml = '';
  277. // WrapMode 选项
  278. const types = Object.keys(ModeMap.wrap).concat('Advanced');
  279. types.forEach((type) => {
  280. optionsHtml += `<option value="${type}">${type}</option>`;
  281. });
  282. this.$.wrapMode.innerHTML = optionsHtml;
  283. // 匹配 wrapMode 值,没有匹配到组合,则为自定义 Advanced
  284. let value = 'Advanced';
  285. for (const wrapKey of Object.keys(ModeMap.wrap)) {
  286. const wrapItem = ModeMap.wrap[wrapKey];
  287. let flag = true;
  288. for (const key of Object.keys(wrapItem)) {
  289. if (this.meta.userData[key] !== wrapItem[key]) {
  290. flag = false;
  291. break;
  292. }
  293. }
  294. if (flag) {
  295. value = wrapKey;
  296. break;
  297. }
  298. }
  299. this.$.wrapMode.value = value;
  300. // 更新时需要判断是否显示自定义项
  301. value === 'Advanced'
  302. ? (this.$.wrapAdvancedSection.style.display = 'block')
  303. : (this.$.wrapAdvancedSection.style.display = 'none');
  304. this.updateInvalid(this.$.wrapMode, 'wrapMode');
  305. updateElementReadonly.call(this, this.$.wrapMode);
  306. },
  307. },
  308. wrapModeS: {
  309. ready() {
  310. this.$.wrapModeS.addEventListener('change', this.change.bind(this, 'wrapModeS'));
  311. this.$.wrapModeS.addEventListener('confirm', () => {
  312. this.dispatch('snapshot');
  313. });
  314. },
  315. update() {
  316. let optionsHtml = '';
  317. const types = {
  318. Repeat: 'repeat',
  319. Clamp: 'clamp-to-edge',
  320. Mirror: 'mirrored-repeat',
  321. };
  322. for (const type in types) {
  323. optionsHtml += `<option value="${types[type]}">${type}</option>`;
  324. }
  325. this.$.wrapModeS.innerHTML = optionsHtml;
  326. this.$.wrapModeS.value = this.meta.userData.wrapModeS || 'repeat';
  327. this.updateInvalid(this.$.wrapModeS, 'wrapModeS');
  328. updateElementReadonly.call(this, this.$.wrapModeS);
  329. },
  330. },
  331. wrapModeT: {
  332. ready() {
  333. this.$.wrapModeT.addEventListener('change', this.change.bind(this, 'wrapModeT'));
  334. this.$.wrapModeT.addEventListener('confirm', () => {
  335. this.dispatch('snapshot');
  336. });
  337. },
  338. update() {
  339. let optionsHtml = '';
  340. const types = {
  341. Repeat: 'repeat',
  342. Clamp: 'clamp-to-edge',
  343. Mirror: 'mirrored-repeat',
  344. };
  345. for (const type in types) {
  346. optionsHtml += `<option value="${types[type]}">${type}</option>`;
  347. }
  348. this.$.wrapModeT.innerHTML = optionsHtml;
  349. this.$.wrapModeT.value = this.meta.userData.wrapModeT || 'repeat';
  350. this.updateInvalid(this.$.wrapModeT, 'wrapModeT');
  351. updateElementReadonly.call(this, this.$.wrapModeT);
  352. },
  353. },
  354. mipBakeMode: {
  355. ready() {
  356. this.$.mipBakeMode.addEventListener('change', this.change.bind(this, 'mipBakeMode'));
  357. this.$.mipBakeMode.addEventListener('confirm', () => {
  358. this.dispatch('snapshot');
  359. });
  360. },
  361. update() {
  362. this.$.mipBakeMode.value = this.meta.userData.mipBakeMode === 2 ? true : false;
  363. this.updateInvalid(this.$.mipBakeMode, 'mipBakeMode');
  364. updateElementReadonly.call(this, this.$.mipBakeMode);
  365. },
  366. },
  367. };
  368. exports.methods = {
  369. updateInvalid(element, prop) {
  370. let invalid;
  371. // filterMode、 wrapMode 和 generateMipmaps 需要拆解进行判断
  372. switch (prop) {
  373. case 'filterMode':
  374. invalid = this.metaList.some((meta) => {
  375. for (const key of Object.keys(ModeMap.filter.Bilinear)) {
  376. if (meta.userData[key] !== this.meta.userData[key]) {
  377. return true;
  378. }
  379. }
  380. return false;
  381. });
  382. break;
  383. case 'wrapMode':
  384. invalid = this.metaList.some((meta) => {
  385. for (const key of Object.keys(ModeMap.wrap.Repeat)) {
  386. if (meta.userData[key] !== this.meta.userData[key]) {
  387. return true;
  388. }
  389. }
  390. return false;
  391. });
  392. break;
  393. case 'generateMipmaps':
  394. invalid = this.metaList.some((meta) => {
  395. return meta.userData['mipfilter'] !== this.meta.userData['mipfilter'];
  396. });
  397. break;
  398. default:
  399. invalid = this.metaList.some((meta) => {
  400. return meta.userData[prop] !== this.meta.userData[prop];
  401. });
  402. break;
  403. }
  404. element.invalid = invalid;
  405. },
  406. change(key, event) {
  407. let value = event.target.value;
  408. if (key === 'mipBakeMode') {
  409. value = event.target.value ? 2 : 1;
  410. }
  411. this.metaList.forEach((meta) => {
  412. if (key === 'filterMode') {
  413. if (ModeMap.filter[value]) {
  414. const data = ModeMap.filter[value];
  415. for (const key of Object.keys(data)) {
  416. meta.userData[key] = data[key];
  417. }
  418. this.$.filterAdvancedSection.style.display = 'none';
  419. } else {
  420. // 选择 advanced 显示自定义项
  421. this.$.filterAdvancedSection.style.display = 'block';
  422. }
  423. } else if (key === 'generateMipmaps') {
  424. if (!value) {
  425. // 没勾选 生成 mipmaps,不显示 mipfilter 选项
  426. meta.userData.mipfilter = 'none';
  427. this.$.generateMipmapsSection.style.display = 'none';
  428. } else {
  429. this.$.generateMipmapsSection.style.display = 'block';
  430. if (this.$.mipfilter.value === 'none') {
  431. this.$.mipfilter.value = 'nearest';
  432. // TODO: 目前 ui-select 通过 .value 修改组件值后没有触发 change 事件,需要手动提交
  433. this.$.mipfilter.dispatch('change');
  434. }
  435. }
  436. } else if (key === 'wrapMode') {
  437. if (ModeMap.wrap[value]) {
  438. const data = ModeMap.wrap[value];
  439. for (const key of Object.keys(data)) {
  440. meta.userData[key] = data[key];
  441. }
  442. this.$.wrapAdvancedSection.style.display = 'none';
  443. } else {
  444. // 选择 advanced 需要显示自定义项
  445. this.$.wrapAdvancedSection.style.display = 'block';
  446. }
  447. } else {
  448. meta.userData[key] = value || undefined;
  449. }
  450. });
  451. this.dispatch('change');
  452. },
  453. };
  454. exports.ready = function() {
  455. for (const key in Elements) {
  456. if (typeof Elements[key].ready === 'function') {
  457. Elements[key].ready.call(this);
  458. }
  459. }
  460. };
  461. exports.update = function(assetList, metaList) {
  462. this.assetList = assetList;
  463. this.metaList = metaList;
  464. this.asset = assetList[0];
  465. this.meta = metaList[0];
  466. for (const key in Elements) {
  467. if (typeof Elements[key].update === 'function') {
  468. Elements[key].update.call(this);
  469. }
  470. }
  471. };