Implement animation merging

This commit is contained in:
JannisX11 2024-07-02 21:39:11 +02:00
parent 64c5d51fd7
commit e68783eda0
2 changed files with 49 additions and 11 deletions

View File

@ -1274,14 +1274,14 @@ BARS.defineActions(function() {
let options = await new Promise(resolve => {
let animation_options = {};
for (let animation of animations) {
for (let animation of Animation.all) {
if (animation == source_animation) continue;
animation_options[animation.uuid] = animation.name;
}
new Dialog('merge_animation', {
name: 'action.merge_animation',
form: {
animation: {label: 'data.animation', type: 'select', options: animation_options},
animation: {label: 'dialog.merge_animation.merge_target', type: 'select', options: animation_options},
},
onConfirm(result) {
resolve(result);
@ -1300,6 +1300,7 @@ BARS.defineActions(function() {
for (let uuid in source_animation.animators) {
let source_animator = source_animation.animators[uuid];
// Get target animator
let target_animator;
if (source_animator instanceof BoneAnimator) {
let node = source_animator.getElement ? source_animator.getElement() : source_animator.getGroup();
@ -1311,9 +1312,21 @@ BARS.defineActions(function() {
target_animator = target_animation.animators.effects;
}
for (let channel in source_animator.channels) {
let channel_config = source_animator.channels[channel];
let source_kfs = source_animator[channel];
let target_kfs = target_animator[channel];
if (source_kfs.length == 0) {
continue;
} else if (target_kfs.length == 0) {
for (let src_kf of source_kfs) {
target_animator.createKeyframe(src_kf, src_kf.time, channel, false, false);
}
continue;
}
let timecodes = {};
// Save base values
for (let kf of source_kfs) {
let key = Math.roundTo(kf.time, 2);
if (!timecodes[key]) timecodes[key] = {};
@ -1326,6 +1339,19 @@ BARS.defineActions(function() {
timecodes[key].target_kf = kf;
timecodes[key].time = kf.time;
}
if (source_animator.interpolate) {
// Interpolate in between values before they become affected by changes
for (let key in timecodes) {
let data = timecodes[key];
Timeline.time = data.time;
if (!data.target_kf) {
data.target_values = target_animator.interpolate(channel, true);
}
if (!data.source_kf) {
data.source_values = source_animator.interpolate(channel, true);
}
}
}
function mergeValues(a, b) {
if (!a) return b;
if (!b) return a;
@ -1336,9 +1362,9 @@ BARS.defineActions(function() {
}
let keys = Object.keys(timecodes).sort((a, b) => a.time - b.time);
for (let key of keys) {
let {source_kf, target_kf, time} = timecodes[key];
let {source_kf, target_kf, target_values, source_values, time} = timecodes[key];
Timeline.time = time;
if (kf.transform) {
if ((source_kf || target_kf).transform) {
if (source_kf && target_kf) {
for (let axis of 'xyz') {
let source_val = source_kf.get(axis);
@ -1346,22 +1372,19 @@ BARS.defineActions(function() {
target_kf.set(axis, mergeValues(target_val, source_val));
}
} else if (source_kf) {
//let interpolated = target_animator.interpolate(channel, true);
// Todo: At time of creatio, this might already be affected by modified keyframes around it. Interpolate values before!
let target_kf = target_animator.createKeyframe(null, time, channel, false, false);
let i = 0;
for (let axis of 'xyz') {
let source_val = source_kf.get(axis);
let target_val = target_kf.get(axis);
let target_val = target_values[i] ?? 0;
target_kf.set(axis, mergeValues(target_val, source_val));
i++;
}
} else if (target_kf) {
let interpolated = this.interpolate(channel, true);
let i = 0;
for (let axis of 'xyz') {
let source_val = source_kf.get(axis);
let source_val = source_values[i] ?? 0;
let target_val = target_kf.get(axis);
target_kf.set(axis, mergeValues(target_val, source_val));
i++;
@ -1369,10 +1392,21 @@ BARS.defineActions(function() {
}
} else if (source_animator instanceof EffectAnimator) {
if (source_kf && target_kf) {
if (channel == 'timeline' ) {
let source = source_kf.data_points[0].script;
let target = target_kf.data_points[0].script;
target_kf.data_points[0].script = (source && target) ? (target + '\n' + source) : (source || target);
} else if (channel_config?.max_data_points > 1) {
for (let src_kfdp of source_kf.data_points) {
let new_dp = new KeyframeDataPoint(target_kf);
new_dp.extend(src_kfdp);
target_kf.data_points.push(new_dp);
}
}
} else if (source_kf) {
} else if (target_kf) {
let new_kf = target_animator.createKeyframe(source_kf, source_kf.time, source_kf.channel, false, false);
Property.resetUniqueValues(Keyframe, new_kf);
}
}
}

View File

@ -27,6 +27,7 @@
"data.group": "Group",
"data.texture": "Texture",
"data.texture_group": "Texture Group",
"data.animation": "Animation",
"data.layer": "Layer",
"data.origin": "Pivot",
"data.plugin": "Plugin",
@ -613,6 +614,7 @@
"dialog.optimize_animation.selection.selected_animation": "Selected Animation",
"dialog.optimize_animation.selection.all_animations": "All Animations",
"dialog.optimize_animation.thresholds": "Threshold values",
"dialog.merge_animation.merge_target": "Merge into",
"dialog.create_texture.folder": "Folder",
"dialog.create_texture.type": "Type",
@ -1774,6 +1776,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.merge_animation": "Merge Animation",
"action.merge_animation.desc": "Optimize the current animation and reduce the keyframe count",
"action.bake_ik_animation": "Bake Inverse Kinematics",
"action.bake_ik_animation.desc": "Bake the rotations applied by inverse kinematics into the selected animation",
"action.bake_animation_into_model": "Bake Animation into Model",