mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-21 01:13:37 +08:00
Animation retargeting WIP
Unused animator validator check
This commit is contained in:
parent
e89e6549d7
commit
954e7d7658
@ -467,6 +467,7 @@ class Animation extends AnimationItem {
|
||||
Animator.preview();
|
||||
updateInterface();
|
||||
}
|
||||
Blockbench.dispatchEvent('select_animation', {animation: this})
|
||||
return this;
|
||||
}
|
||||
setLength(len = this.length) {
|
||||
@ -975,6 +976,49 @@ Blockbench.addDragHandler('animation', {
|
||||
}
|
||||
})
|
||||
|
||||
new ValidatorCheck('unused_animators', {
|
||||
condition: { features: ['animation_mode'], selected: {animation: true} },
|
||||
update_triggers: ['select_animation'],
|
||||
run() {
|
||||
let animation = Animation.selected;
|
||||
if (!animation) return;
|
||||
let animators = [];
|
||||
for (let id in animation.animators) {
|
||||
let animator = animation.animators[id];
|
||||
if (animator instanceof BoneAnimator && animator.keyframes.length) {
|
||||
if (!animator.getGroup()) {
|
||||
animators.push(animator);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (animators.length) {
|
||||
let buttons = [
|
||||
{
|
||||
name: 'Retarget Animators',
|
||||
icon: 'rebase',
|
||||
click() {
|
||||
Validator.dialog.close()
|
||||
BarItems.retarget_animators.click();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Reveal in Timeline',
|
||||
icon: 'fa-sort-amount-up',
|
||||
click() {
|
||||
for (let animator of animators) {
|
||||
animator.addToTimeline();
|
||||
}
|
||||
Validator.dialog.close();
|
||||
},
|
||||
}
|
||||
];
|
||||
this.warn({
|
||||
message: `The animation "${animation.name}" contains ${animators.length} animated nodes that do not exist in the current model.`,
|
||||
buttons,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
BARS.defineActions(function() {
|
||||
new NumSlider('slider_animation_length', {
|
||||
@ -1566,6 +1610,87 @@ BARS.defineActions(function() {
|
||||
}
|
||||
}
|
||||
})
|
||||
new Action('retarget_animators', {
|
||||
icon: 'rebase',
|
||||
category: 'animation',
|
||||
condition: () => Animation.selected,
|
||||
click: async function() {
|
||||
let animation = Animation.selected;
|
||||
let form = {};
|
||||
let unassigned_animators = [];
|
||||
let assigned_animators = [];
|
||||
|
||||
for (let id in animation.animators) {
|
||||
let animator = animation.animators[id];
|
||||
if (animator instanceof BoneAnimator && animator.keyframes.length) {
|
||||
if (!animator.getGroup()) {
|
||||
unassigned_animators.push(animator);
|
||||
} else {
|
||||
assigned_animators.push(animator);
|
||||
}
|
||||
}
|
||||
}
|
||||
let all_animators = unassigned_animators.slice();
|
||||
if (unassigned_animators.length && assigned_animators.length) {
|
||||
all_animators.push('_');
|
||||
}
|
||||
all_animators.push(...assigned_animators);
|
||||
|
||||
for (let animator of all_animators) {
|
||||
if (animator == '_') {
|
||||
form._ = '_';
|
||||
continue;
|
||||
}
|
||||
let is_assigned = assigned_animators.includes(animator);
|
||||
let options = {};
|
||||
let nodes;
|
||||
if (animator.type == 'bone') {
|
||||
nodes = Group.all;
|
||||
} else {
|
||||
nodes = Outliner.all.filter(element => element.type == animator.type);
|
||||
}
|
||||
if (!is_assigned) options[animator.uuid] = '-';
|
||||
for (let node of nodes) {
|
||||
options[node.uuid] = node.name;
|
||||
}
|
||||
form[animator.uuid] = {
|
||||
label: animator.name,
|
||||
type: 'select',
|
||||
value: animator.uuid,
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
let form_result = await new Promise(resolve => {
|
||||
|
||||
new Dialog('retarget_animators', {
|
||||
name: 'action.retarget_animators',
|
||||
form,
|
||||
onConfirm(result) {
|
||||
resolve(result);
|
||||
},
|
||||
onCancel() {
|
||||
resolve(false);
|
||||
}
|
||||
}).show();
|
||||
})
|
||||
if (!form_result) return;
|
||||
Undo.initEdit({animations: [animation]});
|
||||
|
||||
for (let animator of all_animators) {
|
||||
if (animator == '_') continue;
|
||||
|
||||
let new_target = form_result[animator.uuid];
|
||||
if (new_target == animator.uuid) continue;
|
||||
|
||||
delete animation.animators[animator.uuid];
|
||||
animation.animators[new_target] = animator;
|
||||
animator.uuid = new_target;
|
||||
}
|
||||
|
||||
Undo.finishEdit('Retarget animations');
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
@ -4,7 +4,14 @@ const Validator = {
|
||||
warnings: [],
|
||||
errors: [],
|
||||
_timeout: null,
|
||||
accumulated_triggers: [],
|
||||
wildcard_trigger: false,
|
||||
validate(trigger) {
|
||||
if (trigger) {
|
||||
this.accumulated_triggers.safePush(trigger);
|
||||
} else {
|
||||
this.wildcard_trigger = true;
|
||||
}
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = null;
|
||||
@ -20,7 +27,7 @@ const Validator = {
|
||||
try {
|
||||
if (!Condition(check.condition)) return;
|
||||
|
||||
if (!trigger || check.update_triggers.includes(trigger)) {
|
||||
if (this.wildcard_trigger || check.update_triggers.includes(trigger) || this.accumulated_triggers.find(t => check.update_triggers.includes(t))) {
|
||||
check.update();
|
||||
}
|
||||
Validator.warnings.push(...check.warnings);
|
||||
@ -30,6 +37,8 @@ const Validator = {
|
||||
console.error(error);
|
||||
}
|
||||
})
|
||||
this.accumulated_triggers.empty();
|
||||
this.wildcard_trigger = false;
|
||||
}, 40)
|
||||
},
|
||||
openDialog() {
|
||||
|
@ -1779,6 +1779,8 @@
|
||||
"action.export_animation_file.desc": "Export a selection of animations into a new file",
|
||||
"action.optimize_animation": "Optimize Animation",
|
||||
"action.optimize_animation.desc": "Optimize the current animation and reduce the keyframe count",
|
||||
"action.retarget_animators": "Retarget Animations",
|
||||
"action.retarget_animators.desc": "Select which animator targets which bone or element",
|
||||
"action.merge_animation": "Merge Animation",
|
||||
"action.merge_animation.desc": "Optimize the current animation and reduce the keyframe count",
|
||||
"action.bake_ik_animation": "Bake Inverse Kinematics",
|
||||
|
Loading…
Reference in New Issue
Block a user