Add custom animation channel API

Show Sketchfab upload error code
Creating a keyframe in the grapheditor now uses the grapheditor channel
This commit is contained in:
JannisX11 2021-11-19 19:36:19 +01:00
parent cddb23fb5f
commit 524cc5f790
5 changed files with 86 additions and 40 deletions

View File

@ -61,9 +61,9 @@ class Animation {
}
} else {
animator = this.animators[key];
animator.channels.forEach(channel => {
for (let channel in animator.channels) {
animator[channel].empty()
})
}
}
if (kfs && animator) {
kfs.forEach(kf_data => {
@ -697,9 +697,16 @@ class GeneralAnimator {
this.selected = false;
this.uuid = uuid || guid();
this.muted = {};
this.channels.forEach(channel => {
for (let channel in this.channels) {
this.muted[channel] = false;
})
}
}
get keyframes() {
let array = [];
for (let channel in this.channels) {
array.push(...this[channel]);
}
return array;
}
select() {
var scope = this;
@ -718,12 +725,15 @@ class GeneralAnimator {
if (!Timeline.animators.includes(this)) {
Timeline.animators.splice(0, 0, this);
}
for (let channel in this.channels) {
if (!this[channel]) this[channel] = [];
}
if (!this.expanded) this.expanded = true;
return this;
}
addKeyframe(data, uuid) {
var channel = data.channel;
if (typeof channel == 'number') channel = this.channels[channel];
if (typeof channel == 'number') channel = Object.keys(this.channels)[channel];
if (channel && this[channel]) {
var kf = new Keyframe(data, uuid);
this[channel].push(kf);
@ -746,7 +756,7 @@ class GeneralAnimator {
if (value) {
keyframe.extend(value);
} else if (this.fillValues) {
} else if (this.transform && this.fillValues) {
this.fillValues(keyframe, value, true);
}
@ -807,6 +817,20 @@ class GeneralAnimator {
}
}
}
GeneralAnimator.addChannel = function(channel, options) {
this.prototype.channels[channel] = {
name: options.name || channel,
transform: options.transform || false,
mutable: options.mutable instanceof Boolean ? options.mutable : true,
max_data_points: options.max_data_points || 0
}
Timeline.animators.forEach(animator => {
if (animator instanceof this && !animator[channel]) {
animator[channel] = [];
}
})
Timeline.vue.$forceUpdate();
}
class BoneAnimator extends GeneralAnimator {
constructor(uuid, animation, name) {
super(uuid, animation);
@ -825,9 +849,6 @@ class BoneAnimator extends GeneralAnimator {
set name(name) {
this._name = name;
}
get keyframes() {
return [...this.rotation, ...this.position, ...this.scale];
}
getGroup() {
this.group = OutlinerNode.uuids[this.uuid];
if (!this.group) {
@ -1071,7 +1092,11 @@ class BoneAnimator extends GeneralAnimator {
if (!this.muted.scale) this.displayScale(this.interpolate('scale'), multiplier)
}
}
BoneAnimator.prototype.channels = ['rotation', 'position', 'scale']
BoneAnimator.prototype.channels = {
rotation: {name: tl('timeline.rotation'), mutable: true, transform: true, max_data_points: 2},
position: {name: tl('timeline.position'), mutable: true, transform: true, max_data_points: 2},
scale: {name: tl('timeline.scale'), mutable: true, transform: true, max_data_points: 2},
}
class EffectAnimator extends GeneralAnimator {
constructor(animation) {
super(null, animation);
@ -1079,12 +1104,9 @@ class EffectAnimator extends GeneralAnimator {
this.name = tl('timeline.effects')
this.selected = false;
this.particle = [];
this.sound = [];
this.timeline = [];
}
get keyframes() {
return [...this.particle, ...this.sound, ...this.timeline];
for (let channel in this.channels) {
this[channel] = [];
}
}
pushKeyframe(keyframe) {
this[keyframe.channel].push(keyframe)
@ -1171,7 +1193,11 @@ class EffectAnimator extends GeneralAnimator {
}
}
}
EffectAnimator.prototype.channels = ['particle', 'sound', 'timeline']
EffectAnimator.prototype.channels = {
particle: {name: tl('timeline.particle'), mutable: true, max_data_points: 1000},
sound: {name: tl('timeline.sound'), mutable: true, max_data_points: 1000},
timeline: {name: tl('timeline.timeline'), mutable: false, max_data_points: 1},
}
//Clipbench
Object.assign(Clipbench, {
@ -1273,14 +1299,23 @@ WinterskyScene.global_options.parent_mode = 'entity';
const Animator = {
possible_channels: {rotation: true, position: true, scale: true, sound: true, particle: true, timeline: true},
get possible_channels() {
let obj = {};
Object.assign(obj, BoneAnimator.prototype.channels, EffectAnimator.prototype.channels);
return obj;
},
open: false,
get animations() {return Animation.all},
get selected() {return Animation.selected},
MolangParser: new Molang(),
motion_trail: new THREE.Object3D(),
motion_trail_lock: false,
_last_values: {rotation: [0, 0, 0], position: [0, 0, 0], scale: [0, 0, 0]},
_last_values: {},
resetLastValues() {
for (let channel in BoneAnimator.prototype.channels) {
if (BoneAnimator.prototype.channels[channel].transform) Animator._last_values[channel] = [0, 0, 0];
}
},
join() {
if (isApp && (Format.id == 'bedrock' || Format.id == 'bedrock_old') && !Project.BedrockEntityManager.initialized_animations) {
@ -1451,7 +1486,7 @@ const Animator = {
// Bones
Animator.showDefaultPose(true);
Group.all.forEach(group => {
Animator._last_values = {rotation: [0, 0, 0], position: [0, 0, 0], scale: [0, 0, 0]}
Animator.resetLastValues();
Animator.animations.forEach(animation => {
let multiplier = animation.blend_weight ? Math.clamp(Animator.MolangParser.parse(animation.blend_weight), 0, Infinity) : 1;
if (animation.playing) {
@ -1459,7 +1494,7 @@ const Animator = {
}
})
})
Animator._last_values = {rotation: [0, 0, 0], position: [0, 0, 0], scale: [0, 0, 0]}
Animator.resetLastValues();
scene.updateMatrixWorld()
// Effects
@ -1574,7 +1609,7 @@ const Animator = {
animation.animators[uuid] = ba;
//Channels
for (var channel in b) {
if (Animator.possible_channels[channel]) {
if (BoneAnimator.prototype.channels[channel]) {
if (typeof b[channel] === 'string' || typeof b[channel] === 'number' || b[channel] instanceof Array) {
ba.addKeyframe({
time: 0,

View File

@ -43,7 +43,7 @@ class Keyframe {
if (typeof data === 'object') {
Merge.string(this, data, 'channel')
this.transform = this.channel === 'rotation' || this.channel === 'position' || this.channel === 'scale';
this.transform = !!(BoneAnimator.prototype.channels[this.channel] || EffectAnimator.prototype.channels[this.channel]).transform;
this.data_points.push(new KeyframeDataPoint(this));
this.extend(data)
}
@ -484,10 +484,13 @@ BARS.defineActions(function() {
click: function (event) {
var animator = Timeline.selected_animator;
if (!animator) return;
var channel = animator.channels[0];
if (Toolbox.selected.id == 'rotate_tool' && animator.channels.includes('rotation')) channel = 'rotation';
if (Toolbox.selected.id == 'move_tool' && animator.channels.includes('position')) channel = 'position';
if (Toolbox.selected.id == 'resize_tool' && animator.channels.includes('scale')) channel = 'scale';
var channel = Object.keys(animator.channels)[0];
if (Toolbox.selected.id == 'rotate_tool' && animator.channels['rotation']) channel = 'rotation';
if (Toolbox.selected.id == 'move_tool' && animator.channels['position']) channel = 'position';
if (Toolbox.selected.id == 'resize_tool' && animator.channels['scale']) channel = 'scale';
if (Timeline.vue.graph_editor_open && Prop.active_panel == 'timeline' && animator.channels[Timeline.vue.graph_editor_channel]) {
channel = Timeline.vue.graph_editor_channel;
}
animator.createKeyframe((event && (event.shiftKey || Pressing.overrides.shift)) ? {} : null, Timeline.time, channel, true);
if (event && (event.shiftKey || Pressing.overrides.shift)) {
Animator.preview();
@ -520,6 +523,7 @@ BARS.defineActions(function() {
Timeline.selected.forEach((kf) => {
kf.time = Timeline.snapTime(limitNumber(kf.time + Timeline.getStep(), 0, 1e4))
})
Animation.selected.setLength();
Animator.preview()
BarItems.slider_keyframe_time.update()
Undo.finishEdit('Move keyframes forwards')
@ -538,7 +542,7 @@ BARS.defineActions(function() {
let animator = Timeline.selected_animator
|| (Timeline.selected[0] && Timeline.selected[0].animator)
|| Timeline.animators[0];
let channel = Timeline.selected[0] ? Timeline.selected[0].channel : (animator && animator.channels[0]);
let channel = Timeline.selected[0] ? Timeline.selected[0].channel : (animator && Object.keys(animator.channels)[0]);
var matches = []
for (var kf of Timeline.keyframes) {
@ -578,7 +582,7 @@ BARS.defineActions(function() {
let animator = Timeline.selected_animator
|| (Timeline.selected[0] && Timeline.selected[0].animator)
|| Timeline.animators[0];
let channel = Timeline.selected[0] ? Timeline.selected[0].channel : (animator && animator.channels[0]);
let channel = Timeline.selected[0] ? Timeline.selected[0].channel : (animator && Object.keys(animator.channels)[0]);
var matches = []
for (var kf of Timeline.keyframes) {
@ -869,7 +873,7 @@ Interface.definePanels(function() {
addDataPoint() {
Undo.initEdit({keyframes: Timeline.selected})
Timeline.selected.forEach(kf => {
if ((kf.transform && kf.data_points.length <= 1) || kf.channel == 'particle' || kf.channel == 'sound') {
if (kf.data_points.length < kf.animator.channels[kf.channel].max_data_points) {
kf.data_points.push(new KeyframeDataPoint(kf))
kf.data_points.last().extend(kf.data_points[0])
}
@ -927,7 +931,7 @@ Interface.definePanels(function() {
<label>{{ tl('panel.keyframe.type', [getKeyframeInfos()]) }}</label>
<div
class="in_list_button"
v-if="(keyframes[0].transform && keyframes[0].data_points.length <= 1) || channel == 'particle' || channel == 'sound'"
v-if="keyframes[0].animator.channels[channel] && keyframes[0].data_points.length < keyframes[0].animator.channels[channel].max_data_points"
v-on:click.stop="addDataPoint()"
title="${ tl('panel.keyframe.add_data_point') }"
>

View File

@ -129,7 +129,7 @@ const Timeline = {
if (!Timeline.vue.graph_editor_open) {
var channel_index = 0 //animator.channels.indexOf(kf.channel);
for (var channel of animator.channels) {
for (var channel in animator.channels) {
if (kf.channel == channel) break;
if (channels[channel] != false && (!channels.hide_empty || animator[channel].length)) {
channel_index++;
@ -713,6 +713,7 @@ onVueSetup(function() {
},
selectChannel(animator, channel) {
if (this.graph_editor_channel == channel && animator.selected) return;
if (!animator.channels[channel].transform) return;
animator.select();
if (animator[channel].length == 1 && Math.epsilon(animator[channel][0].time, Timeline.time, 0.002)) {
animator[channel][0].select();
@ -961,7 +962,7 @@ onVueSetup(function() {
</div>
</div>
<div class="keyframe_section" v-if="!graph_editor_open">
<template v-for="channel in animator.channels" v-if="!(animator.expanded && channels[channel] != false && (!channels.hide_empty || animator[channel].length))">
<template v-for="(channel_options, channel) in animator.channels" v-if="!(animator.expanded && channels[channel] != false && (!channels.hide_empty || animator[channel].length))">
<div
v-for="keyframe in animator[channel]"
v-bind:style="{left: (8 + keyframe.time * size) + 'px'}"
@ -975,7 +976,7 @@ onVueSetup(function() {
</div>
<div class="animator_channel_bar"
v-bind:style="{width: (size*length + head_width)+'px'}"
v-for="channel in animator.channels"
v-for="(channel_options, channel) in animator.channels"
v-if="animator.expanded && channels[channel] != false && (!channels.hide_empty || animator[channel].length)"
>
<div class="channel_head"
@ -983,17 +984,18 @@ onVueSetup(function() {
v-bind:style="{left: scroll_left+'px', width: head_width+'px'}"
@click.stop="selectChannel(animator, channel);"
>
<div class="text_button" v-on:click.stop="animator.toggleMuted(channel)">
<div class="text_button" v-if="channel_options.mutable" v-on:click.stop="animator.toggleMuted(channel)">
<template v-if="channel === 'sound'">
<i class="channel_mute fas fa-volume-mute" v-if="animator.muted[channel]"></i>
<i class="channel_mute fas fa-volume-up" v-else></i>
</template>
<template v-else-if="channel !== 'timeline'">
<template v-else>
<i class="channel_mute fas fa-eye-slash" v-if="animator.muted[channel]"></i>
<i class="channel_mute fas fa-eye" v-else></i>
</template>
</div>
<span>{{ tl('timeline.'+channel) }}</span>
<div class="text_button" v-else></div>
<span>{{ channel_options.name }}</span>
<div class="text_button" v-on:click.stop="animator.createKeyframe(null, null, channel, true)">
<i class="material-icons">add</i>
</div>
@ -1063,7 +1065,12 @@ BARS.defineActions(function() {
keybind: new Keybind({key: 114}),
onChange(state) {
Timeline.vue.graph_editor_open = state;
if (Timeline.vue.graph_editor_open && Timeline.selected.length) {
if (Timeline.vue.graph_editor_open &&
Timeline.selected.length &&
Timeline.selected_animator &&
Timeline.selected_animator.channels[Timeline.selected[0].channel] &&
Timeline.selected_animator.channels[Timeline.selected[0].channel].transform
) {
Timeline.vue.graph_editor_channel = Timeline.selected[0].channel;
}
}

View File

@ -9,7 +9,7 @@ function buildAnimationTracks() {
let animator = animation.animators[uuid];
if (animator instanceof BoneAnimator && animator.getGroup()) {
for (var channel of animator.channels) {
for (var channel in animator.channels) {
if (animator[channel] && animator[channel].length) {
let times = [];
let values = [];

View File

@ -397,7 +397,7 @@ function uploadSketchfabModel() {
})
},
error: function(response) {
Blockbench.showQuickMessage('message.sketchfab.error', 1500)
Blockbench.showQuickMessage(tl('message.sketchfab.error') + `Error ${response.status}`, 1500)
console.error(response);
}
})