| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229 |
- 'use strict';
- const { updateElementReadonly, updateElementInvalid, setPropValue, getPropValue } = require('../../utils/assets');
- exports.template = /* html */`
- <div class="container">
- <div class="animator-config">
- <ui-prop class="animator-config-import-all-animator">
- <ui-label slot="label" value="i18n:ENGINE.assets.fbx.GlTFUserData.mountAllAnimationsOnPrefab.name"></ui-label>
- <ui-checkbox slot="content"></ui-checkbox>
- </ui-prop>
- </div>
- <div class="show-type-wrap">
- <ui-tab class="show-type" value="0">
- <ui-button value="time">Time</ui-button>
- <ui-button value="frame">Frame</ui-button>
- </ui-tab>
- </div>
- <div class="clips"></div>
- <div class="editor">
- <div class="anim-name">
- <ui-icon value="video"></ui-icon>
- <ui-input class="clip-name"></ui-input>
- </div>
- <div class="clip-info">
- <div class="left">Time: <span class="clip-duration"></span> (s)</div>
- <div class="right">
- <ui-num-input class="clip-fps" min="1" max="120" step="1"></ui-num-input>
- FPS
- </div>
- </div>
- <div class="edit-ruler">
- <div class="grid ruler-making"></div>
- <div class="grid ruler-gear"></div>
- <div class="control-wrap">
- <div class="control-duration"></div>
- <div class="control control-left">
- <div class="box"></div>
- <div class="direction"></div>
- </div>
- <div class="control control-right">
- <div class="box"></div>
- <div class="direction"></div>
- </div>
- <div class="control control-virtual">
- <div class="box"></div>
- <div class="direction"></div>
- <div class="number control-virtual-number"></div>
- </div>
- </div>
- </div>
- <div class="cut-info">
- <div class="left">
- <span>Start: </span>
- <ui-num-input path="from" step="1" min="0" class="clip-from"></ui-num-input>
- </div>
- <div class="frames-info">
- <div class="left">Frames: <span class="clip-frames"></span></div>
- </div>
- <div class="right">
- <span>End: </span>
- <ui-num-input path="to" step="1" min="0" class="clip-to"></ui-num-input>
- </div>
- </div>
- <ui-prop>
- <span slot="label">WrapMode</span>
- <ui-select slot="content" class="wrap-mode">
- <option value="0">Default</option>
- <option value="1">Normal</option>
- <option value="2">Loop</option>
- <option value="22">PingPong</option>
- <option value="36">Reverse</option>
- <option value="38">LoopReverse</option>
- </ui-select>
- </ui-prop>
- <ui-prop>
- <span slot="label">Speed</span>
- <ui-num-input slot="content" class="speed"></ui-num-input>
- </ui-prop>
- <ui-section expand cache-expand="inspector-asset-fbx-animation-additive">
- <ui-label slot="header" value="i18n:ENGINE.assets.fbx.animationSetting.additive.header"></ui-label>
- <ui-prop>
- <ui-label slot="label" value="i18n:ENGINE.assets.fbx.animationSetting.additive.enabled.label"></ui-label>
- <ui-checkbox slot="content" class="additive-enabled"
- tooltip="i18n:ENGINE.assets.fbx.animationSetting.additive.enabled.tooltip"></ui-checkbox>
- </ui-prop>
- <ui-prop ui="asset">
- <ui-label slot="label" value="i18n:ENGINE.assets.fbx.animationSetting.additive.refClip.label"></ui-label>
- <ui-asset slot="content" droppable="cc.AnimationClip" class="ref-clip"
- tooltip="i18n:ENGINE.assets.fbx.animationSetting.additive.refClip.tooltip"></ui-asset>
- </ui-prop>
- </ui-section>
- </div>
- <ui-label class="multiple-warn-tip" value="i18n:ENGINE.assets.multipleWarning"></ui-label>
- </div>
- `;
- exports.style = /* css */`
- .container {
- padding: 4px;
- }
- .container[multiple-invalid] > *:not(.multiple-warn-tip) {
- display: none!important;
- }
- .container[multiple-invalid] > .multiple-warn-tip {
- display: block;
- }
- .container .multiple-warn-tip {
- display: none;
- text-align: center;
- color: var(--color-focus-contrast-weakest);
- margin-top: 8px;
- }
- .container > .show-type-wrap {
- text-align: center;
- margin-top: 8px;
- }
- .container > .clips {
- padding: 4px;
- border-radius: calc(var(--size-normal-radius) * 1px);
- overflow-y: auto;
- max-height: 250px;
- background: var(--color-normal-fill-emphasis);
- margin-bottom: 8px;
- }
- .container > .clips > .clip {}
- .container > .clips > .clip > .table {
- border-radius: 4px;
- border-bottom-right-radius: 0;
- background: var(--color-normal-fill-emphasis);
- }
- .container > .clips > .clip > .table > .header {
- display: flex;
- padding: 0 4px;
- margin-bottom: 4px;
- border-bottom: 1px solid var(--color-default-border);
- color: var(--color-default-fill-weakest);
- }
- .container > .clips > .clip > .table > .line {
- display: flex;
- padding: 0 4px;
- cursor: pointer;
- }
- .container > .clips > .clip > .table > .line[active] {
- background-color: var(--color-info-fill-important);
- }
- .container > .clips > .clip > .table > .line > .name,
- .container > .clips > .clip > .table > .header > .name {
- flex: 1;
- }
- .container > .clips > .clip > .table > .line > .time,
- .container > .clips > .clip > .table > .header > .time {
- width: 40px;
- text-align: right;
- }
- .container > .clips > .clip > .add-clip {
- display: flex;
- justify-content: flex-end;
- }
- .container > .clips > .clip > .add-clip > .button > ui-icon {
- padding: 0 4px;
- border-radius: 2px;
- line-height: 16px;
- margin-left: 10px;
- margin-right: 2px;
- cursor: pointer;
- }
- .container > .clips > .clip > .add-clip > .button > ui-icon:hover {
- background: var(--color-normal-fill);
- }
- .container > .clips > .clip > .add-clip > .button > ui-icon[disabled] {
- opacity: 0.55;
- pointer-events: none;
- }
- .container > .editor[disabled] {
- opacity: 0.55;
- pointer-events: none;
- }
- .container > .editor > .anim-name {
- display: flex;
- justify-content: space-between;
- margin-top: 10px;
- margin-bottom: 5px;
- }
- .container > .editor > .anim-name {
- display: flex;
- justify-content: space-between;
- margin-top: 10px;
- margin-bottom: 5px;
- }
- .container > .editor > .anim-name > ui-icon {
- font-size: 18px;
- margin: auto 10px;
- margin-left: 0;
- }
- .container > .editor > .anim-name > ui-input {
- flex: 1;
- }
- .container > .editor > .clip-info {
- font-size: 11px;
- color: var(--color-normal-fill-weakest);
- display: flex;
- justify-content: space-between;
- margin-bottom: 5px;
- }
- .container > .editor ui-num-input {
- color: var(--color-normal-contrast);
- width: 50px;
- }
- .container > .editor > .cut-info {
- margin: 5px 0 10px;
- line-height: 20px;
- display: flex;
- justify-content: space-between;
- }
- .container > .editor > .cut-info > .frames-info {
- font-size: 11px;
- color: var(--color-normal-fill-weakest);
- }
- .container > .editor > .edit-ruler {
- border: 1px solid var(--color-normal-contrast-important);
- border-top: transparent;
- padding: 0 20px;
- display: flex;
- flex-direction: column;
- position: relative;
- user-select: none;
- }
- .container > .editor > .edit-ruler > .grid {
- position: relative;
- display: flex;
- font-size: 10px;
- box-sizing: border-box;
- height: 18px;
- }
- .container > .editor > .edit-ruler > .grid > .label-item {
- position: absolute;
- }
- .container > .editor > .edit-ruler > .ruler-gear > .start {
- position: absolute;
- top: 3px;
- }
- .container > .editor > .edit-ruler > .ruler-gear > .grid-item {
- display: flex;
- justify-content: space-between;
- align-items: flex-end;
- }
- .container > .editor > .edit-ruler > .grid .sm-grid {
- display: inline-block;
- width: 20%;
- height: 4px;
- border-left: 1px solid var(--color-normal-contrast-important);
- }
- .container > .editor > .edit-ruler > .grid .mid-grid {
- display: inline-block;
- width: 20%;
- height: 8px;
- border-left: 1px solid var(--color-normal-contrast-important);
- }
- .container > .editor > .edit-ruler > .control-wrap {
- position: relative;
- width: 100%;
- left: 0;
- top: -16px;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-duration {
- background: var(--color-focus-fill);
- position: absolute;
- height: 16px;
- opacity: 0.2;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control {
- user-select: none;
- width: 6px;
- position: absolute;
- cursor: pointer;
- opacity: 0.7;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control:hover {
- opacity: 1;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control > .box {
- background: var(--color-focus-fill);
- width: 100%;
- height: 10px;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control > .direction {
- border: 3px solid var(--color-focus-fill);
- width: 0;
- height: 0;
- border-bottom-color: transparent;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-left > .direction {
- border-left-color: transparent;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-right > .direction {
- border-right-color: transparent;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-virtual {
- --color-focus-fill: var(--color-success-fill);
- display: none;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-virtual > .box {
- background: var(--color-focus-fill);
- width: 100%;
- height: 10px;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-virtual[direction="right"] > .direction {
- border-right-color: transparent;
- border-left-color: var(--color-focus-fill);
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-virtual > .direction {
- border-left-color: transparent;
- }
- .container > .editor > .edit-ruler > .control-wrap > .control-virtual > .number {
- margin-top: -47px;
- font-size: 10px;
- color: var(--color-success-fill-weaker);
- }
- `;
- exports.$ = {
- container: '.container',
- importAllAnimationsCheckbox: '.animator-config-import-all-animator ui-checkbox',
- importAllAnimatorWrap: '.animator-config-import-all-animator',
- clips: '.clips',
- editor: '.editor',
- clipName: '.clip-name',
- clipDuration: '.clip-duration',
- clipFPS: '.clip-fps',
- clipFrom: '.clip-from',
- clipTo: '.clip-to',
- clipFrames: '.clip-frames',
- wrapMode: '.wrap-mode',
- speed: '.speed',
- additiveEnabled: '.additive-enabled',
- refClip: '.ref-clip',
- rulerMaking: '.ruler-making',
- rulerGear: '.ruler-gear',
- controlWrap: '.control-wrap',
- controlDuration: '.control-duration',
- controlLeft: '.control-left',
- controlRight: '.control-right',
- controlVirtual: '.control-virtual',
- controlVirtualNumber: '.control-virtual-number',
- showTypeWrap: '.show-type-wrap',
- showType: '.show-type',
- };
- /**
- * attribute corresponds to the edit element
- */
- const Elements = {
- // infos put first
- infos: {
- ready() {
- const panel = this;
- Object.assign(panel, {
- animationInfos: null,
- });
- },
- update() {
- const panel = this;
- if (panel.meta && panel.meta.userData.animationImportSettings) {
- panel.animationInfos = panel.meta.userData.animationImportSettings;
- // Support multiple selection when the list display, limit the number of display clip name collection for renaming and new to determine whether the same name
- panel.clipNames = new Set();
- for (const animationInfo of panel.animationInfos) {
- panel.clipNames.add(animationInfo.name);
- for (const subAnimInfo of animationInfo.splits) {
- panel.clipNames.add(subAnimInfo.name);
- }
- }
- } else {
- panel.animationInfos = null;
- }
- },
- },
- showType: {
- ready() {
- const panel = this;
- panel.animationTimeShowType = panel.$.showType.value === 0 ? 'time' : 'frame';
- panel.$.showType.addEventListener('change', (event) => {
- panel.animationTimeShowType = event.target.value === 0 ? 'time' : 'frame';
- Elements.clips.update.call(panel);
- });
- },
- update() {
- const panel = this;
- if (!panel.animationInfos) {
- panel.$.showTypeWrap.style.display = 'none';
- return;
- } else {
- panel.$.showTypeWrap.style.display = 'block';
- }
- panel.animationTimeShowType = panel.$.showType.value === 0 ? 'time' : 'frame';
- },
- },
- clips: {
- ready() {
- const panel = this;
- Object.assign(panel, {
- splitClipIndex: 0,
- rawClipIndex: 0,
- currentClipInfo: null,
- });
- },
- update() {
- const panel = this;
- panel.$.clips.innerText = '';
- if (!panel.animationInfos) {
- panel.$.clips.style.display = 'none';
- return;
- } else {
- panel.$.clips.style.display = 'block';
- }
- panel.updateRawClipInfo();
- panel.updateCurrentClipInfo();
- panel.animationInfos.forEach((animInfo, rawClipIndex) => {
- const clip = document.createElement('div');
- clip.setAttribute('class', 'clip');
- panel.$.clips.appendChild(clip);
- if (!animInfo.duration) {
- clip.setAttribute('disabled', 'true');
- }
- const table = document.createElement('div');
- table.setAttribute('class', 'table');
- clip.appendChild(table);
- const header = document.createElement('div');
- header.setAttribute('class', 'header');
- table.appendChild(header);
- const name = document.createElement('div');
- name.setAttribute('class', 'name');
- name.innerHTML = `Clips <i>( ${animInfo.name} )</i> `;
- header.appendChild(name);
- const time = document.createElement('div');
- time.setAttribute('class', 'time');
- time.innerHTML = 'Start';
- header.appendChild(time);
- const timeEnd = document.createElement('div');
- timeEnd.setAttribute('class', 'time end');
- timeEnd.innerHTML = 'End';
- header.appendChild(timeEnd);
- animInfo.splits.forEach((subAnim, splitClipIndex) => {
- const line = document.createElement('div');
- line.setAttribute('class', 'line');
- if (panel.rawClipIndex === rawClipIndex && panel.splitClipIndex === splitClipIndex) {
- line.setAttribute('active', true);
- }
- table.appendChild(line);
- line.setAttribute('rawCLipIndex', rawClipIndex);
- line.setAttribute('splitClipIndex', splitClipIndex);
- line.addEventListener('click', () => {
- panel.onSelect(rawClipIndex, splitClipIndex);
- });
- const name = document.createElement('div');
- name.setAttribute('class', 'name');
- name.innerHTML = subAnim.name;
- line.appendChild(name);
- const time = document.createElement('div');
- time.setAttribute('class', 'time');
- time.innerHTML = panel.animationTimeShowType === 'time' ? subAnim.from.toFixed(3) : Math.round(subAnim.from * (subAnim.fps || panel.rawClipInfo.fps));
- line.appendChild(time);
- const timeEnd = document.createElement('div');
- timeEnd.setAttribute('class', 'time end');
- timeEnd.innerHTML = panel.animationTimeShowType === 'time' ? subAnim.to.toFixed(3) : Math.round(subAnim.to * (subAnim.fps || panel.rawClipInfo.fps));
- line.appendChild(timeEnd);
- });
- // Button area
- const addClip = document.createElement('div');
- addClip.setAttribute('class', 'add-clip');
- clip.appendChild(addClip);
- const button = document.createElement('div');
- button.setAttribute('class', 'button');
- addClip.appendChild(button);
- const addIcon = document.createElement('ui-icon');
- addIcon.setAttribute('value', 'add');
- addIcon.setAttribute('tooltip', 'Duplicate Selected');
- updateElementReadonly.call(panel, addIcon);
- button.appendChild(addIcon);
- addIcon.addEventListener('click', () => {
- const newInfo = panel.newClipTemplate();
- panel.clipNames.add(newInfo.name);
- panel.animationInfos[panel.rawClipIndex].splits.push(newInfo);
- Elements.clips.update.call(panel);
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- });
- const miniIcon = document.createElement('ui-icon');
- miniIcon.setAttribute('value', 'mini');
- miniIcon.setAttribute('tooltip', 'Remove Selected');
- updateElementReadonly.call(panel, miniIcon);
- button.prepend(miniIcon);
- miniIcon.addEventListener('click', () => {
- panel.updateCurrentClipInfo();
- if (!panel.currentClipInfo) {
- return;
- }
- panel.clipNames.delete(panel.currentClipInfo.name);
- panel.animationInfos[panel.rawClipIndex].splits.splice(panel.splitClipIndex, 1);
- const length = panel.animationInfos[panel.rawClipIndex].splits.length;
- if (length > 0 && panel.splitClipIndex > 0 && panel.splitClipIndex >= length) {
- panel.splitClipIndex = length - 1;
- }
- Elements.clips.update.call(panel);
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- });
- });
- },
- },
- editor: {
- ready() {
- const panel = this;
- Object.assign(panel, {
- gridTableWith: panel.$.container.getBoundingClientRect().width,
- virtualControl: null,
- clipNames: [],
- });
- panel.onClipNameBind = panel.onClipName.bind(panel);
- panel.$.clipName.addEventListener('confirm', panel.onClipNameBind);
- panel.onMouseDownBindLeft = panel.onMouseDown.bind(panel, 'left');
- panel.onMouseDownBindRight = panel.onMouseDown.bind(panel, 'right');
- panel.$.controlLeft.addEventListener('mousedown', panel.onMouseDownBindLeft);
- panel.$.controlRight.addEventListener('mousedown', panel.onMouseDownBindRight);
- panel.onCutClipBind = panel.onCutClip.bind(panel);
- panel.$.clipFrom.addEventListener('confirm', panel.onCutClipBind);
- panel.$.clipTo.addEventListener('confirm', panel.onCutClipBind);
- panel.onFpsChangeBind = panel.onFpsChange.bind(panel);
- panel.$.clipFPS.addEventListener('confirm', panel.onFpsChangeBind);
- panel.onWrapModeChangeBind = panel.onWrapModeChange.bind(panel);
- panel.$.wrapMode.addEventListener('confirm', panel.onWrapModeChangeBind);
- panel.onSpeedChangeBind = panel.onSpeedChange.bind(panel);
- panel.$.speed.addEventListener('confirm', panel.onSpeedChangeBind);
- panel.onAdditiveEnabledChangedBind = panel.onAdditiveEnabledChanged.bind(panel);
- panel.$.additiveEnabled.addEventListener('confirm', panel.onAdditiveEnabledChangedBind);
- panel.onRefClipChangedBind = panel.onRefClipChanged.bind(panel);
- panel.$.refClip.addEventListener('confirm', panel.onRefClipChangedBind);
- function observer() {
- const rect = panel.$.editor.getBoundingClientRect();
- panel.gridTableWith = rect.width - 60;
- if (panel.gridTableWith < 0) {
- panel.gridTableWith = panel.$.container.getBoundingClientRect().width;
- }
- Elements.clips.update.call(panel);
- Elements.editor.update.call(panel);
- }
- panel.resizeObserver = new window.ResizeObserver(observer);
- panel.resizeObserver.observe(panel.$.editor);
- observer();
- },
- close() {
- const panel = this;
- panel.resizeObserver.unobserve(panel.$.editor);
- panel.$.clipName.removeEventListener('confirm', panel.onClipNameBind);
- panel.$.controlLeft.removeEventListener('mousedown', panel.onMouseDownBindLeft);
- panel.$.controlRight.removeEventListener('mousedown', panel.onMouseDownBindRight);
- panel.$.clipFrom.removeEventListener('confirm', panel.onCutClipBind);
- panel.$.clipTo.removeEventListener('confirm', panel.onCutClipBind);
- panel.$.clipFPS.removeEventListener('confirm', panel.onFpsChangeBind);
- panel.$.wrapMode.removeEventListener('confirm', panel.onWrapModeChangeBind);
- panel.$.speed.removeEventListener('confirm', panel.onSpeedChangeBind);
- panel.$.additiveEnabled.removeEventListener('confirm', panel.onAdditiveEnabledChangedBind);
- panel.$.refClip.removeEventListener('confirm', panel.onRefClipChangedBind);
- },
- update() {
- const panel = this;
- panel.updateRawClipInfo();
- panel.updateCurrentClipInfo();
- panel.updateGridConfig();
- if (!panel.currentClipInfo) {
- panel.$.editor.style.display = 'none';
- return;
- } else {
- panel.$.editor.style.display = 'block';
- }
- updateElementReadonly.call(panel, panel.$.editor);
- panel.$.clipName.value = panel.currentClipInfo.name;
- // ruler making
- panel.$.rulerMaking.innerText = '';
- const maxNum = panel.gridConfig.mod + 1;
- for (let minNum = 1; minNum <= maxNum; minNum++) {
- // If the remaining cells are less than 1.5, hide the label
- if (((panel.gridConfig.width / panel.gridConfig.spacing) % 5) >= 1.5 || minNum !== maxNum) {
- const label = document.createElement('div');
- label.setAttribute('class', 'label-item');
- label.style.left = `${panel.gridConfig.spacing * 5 * (minNum - 1) - 6}px`;
- panel.$.rulerMaking.appendChild(label);
- const span = document.createElement('span');
- span.setAttribute('class', 'mid-label');
- span.innerText = (panel.gridConfig.labelStep * (minNum - 1)).toFixed(3);
- label.appendChild(span);
- }
- }
- const lastMakingLabel = document.createElement('div');
- lastMakingLabel.setAttribute('class', 'label-item');
- lastMakingLabel.style.left = `${panel.gridConfig.width}px`;
- panel.$.rulerMaking.appendChild(lastMakingLabel);
- lastMakingLabel.innerText = panel.rawClipInfo.duration.toFixed(3);
- // ruler gear
- panel.$.rulerGear.innerText = '';
- Object.assign(panel.$.rulerGear.style, {
- 'margin-left': `${panel.gridConfig.spacing}px`,
- });
- const firstRulerGear = document.createElement('div');
- firstRulerGear.setAttribute('class', 'start');
- firstRulerGear.style.left = `${0 - panel.gridConfig.spacing}px`;
- firstRulerGear.innerHTML = '<span class="mid-grid"></span>';
- panel.$.rulerGear.appendChild(firstRulerGear);
- for (let minNum = 0; minNum < panel.gridConfig.mod; minNum++) {
- const item = document.createElement('div');
- item.setAttribute('class', 'grid-item');
- item.style.width = `${panel.gridConfig.spacing * 5}px`;
- const lines = Array(4).fill('<span class="sm-grid"></span>');
- lines.push('<span class="mid-grid"></span>');
- item.innerHTML = lines.join('');
- panel.$.rulerGear.appendChild(item);
- }
- const lastGearItem = document.createElement('div');
- lastGearItem.setAttribute('class', 'grid-item');
- lastGearItem.style.width = `${panel.gridConfig.rest * panel.gridConfig.spacing}px`;
- lastGearItem.innerHTML = Array(panel.gridConfig.rest).fill('<span class="sm-grid"></span>').join('');
- panel.$.rulerGear.appendChild(lastGearItem);
- // control-wrap
- Object.assign(panel.$.controlDuration.style, panel.currentClipInfo.durationStyle);
- Object.assign(panel.$.controlLeft.style, panel.currentClipInfo.ctrlStartStyle);
- Object.assign(panel.$.controlRight.style, panel.currentClipInfo.ctrlEndStyle);
- },
- },
- mountAllAnimationsOnPrefab: {
- ready() {
- const panel = this;
- panel.$.importAllAnimationsCheckbox.addEventListener('change', panel.setProp.bind(panel, 'mountAllAnimationsOnPrefab', 'boolean'));
- panel.$.importAllAnimationsCheckbox.addEventListener('confirm', () => {
- panel.dispatch('snapshot');
- });
- },
- update() {
- const panel = this;
- if (!panel.animationInfos) {
- panel.$.importAllAnimatorWrap.style.display = 'none';
- return;
- } else {
- panel.$.importAllAnimatorWrap.style.display = 'block';
- }
- panel.$.importAllAnimationsCheckbox.value = getPropValue.call(panel, panel.meta.userData.mountAllAnimationsOnPrefab, true);
- updateElementInvalid.call(panel, panel.$.importAllAnimationsCheckbox, 'mountAllAnimationsOnPrefab');
- updateElementReadonly.call(panel, panel.$.importAllAnimationsCheckbox);
- },
- },
- };
- async function callModelPreviewFunction(funcName, ...args) {
- return await Editor.Message.request('scene', 'call-preview-function', 'scene:model-preview', funcName, ...args);
- }
- exports.methods = {
- /** animation name -> uuid */
- initAnimationNameToUUIDMap() {
- if (this.meta && this.meta.subMetas) {
- const animationNameToUUIDMap = new Map();
- Object.keys(this.meta.subMetas).forEach((id) => {
- const subMeta = this.meta.subMetas[id];
- if (subMeta.importer === 'gltf-animation') {
- const sourceName = subMeta.name;
- const animName = sourceName.slice(0, sourceName.lastIndexOf('.'));
- animationNameToUUIDMap.set(animName, subMeta.uuid);
- }
- });
- this.animationNameToUUIDMap = animationNameToUUIDMap;
- }
- },
- initAnimationInfos() {
- if (this.meta && this.meta.userData.animationImportSettings) {
- this.animationInfos = this.meta.userData.animationImportSettings;
- // Collect clip names for renaming and creating to determine whether the name is repeated
- this.clipNames = new Set();
- for (const animationInfo of this.animationInfos) {
- this.clipNames.add(animationInfo.name);
- for (const subAnimInfo of animationInfo.splits) {
- this.clipNames.add(subAnimInfo.name);
- }
- }
- } else {
- this.animationInfos = null;
- }
- },
- onSelect(rawClipIndex, splitClipIndex) {
- this.rawClipIndex = rawClipIndex;
- this.splitClipIndex = splitClipIndex;
- const isElementSelect = (element) => element.getAttribute('rawClipIndex') == rawClipIndex && element.getAttribute('splitClipIndex') == splitClipIndex;
- Elements.editor.update.call(this);
- this.$.clips.querySelectorAll('.line').forEach((child) => {
- if (isElementSelect(child)) {
- child.setAttribute('active', true);
- } else {
- child.removeAttribute('active');
- }
- });
- const curClipInfo = this.getCurClipInfo();
- Editor.Message.broadcast('fbx-inspector:animation-change', curClipInfo);
- },
- getCurClipInfo() {
- if (!this.animationInfos) {
- return null;
- }
- const animInfo = this.animationInfos[this.rawClipIndex];
- const splitInfo = animInfo.splits[this.splitClipIndex];
- if (!animInfo) {
- return null;
- }
- if (!splitInfo) {
- return null;
- }
- const rawClipUUID = this.animationNameToUUIDMap.get(animInfo.name);
- const clipUUID = this.animationNameToUUIDMap.get(splitInfo.name);
- let duration = animInfo.duration;
- let fps = animInfo.fps;
- let from = 0;
- let to = duration;
- if (splitInfo) {
- from = splitInfo.from;
- to = splitInfo.to;
- duration = to - from;
- if (splitInfo.fps !== undefined) {
- fps = splitInfo.fps;
- }
- // if (this.animationNameToUUIDMap.has(splitInfo.name)) {
- // clipUUID = this.animationNameToUUIDMap.get(splitInfo.name);
- // }
- }
- return {
- rawClipUUID,
- rawClipIndex: this.rawClipIndex,
- clipUUID,
- duration,
- fps,
- from,
- to,
- wrapMode: splitInfo.wrapMode,
- speed: splitInfo.speed || 1,
- additiveEnabled: splitInfo.additive?.enabled ?? false,
- refClip: splitInfo.additive?.refClip || '',
- };
- },
- getRightName(name) {
- if (!name) {
- return null;
- }
- const panel = this;
- do {
- const result = name.match(/(.*)_(\d{0,3})/);
- if (result) {
- name = `${result[1]}_${Number(result[2]) + 1}`;
- } else {
- name += '_1';
- }
- } while (panel.clipNames.has(name));
- return name;
- },
- newClipTemplate() {
- const panel = this;
- // Verify the name
- return {
- name: panel.getRightName(panel.rawClipInfo.name),
- from: 0,
- to: panel.rawClipInfo.duration,
- wrapMode: 2 /* Loop */,
- speed: 1,
- };
- },
- updateCurrentClipInfo() {
- const panel = this;
- if (!panel.animationInfos) {
- panel.currentClipInfo = null;
- return;
- }
- const info = panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex];
- if (!info || !panel.gridTableWith) {
- panel.currentClipInfo = null;
- return;
- }
- const duration = info.to - info.from;
- const ctrlStart = (info.from / panel.rawClipInfo.duration) * panel.gridTableWith;
- const ctrlEnd = (info.to / panel.rawClipInfo.duration) * panel.gridTableWith;
- const durationWidth = (duration / panel.rawClipInfo.duration) * panel.gridTableWith;
- const fps = info.fps !== undefined ? info.fps : panel.rawClipInfo.fps;
- const wrapMode = info.wrapMode ?? panel.rawClipInfo.wrapMode;
- const speed = info.speed ?? panel.rawClipInfo.speed;
- const additiveEnabled = (info.additive?.enabled) ?? panel.rawClipInfo.additiveEnabled;
- const refClip = (info.additive?.refClip) ?? panel.rawClipInfo.refClip;
- panel.currentClipInfo = {
- name: info.name,
- from: info.from * fps,
- to: info.to * fps,
- ctrlStart,
- ctrlEnd,
- ctrlStartStyle: {
- transform: `translateX(${ctrlStart - 6}px)`,
- },
- ctrlEndStyle: {
- transform: `translateX(${ctrlEnd}px)`,
- },
- durationStyle: {
- width: `${durationWidth}px`,
- transform: `translateX(${ctrlStart}px)`,
- },
- duration,
- fps,
- wrapMode,
- speed,
- additiveEnabled,
- refClip,
- };
- const maxFrames = (panel.rawClipInfo.duration * panel.currentClipInfo.fps).toFixed(0);
- const startFrames = panel.currentClipInfo.from.toFixed(0);
- const endFrames = panel.currentClipInfo.to.toFixed(0);
- panel.$.clipFrames.innerText = maxFrames;
- panel.$.clipFPS.value = fps;
- // TODO: hack for bug at 3d-tasks#10113. Because the new value would be limited in min and max, should firstly remove min and max.
- panel.$.clipFrom.max = null;
- panel.$.clipTo.min = null;
- panel.$.clipTo.max = null;
- panel.$.clipFrom.value = startFrames;
- panel.$.clipFrom.setAttribute('max', endFrames);
- panel.$.clipTo.value = endFrames;
- panel.$.clipTo.setAttribute('min', startFrames);
- panel.$.clipTo.setAttribute('max', maxFrames);
- panel.$.wrapMode.value = panel.currentClipInfo.wrapMode;
- panel.$.speed.value = panel.currentClipInfo.speed || 1;
- panel.$.additiveEnabled.value = panel.currentClipInfo.additiveEnabled || false;
- panel.$.refClip.value = panel.currentClipInfo.refClip || '';
- },
- updateRawClipInfo() {
- const panel = this;
- if (!panel.animationInfos) {
- panel.rawClipInfo = null;
- return;
- }
- if (!panel.animationInfos[panel.rawClipIndex]) {
- panel.rawClipInfo = null;
- return;
- }
- const { name, duration, fps } = panel.animationInfos[panel.rawClipIndex];
- panel.rawClipInfo = { name, duration, fps };
- panel.$.clipDuration.innerText = duration.toFixed(3);
- },
- updateGridConfig() {
- const panel = this;
- if (!panel.currentClipInfo) {
- return null;
- }
- let width = panel.gridTableWith;
- const info = panel.rawClipInfo;
- const { step, spacing } = panel.getStepAndSpacing(width, panel.currentClipInfo.fps * info.duration);
- width = (panel.currentClipInfo.fps * info.duration * spacing) / step;
- const mod = Math.floor(panel.gridTableWith / (spacing * 5));
- const rest = Math.floor((panel.gridTableWith % (spacing * 5)) / spacing);
- const labelStep = info.duration * ((5 * step) / (panel.currentClipInfo.fps * info.duration));
- panel.gridConfig = {
- step,
- spacing,
- mod,
- rest,
- width,
- labelStep,
- };
- },
- getStepAndSpacing(width, frames) {
- const config = {
- minSpacing: 10,
- maxSpacing: 20,
- };
- const rawMinSpacing = width / frames;
- let spacing = rawMinSpacing;
- let step = 1;
- if (rawMinSpacing < config.minSpacing) {
- // Calculates a minimum spacing value that is a multiple of maxSpacing
- step = Math.ceil(config.minSpacing / rawMinSpacing);
- spacing = rawMinSpacing * step;
- }
- return {
- step,
- spacing,
- };
- },
- onMouseDown(type) {
- const panel = this;
- const info = panel.currentClipInfo;
- if (!info) {
- return;
- }
- panel.virtualControl = { type };
- if (type === 'right') {
- panel.virtualControl.style = info.ctrlEndStyle;
- panel.virtualControl.value = info.to / panel.currentClipInfo.fps;
- } else {
- panel.virtualControl.style = info.ctrlStartStyle;
- panel.virtualControl.value = info.from / panel.currentClipInfo.fps;
- }
- panel.$.controlVirtual.setAttribute('direction', panel.virtualControl.type);
- panel.$.controlVirtual.style.display = 'block';
- panel.updateVirtualControl();
- panel.onMouseMoveBind = panel.onMouseMove.bind(panel);
- panel.onMouseUpBind = panel.onMouseUp.bind(panel);
- document.addEventListener('mousemove', panel.onMouseMoveBind);
- document.addEventListener('mouseup', panel.onMouseUpBind);
- },
- onMouseMove(event) {
- const panel = this;
- event.preventDefault();
- if (!panel.virtualControl) {
- return;
- }
- // beyond border
- const { type } = panel.virtualControl;
- let x = event.x - panel.$.rulerMaking.getBoundingClientRect().x;
- if (
- x > panel.gridConfig.width
- || x < 0
- || (type === 'left' && x > panel.currentClipInfo.ctrlEnd)
- || (type === 'right' && x < panel.currentClipInfo.ctrlStart)
- ) {
- return;
- }
- const { duration } = panel.rawClipInfo;
- const value = (x / panel.gridConfig.width) * duration;
- const currentTime = Editor.Utils.Math.clamp(value, 0, duration);
- const currentFrame = parseInt((currentTime * panel.currentClipInfo.fps).toFixed(0));
- panel.virtualControl.value = currentTime;
- if (type === 'left') {
- panel.virtualControl.startFrame = currentFrame;
- x -= 6;
- } else {
- panel.virtualControl.endFrame = currentFrame;
- }
- panel.virtualControl.style = {
- transform: `translateX(${x}px)`,
- };
- cancelAnimationFrame(panel.animationId);
- panel.animationId = requestAnimationFrame(() => {
- panel.updateVirtualControl();
- });
- },
- onMouseUp() {
- const panel = this;
- if (!panel.virtualControl) {
- return;
- }
- document.removeEventListener('mousemove', panel.onMouseMoveBind);
- document.removeEventListener('mouseup', panel.onMouseUpBind);
- const { value } = panel.virtualControl;
- let { type } = panel.virtualControl;
- type = type === 'right' ? 'to' : 'from';
- // refresh data
- const splitInfo = panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex];
- if (splitInfo[type] !== value) {
- const { duration } = panel.rawClipInfo;
- splitInfo[type] = Editor.Utils.Math.clamp(value, 0, duration);
- }
- Elements.clips.update.call(panel);
- Object.assign(panel.$.controlDuration.style, panel.currentClipInfo.durationStyle);
- Object.assign(panel.$.controlLeft.style, panel.currentClipInfo.ctrlStartStyle);
- Object.assign(panel.$.controlRight.style, panel.currentClipInfo.ctrlEndStyle);
- panel.$.controlVirtual.style.display = 'none';
- const curClipInfo = panel.getCurClipInfo();
- Editor.Message.broadcast('fbx-inspector:animation-change', curClipInfo);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- updateVirtualControl() {
- const panel = this;
- Object.assign(panel.$.controlVirtual.style, panel.virtualControl.style);
- panel.$.controlVirtualNumber.innerText = panel.virtualControl.value.toFixed(3);
- if (panel.virtualControl.startFrame || panel.virtualControl.endFrame) {
- if (panel.virtualControl.type === 'left') {
- panel.$.clipFrom.value = panel.virtualControl.startFrame;
- } else {
- panel.$.clipTo.value = panel.virtualControl.endFrame;
- }
- }
- },
- onClipName(event) {
- const panel = this;
- if (!panel.currentClipInfo) {
- return;
- }
- let name = event.target.value;
- if (!name) {
- return;
- }
- if (panel.clipNames.has(name)) {
- name = panel.getRightName(name);
- }
- const info = panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex];
- if (info.name === name) {
- return;
- }
- panel.clipNames.delete(info.name);
- info.name = name;
- panel.clipNames.add(name);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- Elements.clips.update.call(panel);
- },
- onCutClip(event) {
- const panel = this;
- const path = event.target.getAttribute('path');
- panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex][path] = event.target.value / panel.currentClipInfo.fps;
- Elements.clips.update.call(panel);
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- onFpsChange(event) {
- const panel = this;
- panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex].fps = Number(event.target.value);
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- onWrapModeChange(event) {
- const panel = this;
- const wrapMode = Number(event.target.value);
- panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex].wrapMode = wrapMode;
- callModelPreviewFunction(
- 'setClipConfig',
- {
- wrapMode,
- }
- );
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- onSpeedChange(event) {
- const panel = this;
- const speed = Number(event.target.value);
- panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex].speed = speed;
- callModelPreviewFunction(
- 'setClipConfig',
- {
- speed,
- }
- );
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- onAdditiveEnabledChanged(event) {
- const panel = this;
- const enabled = Boolean(event.target.value);
- (panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex].additive ??= { enabled: false }).enabled = enabled;
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- onRefClipChanged(event) {
- const panel = this;
- const refClipUUID = String(event.target.value);
- (panel.animationInfos[panel.rawClipIndex].splits[panel.splitClipIndex].additive ??= { enabled: false }).refClip = refClipUUID;
- Elements.editor.update.call(panel);
- panel.dispatch('change');
- panel.dispatch('snapshot');
- },
- setProp(prop, type, event) {
- setPropValue.call(this, prop, type, event);
- this.dispatch('change');
- this.dispatch('track', { tab: 'animation', prop, value: event.target.value });
- },
- };
- exports.ready = function() {
- 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];
- if (assetList.length > 1) {
- this.$.container.setAttribute('multiple-invalid', '');
- return;
- } else {
- this.$.container.removeAttribute('multiple-invalid');
- }
- for (const prop in Elements) {
- const element = Elements[prop];
- if (element.update) {
- element.update.call(this);
- }
- }
- this.initAnimationNameToUUIDMap();
- this.initAnimationInfos();
- if (this.animationInfos) {
- this.onSelect(this.rawClipIndex, this.splitClipIndex);
- }
- };
- exports.close = function() {
- for (const prop in Elements) {
- const element = Elements[prop];
- if (element.close) {
- element.close.call(this);
- }
- }
- };
|