'use strict';
const { updateElementReadonly, updateElementInvalid, getPropValue, setPropValue } = require('../../utils/assets');
exports.template = /* html */`
There is no LOD group found in the source file. If you want to generate LODs for this model, please use above settings.
`;
exports.style = /* css */`
ui-prop { margin-right: 4px; }
ui-section.config { margin-right: 0; }
.warn-words {
color: var(--color-warn-fill);
}
.lods {
margin-top: 0;
}
.lod-item .lod-item-header {
flex: 1;
display: flex;
align-items: center;
}
.lod-item .lod-item-header .left {
flex: 1;
}
.lod-item .lod-item-header .middle,
.lod-item .lod-item-header .right {
display: flex;
flex: 2;
text-align: right;
}
.lod-item .lod-item-header .middle {
margin: 0 4px;
}
.lod-item .lod-item-header .middle[hidden] {
display: none;
}
.lod-item .lod-item-header .middle > ui-num-input {
width: 48px;
margin-left: 4px;
}
.lod-item .lod-item-header .middle .face-count,
.lod-item .lod-item-header .right .triangles {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 0;
}
.lod-item .lod-item-header .right .operator {
display: none;
margin-left: 8px;
background: var(--color-default-fill);
border-color: var(--color-default-border);
border-radius: calc(var(--size-normal-radius) * 1px);
}
.lod-item .lod-item-header .right .operator > ui-icon {
padding: 0 5px;
transition: color 0.15s;
color: var(--color-default-contrast-emphasis);
position: relative;
}
.lod-item .lod-item-header .right .operator > ui-icon + ui-icon {
margin-left: 1px;
}
.lod-item .lod-item-header .right .operator > ui-icon + ui-icon::after {
content: '';
display: block;
width: 1px;
height: 12px;
position: absolute;
top: 6px;
left: -1px;
background: var(--color-normal-fill-normal);
}
.lod-item .lod-item-header:hover .right .operator:not([hidden]) {
display: flex;
}
.lod-item .lod-item-header .right .operator > ui-icon:hover {
background: var(--color-hover-fill-weaker);
color: var(--color-focus-contrast-emphasis);
}
.lods .not-lod-mesh-label,
.lods .no-lod-label {
color: var(--color-default-fill-weakest)
}
.lods .not-lod-mesh-label[hidden],
.lods .no-lod-label[hidden] {
display: none;
}
.lods .load-mask {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
text-align: center;
background-color: #1b1d1db0;
z-index: 10;
display: none;
}
.lods .load-mask > ui-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`;
exports.$ = {
container: '.container',
normalsSelect: '.normals-select',
tangentsSelect: '.tangents-select',
morphNormalsSelect: '.morphNormals-select',
skipValidationCheckbox: '.skipValidation-checkbox',
disableMeshSplitCheckbox: '.disableMeshSplit-checkbox',
allowMeshDataAccessCheckbox: '.allowMeshDataAccess-checkbox',
addVertexColorCheckbox: '.addVertexColor-checkbox',
promoteSingleRootNodeCheckbox: '.promoteSingleRootNode-checkbox',
generateLightmapUVNodeCheckbox: '.generateLightmapUVNode-checkbox',
// meshOptimizeOptions
meshOptimizeCheckbox: '.meshOptimize-checkbox',
meshOptimizeVertexCacheCheckbox: '.meshOptimize-vertexCache-checkbox',
meshOptimizeVertexFetchCheckbox: '.meshOptimize-vertexFetch-checkbox',
meshOptimizeOverdrawCheckbox: '.meshOptimize-overdraw-checkbox',
// simplifyOptions
meshSimplifyCheckbox: '.meshSimplify-checkbox',
meshSimplifyTargetRatioSlider: '.meshSimplify-targetRatio-slider',
meshSimplifyAutoErrorRateCheckbox: '.meshSimplify-autoErrorRate-checkbox',
meshSimplifyErrorRateSlider: '.meshSimplify-errorRate-slider',
meshSimplifyLockBoundaryCheckbox: '.meshSimplify-lockBoundary-checkbox',
meshClusterCheckbox: '.meshCluster-checkbox',
meshClusterGenerateBoundingCheckbox: '.meshCluster-generateBounding-checkbox',
meshCompressCheckbox: '.meshCompress-checkbox',
meshCompressEncodeCheckbox: '.meshCompress-encode-checkbox',
meshCompressCompressCheckbox: '.meshCompress-compress-checkbox',
meshCompressQuantizeCheckbox: '.meshCompress-quantize-checkbox',
// lods
lodsCheckbox: '.lods-checkbox',
lodItems: '.lod-items',
noLodLabel: '.no-lod-label',
notLodMeshLabel: '.not-lod-mesh-label',
loadMask: '.load-mask',
};
const Elements = {
normals: {
ready() {
const panel = this;
panel.$.normalsSelect.addEventListener('change', panel.setProp.bind(panel, 'normals', 'number'));
panel.$.normalsSelect.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let optionsHtml = '';
const types = ['optional', 'exclude', 'require', 'recalculate'];
types.forEach((type, index) => {
optionsHtml += ``;
});
panel.$.normalsSelect.innerHTML = optionsHtml;
panel.$.normalsSelect.value = getPropValue.call(panel, panel.meta.userData.normals, 2);
updateElementInvalid.call(panel, panel.$.normalsSelect, 'normals');
updateElementReadonly.call(panel, panel.$.normalsSelect);
},
},
tangents: {
ready() {
const panel = this;
panel.$.tangentsSelect.addEventListener('change', panel.setProp.bind(panel, 'tangents', 'number'));
panel.$.tangentsSelect.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let optionsHtml = '';
const types = ['optional', 'exclude', 'require', 'recalculate'];
types.forEach((type, index) => {
optionsHtml += ``;
});
panel.$.tangentsSelect.innerHTML = optionsHtml;
panel.$.tangentsSelect.value = getPropValue.call(panel, panel.meta.userData.tangents, 2);
updateElementInvalid.call(panel, panel.$.tangentsSelect, 'tangents');
updateElementReadonly.call(panel, panel.$.tangentsSelect);
},
},
morphNormals: {
ready() {
const panel = this;
panel.$.morphNormalsSelect.addEventListener('change', panel.setProp.bind(panel, 'morphNormals', 'number'));
panel.$.morphNormalsSelect.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let optionsHtml = '';
const types = ['optional', 'exclude'];
types.forEach((type, index) => {
optionsHtml += ``;
});
panel.$.morphNormalsSelect.innerHTML = optionsHtml;
panel.$.morphNormalsSelect.value = getPropValue.call(panel, panel.meta.userData.morphNormals, 1);
updateElementInvalid.call(panel, panel.$.morphNormalsSelect, 'morphNormals');
updateElementReadonly.call(panel, panel.$.morphNormalsSelect);
},
},
skipValidation: {
ready() {
const panel = this;
panel.$.skipValidationCheckbox.addEventListener('change', panel.setProp.bind(panel, 'skipValidation', 'boolean'));
panel.$.skipValidationCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.skipValidationCheckbox.value = getPropValue.call(panel, panel.meta.userData.skipValidation, true);
updateElementInvalid.call(panel, panel.$.skipValidationCheckbox, 'skipValidation');
updateElementReadonly.call(panel, panel.$.skipValidationCheckbox);
},
},
disableMeshSplit: {
ready() {
const panel = this;
panel.$.disableMeshSplitCheckbox.addEventListener('change', panel.setProp.bind(panel, 'disableMeshSplit', 'boolean'));
panel.$.disableMeshSplitCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.disableMeshSplitCheckbox.value = getPropValue.call(panel, panel.meta.userData.disableMeshSplit, true);
updateElementInvalid.call(panel, panel.$.disableMeshSplitCheckbox, 'disableMeshSplit');
updateElementReadonly.call(panel, panel.$.disableMeshSplitCheckbox);
},
},
allowMeshDataAccess: {
ready() {
const panel = this;
panel.$.allowMeshDataAccessCheckbox.addEventListener('change', panel.setProp.bind(panel, 'allowMeshDataAccess', 'boolean'));
panel.$.allowMeshDataAccessCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.allowMeshDataAccessCheckbox.value = getPropValue.call(panel, panel.meta.userData.allowMeshDataAccess, true);
updateElementInvalid.call(panel, panel.$.allowMeshDataAccessCheckbox, 'allowMeshDataAccess');
updateElementReadonly.call(panel, panel.$.allowMeshDataAccessCheckbox);
},
},
addVertexColorCheckbox: {
ready() {
const panel = this;
panel.$.addVertexColorCheckbox.addEventListener('change', panel.setProp.bind(panel, 'addVertexColor', 'boolean'));
panel.$.addVertexColorCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let defaultValue = false;
if (panel.meta.userData) {
defaultValue = getPropValue.call(panel, panel.meta.userData.addVertexColor, defaultValue);
}
panel.$.addVertexColorCheckbox.value = defaultValue;
updateElementInvalid.call(panel, panel.$.addVertexColorCheckbox, 'addVertexColor');
updateElementReadonly.call(panel, panel.$.addVertexColorCheckbox);
},
},
// move this from ./fbx.js in v3.6.0
promoteSingleRootNode: {
ready() {
const panel = this;
panel.$.promoteSingleRootNodeCheckbox.addEventListener('change', panel.setProp.bind(panel, 'promoteSingleRootNode', 'boolean'));
panel.$.promoteSingleRootNodeCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let defaultValue = false;
if (panel.meta.userData) {
defaultValue = getPropValue.call(panel, panel.meta.userData.promoteSingleRootNode, defaultValue);
}
panel.$.promoteSingleRootNodeCheckbox.value = defaultValue;
updateElementInvalid.call(panel, panel.$.promoteSingleRootNodeCheckbox, 'promoteSingleRootNode');
updateElementReadonly.call(panel, panel.$.promoteSingleRootNodeCheckbox);
},
},
// move this from ./fbx.js in v3.6.0
generateLightmapUVNode: {
ready() {
const panel = this;
panel.$.generateLightmapUVNodeCheckbox.addEventListener('change', panel.setProp.bind(panel, 'generateLightmapUVNode', 'boolean'));
panel.$.generateLightmapUVNodeCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
let defaultValue = false;
if (panel.meta.userData) {
defaultValue = getPropValue.call(panel, panel.meta.userData.generateLightmapUVNode, defaultValue);
}
panel.$.generateLightmapUVNodeCheckbox.value = defaultValue;
updateElementInvalid.call(panel, panel.$.generateLightmapUVNodeCheckbox, 'generateLightmapUVNode');
updateElementReadonly.call(panel, panel.$.generateLightmapUVNodeCheckbox);
},
},
// meshOptimize start
meshOptimize: {
ready() {
const panel = this;
panel.$.meshOptimizeCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshOptimize.enable', 'boolean'));
panel.$.meshOptimizeCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshOptimizeCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshOptimize, false, 'enable');
updateElementInvalid.call(panel, panel.$.meshOptimizeCheckbox, 'meshOptimize.enable');
updateElementReadonly.call(panel, panel.$.meshOptimizeCheckbox);
},
},
meshOptimizeVertexCache: {
ready() {
const panel = this;
panel.$.meshOptimizeVertexCacheCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshOptimize.vertexCache', 'boolean'));
panel.$.meshOptimizeVertexCacheCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshOptimizeVertexCacheCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshOptimize, false, 'vertexCache');
updateElementInvalid.call(panel, panel.$.meshOptimizeVertexCacheCheckbox, 'meshOptimize.vertexCache');
updateElementReadonly.call(panel, panel.$.meshOptimizeVertexCacheCheckbox);
},
},
meshOptimizeVertexFetch: {
ready() {
const panel = this;
panel.$.meshOptimizeVertexFetchCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshOptimize.vertexFetch', 'boolean'));
panel.$.meshOptimizeVertexFetchCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshOptimizeVertexFetchCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshOptimize, false, 'vertexFetch');
updateElementInvalid.call(panel, panel.$.meshOptimizeVertexFetchCheckbox, 'meshOptimize.vertexFetch');
updateElementReadonly.call(panel, panel.$.meshOptimizeVertexFetchCheckbox);
},
},
meshOptimizeOverdraw: {
ready() {
const panel = this;
panel.$.meshOptimizeOverdrawCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshOptimize.overdraw', 'boolean'));
panel.$.meshOptimizeOverdrawCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshOptimizeOverdrawCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshOptimize, false, 'overdraw');
updateElementInvalid.call(panel, panel.$.meshOptimizeOverdrawCheckbox, 'meshOptimize.overdraw');
updateElementReadonly.call(panel, panel.$.meshOptimizeOverdrawCheckbox);
},
},
// meshOptimize end
// meshSimplify start
meshSimplify: {
ready() {
const panel = this;
panel.$.meshSimplifyCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshSimplify.enable', 'boolean'));
panel.$.meshSimplifyCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshSimplifyCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshSimplify, false, 'enable');
updateElementInvalid.call(panel, panel.$.meshSimplifyCheckbox, 'meshSimplify.enable');
updateElementReadonly.call(panel, panel.$.meshSimplifyCheckbox);
},
},
meshSimplifyTargetRatio: {
ready() {
const panel = this;
panel.$.meshSimplifyTargetRatioSlider.addEventListener('change', panel.setProp.bind(panel, 'meshSimplify.targetRatio', 'number'));
panel.$.meshSimplifyTargetRatioSlider.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshSimplifyTargetRatioSlider.value = getPropValue.call(panel, panel.meta.userData.meshSimplify, 1, 'targetRatio');
updateElementInvalid.call(panel, panel.$.meshSimplifyTargetRatioSlider, 'meshSimplify.targetRatio');
updateElementReadonly.call(panel, panel.$.meshSimplifyTargetRatioSlider);
},
},
meshSimplifyAutoErrorRateCheckbox: {
ready() {
const panel = this;
panel.$.meshSimplifyAutoErrorRateCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshSimplify.autoErrorRate', 'boolean'));
panel.$.meshSimplifyAutoErrorRateCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshSimplifyAutoErrorRateCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshSimplify, false, 'autoErrorRate');
updateElementInvalid.call(panel, panel.$.meshSimplifyAutoErrorRateCheckbox, 'meshSimplify.autoErrorRate');
updateElementReadonly.call(panel, panel.$.meshSimplifyAutoErrorRateCheckbox);
},
},
meshSimplifyErrorRate: {
ready() {
const panel = this;
panel.$.meshSimplifyErrorRateSlider.addEventListener('change', panel.setProp.bind(panel, 'meshSimplify.errorRate', 'number'));
panel.$.meshSimplifyErrorRateSlider.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshSimplifyErrorRateSlider.value = getPropValue.call(panel, panel.meta.userData.meshSimplify, 1, 'errorRate');
updateElementInvalid.call(panel, panel.$.meshSimplifyErrorRateSlider, 'meshSimplify.errorRate');
updateElementReadonly.call(panel, panel.$.meshSimplifyErrorRateSlider);
},
},
meshSimplifyLockBoundaryCheckbox: {
ready() {
const panel = this;
panel.$.meshSimplifyLockBoundaryCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshSimplify.lockBoundary', 'boolean'));
panel.$.meshSimplifyLockBoundaryCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshSimplifyLockBoundaryCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshSimplify, false, 'lockBoundary');
updateElementInvalid.call(panel, panel.$.meshSimplifyLockBoundaryCheckbox, 'meshSimplify.lockBoundary');
updateElementReadonly.call(panel, panel.$.meshSimplifyLockBoundaryCheckbox);
},
},
meshClusterCheckbox: {
ready() {
const panel = this;
panel.$.meshClusterCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCluster.enable', 'boolean'));
panel.$.meshClusterCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshClusterCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCluster, false, 'enable');
updateElementInvalid.call(panel, panel.$.meshClusterCheckbox, 'meshCluster.enable');
updateElementReadonly.call(panel, panel.$.meshClusterCheckbox);
},
},
meshClusterGenerateBoundingCheckbox: {
ready() {
const panel = this;
panel.$.meshClusterGenerateBoundingCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCluster.generateBounding', 'boolean'));
panel.$.meshClusterGenerateBoundingCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshClusterGenerateBoundingCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCluster, false, 'generateBounding');
updateElementInvalid.call(panel, panel.$.meshClusterGenerateBoundingCheckbox, 'meshCluster.generateBounding');
updateElementReadonly.call(panel, panel.$.meshClusterGenerateBoundingCheckbox);
},
},
// meshSimplify end
// meshCompress start
meshCompress: {
ready() {
const panel = this;
panel.$.meshCompressCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCompress.enable', 'boolean'));
panel.$.meshCompressCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshCompressCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCompress, false, 'enable');
updateElementInvalid.call(panel, panel.$.meshCompressCheckbox, 'meshCompress.enable');
updateElementReadonly.call(panel, panel.$.meshCompressCheckbox);
},
},
meshCompressEncode: {
ready() {
const panel = this;
panel.$.meshCompressEncodeCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCompress.encode', 'boolean'));
panel.$.meshCompressEncodeCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshCompressEncodeCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCompress, false, 'encode');
updateElementInvalid.call(panel, panel.$.meshCompressEncodeCheckbox, 'meshCompress.encode');
updateElementReadonly.call(panel, panel.$.meshCompressEncodeCheckbox);
},
},
meshCompressCompress: {
ready() {
const panel = this;
panel.$.meshCompressCompressCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCompress.compress', 'boolean'));
panel.$.meshCompressCompressCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshCompressCompressCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCompress, false, 'compress');
updateElementInvalid.call(panel, panel.$.meshCompressCompressCheckbox, 'meshCompress.compress');
updateElementReadonly.call(panel, panel.$.meshCompressCompressCheckbox);
},
},
meshCompressQuantize: {
ready() {
const panel = this;
panel.$.meshCompressQuantizeCheckbox.addEventListener('change', panel.setProp.bind(panel, 'meshCompress.quantize', 'boolean'));
panel.$.meshCompressQuantizeCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.meshCompressQuantizeCheckbox.value = getPropValue.call(panel, panel.meta.userData.meshCompress, false, 'quantize');
updateElementInvalid.call(panel, panel.$.meshCompressQuantizeCheckbox, 'meshCompress.quantize');
updateElementReadonly.call(panel, panel.$.meshCompressQuantizeCheckbox);
},
},
// meshCompress end
// lods start
lods: {
ready() {
const panel = this;
// Listening lod on and off
panel.$.lodsCheckbox.addEventListener('change', panel.setProp.bind(panel, 'lods.enable', 'boolean'));
panel.$.lodsCheckbox.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
// listening for screenRatio and faceCount changes
panel.$.lodItems.addEventListener('change', (event) => {
const path = event.target.getAttribute('path');
const index = Number(event.target.getAttribute('key'));
const value = Editor.Utils.Math.divide(event.target.value, 100);
switch (path) {
case 'screenRatio':
// TODO: Min/max of the screenRatio for each level of LOD
panel.metaList.forEach((meta) => {
meta.userData.lods && (meta.userData.lods.options[index].screenRatio = value);
});
panel.dispatch('change');
break;
case 'faceCount':
// TODO: Min/max of the faceCount for each level of LOD
panel.metaList.forEach((meta) => {
meta.userData.lods && (meta.userData.lods.options[index].faceCount = value);
});
panel.dispatch('change');
break;
}
});
panel.$.lodItems.addEventListener('confirm', () => {
panel.dispatch('snapshot');
});
},
update() {
const panel = this;
panel.$.lodsCheckbox.value = getPropValue.call(panel, panel.meta.userData.lods, false, 'enable');
const lodOptions = panel.meta.userData.lods && panel.meta.userData.lods.options || [];
const hasBuiltinLOD = panel.meta.userData.lods && panel.meta.userData.lods.hasBuiltinLOD || false;
panel.$.lodItems.innerHTML = getLodItemHTML(lodOptions, panel.LODTriangleCounts, hasBuiltinLOD);
hasBuiltinLOD ? panel.$.noLodLabel.setAttribute('hidden', '') : panel.$.noLodLabel.removeAttribute('hidden');
if (panel.notLODTriangleCounts && panel.notLODTriangleCounts.length > 0) {
const totalNotLODTriangleCounts = panel.notLODTriangleCounts.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
panel.$.notLodMeshLabel.innerHTML = `There are ${panel.notLODTriangleCounts.length} non-LOD mesh(es) in the FBX, and the total triangles count is ${totalNotLODTriangleCounts}.`;
panel.$.notLodMeshLabel.removeAttribute('hidden');
} else {
panel.$.notLodMeshLabel.setAttribute('hidden', '');
}
if (panel.$.loadMask.style.display === 'block' && this.asset.imported) {
panel.$.loadMask.style.display = 'none';
}
// Listening to the addition and removal of the lod hierarchy
const uiIcons = panel.$.lodItems.querySelectorAll('ui-icon[value="add"], .lod-items ui-icon[value="reduce"]');
uiIcons.forEach((uiIcon) => {
uiIcon.addEventListener('click', (event) => {
event.stopPropagation();
const path = event.target.getAttribute('path');
const index = Number(event.target.getAttribute('key'));
const lods = panel.meta.userData.lods;
if (!lods) {
return;
}
if (path === 'insertLod') {
if (Object.keys(lods.options).length >= 8) {
console.warn('Maximum 8 LOD, Can\'t add more LOD');
return;
}
const preScreenRatio = lods.options[index].screenRatio;
const nextScreenRatio = lods.options[index + 1] ? lods.options[index + 1].screenRatio : 0;
const preFaceCount = lods.options[index].faceCount;
const nextFaceCount = lods.options[index + 1] ? lods.options[index + 1].faceCount : 0;
const option = {
screenRatio: (preScreenRatio + nextScreenRatio) / 2,
faceCount: (preFaceCount + nextFaceCount) / 2,
};
// Insert the specified lod level
for (let keyIndex = Object.keys(lods.options).length - 1; keyIndex > index; keyIndex--) {
lods.options[keyIndex + 1] = lods.options[keyIndex];
panel.LODTriangleCounts[keyIndex + 1] = panel.LODTriangleCounts[keyIndex];
}
lods.options[index + 1] = option;
panel.LODTriangleCounts[index + 1] = 0;
// update panel
Elements.lods.update.call(panel);
panel.dispatch('change');
panel.dispatch('snapshot');
} else if (path === 'deleteLod') {
if (Object.keys(lods.options).length <= 1) {
console.warn('At least one LOD, Can\'t delete any more');
return;
}
// Delete the specified lod level
for (let key = index; key < Object.keys(lods.options).length; key++) {
lods.options[key] = lods.options[key + 1];
panel.LODTriangleCounts[key] = panel.LODTriangleCounts[key + 1];
}
lods.options.pop();
panel.LODTriangleCounts.pop();
// update panel
Elements.lods.update.call(panel);
panel.dispatch('change');
panel.dispatch('snapshot');
}
});
});
updateElementInvalid.call(panel, panel.$.lodsCheckbox, 'lods.enable');
updateElementReadonly.call(panel, panel.$.lodsCheckbox, hasBuiltinLOD);
},
},
// lods end
// when lang change
i18n: {
ready() {
const panel = this;
Elements.i18n.changeBind = Elements.i18n.change.bind(panel);
Editor.Message.addBroadcastListener('i18n:change', Elements.i18n.changeBind);
},
close() {
Editor.Message.removeBroadcastListener('i18n:change', Elements.i18n.changeBind);
Elements.i18n.changeBind = undefined;
},
change() {
const panel = this;
Elements.normals.update.call(panel);
Elements.tangents.update.call(panel);
Elements.morphNormals.update.call(panel);
},
},
};
exports.methods = {
t(key) {
return Editor.I18n.t(`ENGINE.assets.fbx.${key}`);
},
setProp(prop, type, event) {
setPropValue.call(this, prop, type, event);
this.dispatch('change');
this.dispatch('track', { tab: 'model', prop, value: event.target.value });
},
apply() {
this.$.loadMask.style.display = 'block';
},
};
exports.ready = function() {
this.applyFun = this.apply.bind(this);
Editor.Message.addBroadcastListener('fbx-inspector:apply', this.applyFun);
for (const prop in Elements) {
const element = Elements[prop];
if (element.ready) {
element.ready.call(this);
}
}
};
exports.update = function(assetList, metaList) {
this.assetList = assetList;
this.metaList = metaList;
this.asset = assetList[0];
this.meta = metaList[0];
const { LODTriangleCounts, notLODTriangleCounts } = handleLODTriangleCounts(this.meta);
this.LODTriangleCounts = LODTriangleCounts;
this.notLODTriangleCounts = notLODTriangleCounts;
for (const prop in Elements) {
const element = Elements[prop];
if (element.update) {
element.update.call(this);
}
}
};
exports.close = function() {
Editor.Message.removeBroadcastListener('fbx-inspector:apply', this.applyFun);
for (const prop in Elements) {
const element = Elements[prop];
if (element.close) {
element.close.call(this);
}
}
};
function handleLODTriangleCounts(meta) {
if (!meta.userData.lods) {
return [];
}
let LODTriangleCounts = new Array(meta.userData.lods.options.length).fill(0);
let notLODTriangleCounts = new Array();
for (const key in meta.subMetas) {
const subMeta = meta.subMetas[key];
if (subMeta.importer === 'gltf-mesh') {
const { lodOptions, triangleCount, lodLevel } = subMeta.userData;
const index = !meta.userData.lods.hasBuiltinLOD ? (lodOptions ? lodLevel : 0) : lodLevel;
// When an FBX comes with LOD, there may be non-LOD meshes, and the count of these meshes should be calculated separately.
if (index === undefined) {
notLODTriangleCounts.push(triangleCount || 0);
continue;
}
LODTriangleCounts[index] = (LODTriangleCounts[index] || 0) + (triangleCount || 0);
}
}
return {
LODTriangleCounts,
notLODTriangleCounts,
};
}
function getLodItemHTML(lodOptions, LODTriangleCounts, hasBuiltinLOD = false) {
let lodItemsStr = '';
for (const index in lodOptions) {
const lodItem = lodOptions[index];
lodItemsStr += `
`;
}
return lodItemsStr;
}