mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-27 04:21:46 +08:00
Add animation onion skinning
This commit is contained in:
parent
f70d7da324
commit
63dbcf7055
@ -1047,6 +1047,21 @@
|
||||
margin-top: -2px;
|
||||
height: 5000px;
|
||||
}
|
||||
#timeline_onion_skin_point {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
cursor: ew-resize;
|
||||
|
||||
height: 26px;
|
||||
width: 18px;
|
||||
top: 0;
|
||||
margin-left: -8px;
|
||||
|
||||
border-right: 9px solid transparent;
|
||||
border-left: 9px solid transparent;
|
||||
border-top: 12px solid var(--color-text);
|
||||
border-radius: 2px;
|
||||
}
|
||||
#timeline_endbracket {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
@ -914,6 +914,7 @@ const Animator = {
|
||||
get selected() {return Animation.selected},
|
||||
MolangParser: new Molang(),
|
||||
motion_trail: new THREE.Object3D(),
|
||||
onion_skin_object: new THREE.Object3D(),
|
||||
motion_trail_lock: false,
|
||||
_last_values: {},
|
||||
resetLastValues() {
|
||||
@ -976,6 +977,7 @@ const Animator = {
|
||||
|
||||
scene.remove(WinterskyScene.space);
|
||||
scene.remove(Animator.motion_trail);
|
||||
scene.remove(Animator.onion_skin_object);
|
||||
Animator.resetParticles(true);
|
||||
|
||||
three_grid.position.z = three_grid.position.x;
|
||||
@ -1117,6 +1119,63 @@ const Animator = {
|
||||
keyframe_points.keyframeUUIDs = keyframeUUIDs;
|
||||
Animator.motion_trail.add(keyframe_points);
|
||||
},
|
||||
updateOnionSkin() {
|
||||
let mode = BarItems.animation_onion_skin.value;
|
||||
if (mode == 'off') {
|
||||
Animator.onion_skin_object.children.empty();
|
||||
return;
|
||||
}
|
||||
let times = [];
|
||||
if (mode == 'previous') {
|
||||
times = [Timeline.time - Timeline.getStep()];
|
||||
} else if (mode == 'next') {
|
||||
times = [Timeline.time + Timeline.getStep()];
|
||||
} else if (mode == 'previous_next') {
|
||||
times = [
|
||||
Timeline.time - Timeline.getStep(),
|
||||
Timeline.time + Timeline.getStep()
|
||||
];
|
||||
} else if (mode == 'select') {
|
||||
times = [Timeline.vue.onion_skin_time];
|
||||
}
|
||||
|
||||
let elements = Outliner.elements;
|
||||
let last_time = Timeline.time;
|
||||
|
||||
Animator.onion_skin_object.children.empty();
|
||||
|
||||
let i = -1;
|
||||
for (let time of times) {
|
||||
i++;
|
||||
Timeline.time = time;
|
||||
Animator.showDefaultPose(true);
|
||||
Animator.stackAnimations(Animation.all.filter(a => a.playing), false);
|
||||
|
||||
elements.forEach(obj => {
|
||||
if (!obj.visibility) return;
|
||||
let mesh = obj.mesh;
|
||||
if (!mesh || !mesh.geometry || !mesh.outline) return;
|
||||
|
||||
let copy = mesh.outline.clone();
|
||||
copy.geometry = mesh.outline.geometry.clone();
|
||||
copy.material = Canvas.gridMaterial;
|
||||
copy.visible = true;
|
||||
|
||||
THREE.fastWorldPosition(mesh, copy.position);
|
||||
copy.position.sub(scene.position);
|
||||
copy.rotation.setFromQuaternion(mesh.getWorldQuaternion(new THREE.Quaternion()));
|
||||
mesh.getWorldScale(copy.scale);
|
||||
|
||||
copy.name = obj.uuid+'_onion_skin_outline';
|
||||
Animator.onion_skin_object.add(copy);
|
||||
})
|
||||
}
|
||||
Timeline.time = last_time;
|
||||
Animator.showDefaultPose(true);
|
||||
Animator.stackAnimations(Animation.all.filter(a => a.playing), false);
|
||||
|
||||
scene.add(Animator.onion_skin_object);
|
||||
},
|
||||
stackAnimations(animations, in_loop, controller_blend_values = 0) {
|
||||
[...Group.all, ...Outliner.elements].forEach(node => {
|
||||
if (!node.constructor.animator) return;
|
||||
@ -1201,6 +1260,8 @@ const Animator = {
|
||||
Canvas.ground_plane.position.z = Canvas.ground_plane.position.x;
|
||||
}
|
||||
|
||||
Animator.updateOnionSkin();
|
||||
|
||||
if (Interface.Panels.variable_placeholders.inside_vue.text.match(/^\s*preview\.texture\s*=/mi)) {
|
||||
let tex_index = Animator.MolangParser.variableHandler('preview.texture');
|
||||
let texture = Texture.all[tex_index % Texture.all.length];
|
||||
@ -1691,7 +1752,7 @@ const Animator = {
|
||||
}
|
||||
}
|
||||
}
|
||||
Canvas.gizmos.push(Animator.motion_trail);
|
||||
Canvas.gizmos.push(Animator.motion_trail, Animator.onion_skin_object);
|
||||
Blockbench.on('reset_project', () => {
|
||||
for (let path in Animator.particle_effects) {
|
||||
let effect = Animator.particle_effects[path];
|
||||
@ -1908,6 +1969,23 @@ BARS.defineActions(function() {
|
||||
}
|
||||
})
|
||||
|
||||
// Onion Skin
|
||||
new BarSelect('animation_onion_skin', {
|
||||
category: 'view',
|
||||
condition: {modes: ['animate']},
|
||||
value: 'off',
|
||||
options: {
|
||||
off: true,
|
||||
select: true,
|
||||
previous: true,
|
||||
next: true,
|
||||
previous_next: true
|
||||
},
|
||||
onChange() {
|
||||
Timeline.vue.onion_skin_mode = this.value;
|
||||
}
|
||||
})
|
||||
|
||||
new Action('bake_animation_into_model', {
|
||||
icon: 'directions_run',
|
||||
category: 'animation',
|
||||
|
@ -298,6 +298,10 @@ const Timeline = {
|
||||
Blockbench.showQuickMessage('message.no_animation_selected');
|
||||
}
|
||||
|
||||
} else if (e.target.id == 'timeline_onion_skin_point') {
|
||||
|
||||
Timeline.dragging_onion_skin_point = true;
|
||||
|
||||
} else {
|
||||
|
||||
convertTouchEvent(e);
|
||||
@ -349,6 +353,17 @@ const Timeline = {
|
||||
Animation.selected.setLength(time)
|
||||
Timeline.revealTime(time)
|
||||
|
||||
} else if (Timeline.dragging_onion_skin_point) {
|
||||
|
||||
convertTouchEvent(e);
|
||||
let offset = e.clientX - $('#timeline_time').offset().left;
|
||||
let time = Timeline.snapTime(offset / Timeline.vue._data.size)
|
||||
|
||||
if (Timeline.vue.onion_skin_time != time) {
|
||||
Timeline.vue.onion_skin_time = time;
|
||||
Timeline.revealTime(time);
|
||||
Animator.updateOnionSkin();
|
||||
}
|
||||
}
|
||||
})
|
||||
.on('mouseup touchend', e => {
|
||||
@ -358,9 +373,11 @@ const Timeline = {
|
||||
Timeline.pause();
|
||||
|
||||
} else if (Timeline.dragging_endbracket) {
|
||||
|
||||
Undo.finishEdit('Change Animation Length')
|
||||
delete Timeline.dragging_endbracket
|
||||
|
||||
} else if (Timeline.dragging_onion_skin_point) {
|
||||
delete Timeline.dragging_onion_skin_point
|
||||
}
|
||||
})
|
||||
|
||||
@ -713,6 +730,9 @@ Interface.definePanels(() => {
|
||||
show_all_handles: !Settings.get('only_selected_bezier_handles'),
|
||||
loop_graphs: [''],
|
||||
|
||||
onion_skin_mode: BarItems.animation_onion_skin.value,
|
||||
onion_skin_time: 0,
|
||||
|
||||
channels: {
|
||||
rotation: true,
|
||||
position: true,
|
||||
@ -1418,10 +1438,14 @@ Interface.definePanels(() => {
|
||||
</div>
|
||||
<div id="timeline_playhead"
|
||||
v-bind:style="{left: (playhead * size) + 'px'}"
|
||||
></div>
|
||||
/>
|
||||
<div id="timeline_onion_skin_point"
|
||||
v-if="onion_skin_mode == 'select'"
|
||||
v-bind:style="{left: (onion_skin_time * size) + 'px'}"
|
||||
/>
|
||||
<div id="timeline_endbracket"
|
||||
v-bind:style="{left: (animation_length * size) + 'px'}"
|
||||
></div>
|
||||
/>
|
||||
<div
|
||||
v-for="marker in markers"
|
||||
class="timeline_marker"
|
||||
|
@ -329,6 +329,7 @@ const MenuBar = {
|
||||
|
||||
new BarMenu('animation', [
|
||||
new MenuSeparator('edit_options'),
|
||||
'animation_onion_skin',
|
||||
'lock_motion_trail',
|
||||
new MenuSeparator('edit'),
|
||||
'add_marker',
|
||||
|
@ -1705,6 +1705,13 @@
|
||||
"action.set_ik_source.desc": "Select the root bone of the chain that should be moved by this null object via Inverse Kinematics",
|
||||
"action.lock_motion_trail": "Lock Motion Trail",
|
||||
"action.lock_motion_trail.desc": "Lock the motion trail to the currently selected group",
|
||||
"action.animation_onion_skin": "Animation Onion Skin",
|
||||
"action.animation_onion_skin.desc": "Display an wireframe view of a different frame in the animation for reference",
|
||||
"action.animation_onion_skin.off": "Off",
|
||||
"action.animation_onion_skin.select": "Select",
|
||||
"action.animation_onion_skin.previous": "Previous",
|
||||
"action.animation_onion_skin.next": "Next",
|
||||
"action.animation_onion_skin.previous_next": "Previous + Next",
|
||||
"action.looped_animation_playback": "Looped Playback",
|
||||
"action.looped_animation_playback.desc": "Preview animations in an infinite loop",
|
||||
"action.bedrock_animation_mode": "Bedrock Animation Mode",
|
||||
|
Loading…
Reference in New Issue
Block a user