Keyframe interpolation interface and io
This commit is contained in:
parent
c3b6922d52
commit
992f42e3a0
@ -890,7 +890,7 @@
|
|||||||
:title="tl('timeline.'+keyframe.channel)"
|
:title="tl('timeline.'+keyframe.channel)"
|
||||||
@contextmenu.prevent="keyframe.showContextMenu($event)"
|
@contextmenu.prevent="keyframe.showContextMenu($event)"
|
||||||
>
|
>
|
||||||
<i class="material-icons">stop</i>
|
<i class="material-icons">{{ keyframe.interpolation == 'catmullrom' ? 'lens' : 'stop' }}</i>
|
||||||
<svg class="keyframe_waveform" v-if="keyframe.channel == 'sound' && keyframe.file && waveforms[keyframe.file]" :style="{width: waveforms[keyframe.file].duration * size}">
|
<svg class="keyframe_waveform" v-if="keyframe.channel == 'sound' && keyframe.file && waveforms[keyframe.file]" :style="{width: waveforms[keyframe.file].duration * size}">
|
||||||
<polygon :points="getWaveformPoints(waveforms[keyframe.file].samples, size)"></polygon>
|
<polygon :points="getWaveformPoints(waveforms[keyframe.file].samples, size)"></polygon>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -1215,6 +1215,7 @@ const Animator = {
|
|||||||
ba.addKeyframe({
|
ba.addKeyframe({
|
||||||
time: parseFloat(timestamp),
|
time: parseFloat(timestamp),
|
||||||
channel,
|
channel,
|
||||||
|
interpolation: b[channel][timestamp].lerp_mode,
|
||||||
data_points: getKeyframeDataPoints(b[channel][timestamp]),
|
data_points: getKeyframeDataPoints(b[channel][timestamp]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1369,6 +1370,26 @@ Blockbench.addDragHandler('animation', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
BARS.defineActions(function() {
|
BARS.defineActions(function() {
|
||||||
|
new NumSlider('slider_animation_length', {
|
||||||
|
category: 'animation',
|
||||||
|
condition: () => Animator.open && Animation.selected,
|
||||||
|
getInterval(event) {
|
||||||
|
if (event && event.shiftKey) return 1;
|
||||||
|
return Timeline.getStep()
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
return Animation.selected.length
|
||||||
|
},
|
||||||
|
change: function(modify) {
|
||||||
|
Animation.selected.setLength(limitNumber(modify(Animation.selected.length), 0, 1e4))
|
||||||
|
},
|
||||||
|
onBefore: function() {
|
||||||
|
Undo.initEdit({animations: [Animation.selected]});
|
||||||
|
},
|
||||||
|
onAfter: function() {
|
||||||
|
Undo.finishEdit('Change Animation Length')
|
||||||
|
}
|
||||||
|
})
|
||||||
new Action('add_animation', {
|
new Action('add_animation', {
|
||||||
icon: 'fa-plus-circle',
|
icon: 'fa-plus-circle',
|
||||||
category: 'animation',
|
category: 'animation',
|
||||||
|
@ -218,11 +218,19 @@ class Keyframe {
|
|||||||
compileBedrockKeyframe() {
|
compileBedrockKeyframe() {
|
||||||
if (this.transform) {
|
if (this.transform) {
|
||||||
if (this.data_points.length == 1) {
|
if (this.data_points.length == 1) {
|
||||||
return this.getArray()
|
if (this.interpolation == 'linear') {
|
||||||
|
return this.getArray();
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
post: this.getArray(),
|
||||||
|
lerp_mode: this.interpolation,
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
pre: this.getArray(0),
|
pre: this.getArray(0),
|
||||||
post: this.getArray(1),
|
post: this.getArray(1),
|
||||||
|
lerp_mode: this.interpolation != 'linear' ? this.interpolation : undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.channel == 'timeline') {
|
} else if (this.channel == 'timeline') {
|
||||||
@ -237,7 +245,7 @@ class Keyframe {
|
|||||||
let points = [];
|
let points = [];
|
||||||
this.data_points.forEach(data_point => {
|
this.data_points.forEach(data_point => {
|
||||||
if (data_point.effect) {
|
if (data_point.effect) {
|
||||||
let script = kf.script || undefined;
|
let script = this.script || undefined;
|
||||||
if (script && !script.match(/;$/)) script += ';';
|
if (script && !script.match(/;$/)) script += ';';
|
||||||
points.push({
|
points.push({
|
||||||
effect: data_point.effect,
|
effect: data_point.effect,
|
||||||
@ -375,6 +383,8 @@ class Keyframe {
|
|||||||
},*/
|
},*/
|
||||||
'change_keyframe_file',
|
'change_keyframe_file',
|
||||||
'_',
|
'_',
|
||||||
|
// todo: integrate
|
||||||
|
'keyframe_interpolation',
|
||||||
{name: 'menu.cube.color', icon: 'color_lens', children: [
|
{name: 'menu.cube.color', icon: 'color_lens', children: [
|
||||||
{icon: 'bubble_chart', name: 'generic.unset', click: function(kf) {kf.forSelected(kf2 => {kf2.color = -1}, 'change color')}},
|
{icon: 'bubble_chart', name: 'generic.unset', click: function(kf) {kf.forSelected(kf2 => {kf2.color = -1}, 'change color')}},
|
||||||
{icon: 'bubble_chart', color: markerColors[0].standard, name: 'cube.color.'+markerColors[0].name, click: function(kf) {kf.forSelected(function(kf2){kf2.color = 0}, 'change color')}},
|
{icon: 'bubble_chart', color: markerColors[0].standard, name: 'cube.color.'+markerColors[0].name, click: function(kf) {kf.forSelected(function(kf2){kf2.color = 0}, 'change color')}},
|
||||||
@ -391,6 +401,7 @@ class Keyframe {
|
|||||||
])
|
])
|
||||||
new Property(Keyframe, 'number', 'time')
|
new Property(Keyframe, 'number', 'time')
|
||||||
new Property(Keyframe, 'number', 'color', {default: -1})
|
new Property(Keyframe, 'number', 'color', {default: -1})
|
||||||
|
new Property(Keyframe, 'string', 'interpolation', {default: 'linear'})
|
||||||
Keyframe.selected = [];
|
Keyframe.selected = [];
|
||||||
|
|
||||||
// Misc Functions
|
// Misc Functions
|
||||||
@ -410,6 +421,7 @@ function updateKeyframeSelection() {
|
|||||||
})
|
})
|
||||||
if (Timeline.selected.length) {
|
if (Timeline.selected.length) {
|
||||||
BarItems.slider_keyframe_time.update()
|
BarItems.slider_keyframe_time.update()
|
||||||
|
BarItems.keyframe_interpolation.set(Timeline.selected[0].interpolation)
|
||||||
}
|
}
|
||||||
BARS.updateConditions()
|
BARS.updateConditions()
|
||||||
Blockbench.dispatchEvent('update_keyframe_selection');
|
Blockbench.dispatchEvent('update_keyframe_selection');
|
||||||
@ -564,26 +576,6 @@ BARS.defineActions(function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
new NumSlider('slider_animation_length', {
|
|
||||||
category: 'animation',
|
|
||||||
condition: () => Animator.open && Animation.selected,
|
|
||||||
getInterval(event) {
|
|
||||||
if (event && event.shiftKey) return 1;
|
|
||||||
return Timeline.getStep()
|
|
||||||
},
|
|
||||||
get: function() {
|
|
||||||
return Animation.selected.length
|
|
||||||
},
|
|
||||||
change: function(modify) {
|
|
||||||
Animation.selected.setLength(limitNumber(modify(Animation.selected.length), 0, 1e4))
|
|
||||||
},
|
|
||||||
onBefore: function() {
|
|
||||||
Undo.initEdit({animations: [Animation.selected]});
|
|
||||||
},
|
|
||||||
onAfter: function() {
|
|
||||||
Undo.finishEdit('Change Animation Length')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
new NumSlider('slider_keyframe_time', {
|
new NumSlider('slider_keyframe_time', {
|
||||||
category: 'animation',
|
category: 'animation',
|
||||||
condition: () => Animator.open && Timeline.selected.length,
|
condition: () => Animator.open && Timeline.selected.length,
|
||||||
@ -607,6 +599,21 @@ BARS.defineActions(function() {
|
|||||||
Undo.finishEdit('move keyframes')
|
Undo.finishEdit('move keyframes')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
new BarSelect('keyframe_interpolation', {
|
||||||
|
category: 'animation',
|
||||||
|
condition: () => Animator.open && Timeline.selected.length,
|
||||||
|
options: {
|
||||||
|
linear: true,
|
||||||
|
catmullrom: true,
|
||||||
|
},
|
||||||
|
onChange: function(sel, event) {
|
||||||
|
Undo.initEdit({keyframes: Timeline.selected})
|
||||||
|
Timeline.selected.forEach((kf) => {
|
||||||
|
kf.interpolation = sel.value;
|
||||||
|
})
|
||||||
|
Undo.finishEdit('change keyframes interpolation')
|
||||||
|
}
|
||||||
|
})
|
||||||
new Action('reset_keyframe', {
|
new Action('reset_keyframe', {
|
||||||
icon: 'replay',
|
icon: 'replay',
|
||||||
category: 'animation',
|
category: 'animation',
|
||||||
|
@ -146,6 +146,8 @@ const Timeline = {
|
|||||||
if (bot < 0) body.scrollTop = Math.clamp(body.scrollTop + speed, 0, body_inner.clientHeight - body.clientHeight + 3);
|
if (bot < 0) body.scrollTop = Math.clamp(body.scrollTop + speed, 0, body_inner.clientHeight - body.clientHeight + 3);
|
||||||
if (lef < 0) body.scrollLeft = body.scrollLeft - speed;
|
if (lef < 0) body.scrollLeft = body.scrollLeft - speed;
|
||||||
if (rig < 0) body.scrollLeft = Math.clamp(body.scrollLeft + speed, 0, body_inner.clientWidth - body.clientWidth);
|
if (rig < 0) body.scrollLeft = Math.clamp(body.scrollLeft + speed, 0, body_inner.clientWidth - body.clientWidth);
|
||||||
|
|
||||||
|
updateKeyframeSelection()
|
||||||
},
|
},
|
||||||
end(e) {
|
end(e) {
|
||||||
if (!Timeline.selector.selecting) return false;
|
if (!Timeline.selector.selecting) return false;
|
||||||
|
@ -1621,6 +1621,7 @@ const BARS = {
|
|||||||
id: 'keyframe',
|
id: 'keyframe',
|
||||||
children: [
|
children: [
|
||||||
'slider_keyframe_time',
|
'slider_keyframe_time',
|
||||||
|
'keyframe_interpolation',
|
||||||
'change_keyframe_file',
|
'change_keyframe_file',
|
||||||
'reset_keyframe'
|
'reset_keyframe'
|
||||||
]
|
]
|
||||||
|
@ -147,61 +147,103 @@ class Menu {
|
|||||||
if (last.length && !last.hasClass('menu_separator')) {
|
if (last.length && !last.hasClass('menu_separator')) {
|
||||||
parent.append(entry)
|
parent.append(entry)
|
||||||
}
|
}
|
||||||
} else if (typeof s === 'string' || s instanceof Action) {
|
return;
|
||||||
if (typeof s === 'string') {
|
}
|
||||||
s = BarItems[s]
|
if (typeof s == 'string' && BarItems[s]) {
|
||||||
}
|
s = BarItems[s];
|
||||||
if (!s) {
|
}
|
||||||
return;
|
if (!Condition(s.condition, context)) return;
|
||||||
}
|
|
||||||
entry = $(s.menu_node)
|
|
||||||
if (BARS.condition(s.condition)) {
|
|
||||||
|
|
||||||
entry.off('click')
|
if (s instanceof Action) {
|
||||||
entry.off('mouseenter mousedown')
|
|
||||||
entry.on('mouseenter mousedown', function(e) {
|
entry = $(s.menu_node)
|
||||||
if (this == e.target) {
|
|
||||||
scope.hover(this, e)
|
entry.off('click')
|
||||||
}
|
entry.off('mouseenter mousedown')
|
||||||
})
|
entry.on('mouseenter mousedown', function(e) {
|
||||||
//Submenu
|
if (this == e.target) {
|
||||||
if (typeof s.children == 'function' || typeof s.children == 'object') {
|
scope.hover(this, e)
|
||||||
createChildList(s, entry)
|
|
||||||
} else {
|
|
||||||
entry.on('click', (e) => {s.trigger(e)})
|
|
||||||
//entry[0].addEventListener('click', )
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
//Submenu
|
||||||
|
if (typeof s.children == 'function' || typeof s.children == 'object') {
|
||||||
|
createChildList(s, entry)
|
||||||
|
} else {
|
||||||
|
entry.on('click', (e) => {s.trigger(e)})
|
||||||
|
//entry[0].addEventListener('click', )
|
||||||
|
}
|
||||||
|
parent.append(entry)
|
||||||
|
|
||||||
|
} else if (s instanceof BarSelect) {
|
||||||
|
|
||||||
|
if (typeof s.icon === 'function') {
|
||||||
|
var icon = Blockbench.getIconNode(s.icon(context), s.color)
|
||||||
|
} else {
|
||||||
|
var icon = Blockbench.getIconNode(s.icon, s.color)
|
||||||
|
}
|
||||||
|
entry = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
||||||
|
entry.prepend(icon)
|
||||||
|
|
||||||
|
//Submenu
|
||||||
|
var children = [];
|
||||||
|
for (var key in s.options) {
|
||||||
|
|
||||||
|
let val = s.options[key];
|
||||||
|
if (val) {
|
||||||
|
(function() {
|
||||||
|
var save_key = key;
|
||||||
|
children.push({
|
||||||
|
name: s.getNameFor(key),
|
||||||
|
id: key,
|
||||||
|
icon: val.icon || ((s.value == save_key) ? 'far.fa-dot-circle' : 'far.fa-circle'),
|
||||||
|
condition: val.condition,
|
||||||
|
click: (e) => {
|
||||||
|
s.set(save_key);
|
||||||
|
if (s.onChange) {
|
||||||
|
s.onChange(s, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let child_count = createChildList({children}, entry)
|
||||||
|
|
||||||
|
if (child_count !== 0 || typeof s.click === 'function') {
|
||||||
parent.append(entry)
|
parent.append(entry)
|
||||||
}
|
}
|
||||||
|
entry.mouseenter(function(e) {
|
||||||
|
scope.hover(this, e)
|
||||||
|
})
|
||||||
|
|
||||||
} else if (typeof s === 'object') {
|
} else if (typeof s === 'object') {
|
||||||
|
|
||||||
if (BARS.condition(s.condition, context)) {
|
let child_count;
|
||||||
let child_count;
|
if (typeof s.icon === 'function') {
|
||||||
if (typeof s.icon === 'function') {
|
var icon = Blockbench.getIconNode(s.icon(context), s.color)
|
||||||
var icon = Blockbench.getIconNode(s.icon(context), s.color)
|
} else {
|
||||||
} else {
|
var icon = Blockbench.getIconNode(s.icon, s.color)
|
||||||
var icon = Blockbench.getIconNode(s.icon, s.color)
|
}
|
||||||
}
|
entry = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
||||||
entry = $(`<li title="${s.description||''}" menu_item="${s.id}">${tl(s.name)}</li>`)
|
entry.prepend(icon)
|
||||||
entry.prepend(icon)
|
if (typeof s.click === 'function') {
|
||||||
if (typeof s.click === 'function') {
|
entry.click(e => {
|
||||||
entry.click(e => {
|
if (e.target == entry.get(0)) {
|
||||||
if (e.target == entry.get(0)) {
|
s.click(context, e)
|
||||||
s.click(context, e)
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
//Submenu
|
|
||||||
if (typeof s.children == 'function' || typeof s.children == 'object') {
|
|
||||||
child_count = createChildList(s, entry)
|
|
||||||
}
|
|
||||||
if (child_count !== 0 || typeof s.click === 'function') {
|
|
||||||
parent.append(entry)
|
|
||||||
}
|
|
||||||
entry.mouseenter(function(e) {
|
|
||||||
scope.hover(this, e)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
//Submenu
|
||||||
|
if (typeof s.children == 'function' || typeof s.children == 'object') {
|
||||||
|
child_count = createChildList(s, entry)
|
||||||
|
}
|
||||||
|
if (child_count !== 0 || typeof s.click === 'function') {
|
||||||
|
parent.append(entry)
|
||||||
|
}
|
||||||
|
entry.mouseenter(function(e) {
|
||||||
|
scope.hover(this, e)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -958,6 +958,10 @@
|
|||||||
"action.slider_animation_length.desc": "Change the length of the selected animation",
|
"action.slider_animation_length.desc": "Change the length of the selected animation",
|
||||||
"action.slider_keyframe_time": "Timecode",
|
"action.slider_keyframe_time": "Timecode",
|
||||||
"action.slider_keyframe_time.desc": "Change the timecode of the selected keyframes",
|
"action.slider_keyframe_time.desc": "Change the timecode of the selected keyframes",
|
||||||
|
"action.keyframe_interpolation": "Interpolation",
|
||||||
|
"action.keyframe_interpolation.desc": "Select the keyframe interpolation mode",
|
||||||
|
"action.keyframe_interpolation.linear": "Linear",
|
||||||
|
"action.keyframe_interpolation.catmullrom": "Smooth",
|
||||||
"action.change_keyframe_file": "Select File",
|
"action.change_keyframe_file": "Select File",
|
||||||
"action.change_keyframe_file.desc": "Select an audio file to preview a sound effect.",
|
"action.change_keyframe_file.desc": "Select an audio file to preview a sound effect.",
|
||||||
"action.reset_keyframe": "Reset Keyframe",
|
"action.reset_keyframe": "Reset Keyframe",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user