mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-27 04:21:46 +08:00
Add rotate in global space on animators
This commit is contained in:
parent
f16b3fb3ce
commit
d9261e55dc
@ -49,8 +49,10 @@ class Animation {
|
||||
var animator;
|
||||
if (!this.animators[key]) {
|
||||
if (key == 'effects') {
|
||||
// Effects
|
||||
animator = this.animators[key] = new EffectAnimator(this);
|
||||
} else if (animator_blueprint.type && animator_blueprint.type !== 'bone') {
|
||||
// Element
|
||||
let uuid = isUUID(key) && key;
|
||||
let element;
|
||||
if (!uuid) {
|
||||
@ -61,13 +63,15 @@ class Animation {
|
||||
if (!element) element = Outliner.elements.find(element => element.constructor.animator && element.uuid == uuid);
|
||||
animator = this.animators[uuid] = new element.constructor.animator(uuid, this, animator_blueprint.name)
|
||||
} else {
|
||||
// Bone
|
||||
let uuid = isUUID(key) && key;
|
||||
if (!uuid) {
|
||||
let lowercase_bone_name = key.toLowerCase();
|
||||
let group_match = Group.all.find(group => group.name.toLowerCase() == lowercase_bone_name)
|
||||
uuid = group_match ? group_match.uuid : guid();
|
||||
}
|
||||
animator = this.animators[uuid] = new BoneAnimator(uuid, this, animator_blueprint.name)
|
||||
animator = this.animators[uuid] = new BoneAnimator(uuid, this, animator_blueprint.name);
|
||||
if (animator_blueprint.rotation_global) animator.rotation_global = true;
|
||||
}
|
||||
} else {
|
||||
animator = this.animators[key];
|
||||
@ -113,10 +117,11 @@ class Animation {
|
||||
for (var uuid in this.animators) {
|
||||
let ba = this.animators[uuid]
|
||||
var kfs = ba.keyframes
|
||||
if (kfs && kfs.length) {
|
||||
if ((kfs && kfs.length) || ba.rotation_global) {
|
||||
let ba_copy = copy.animators[uuid] = {
|
||||
name: ba.name,
|
||||
type: ba.type,
|
||||
rotation_global: ba.rotation_global ? true : undefined,
|
||||
keyframes: []
|
||||
}
|
||||
kfs.forEach(kf => {
|
||||
@ -146,7 +151,7 @@ class Animation {
|
||||
|
||||
for (var uuid in this.animators) {
|
||||
var animator = this.animators[uuid];
|
||||
if (!animator.keyframes.length) continue;
|
||||
if (!animator.keyframes.length && !animator.rotation_global) continue;
|
||||
if (animator instanceof EffectAnimator) {
|
||||
|
||||
animator.sound.sort((kf1, kf2) => (kf1.time - kf2.time)).forEach(kf => {
|
||||
@ -167,6 +172,9 @@ class Animation {
|
||||
var group = animator.getGroup();
|
||||
var bone_tag = ani_tag.bones[group ? group.name : animator.name] = {};
|
||||
var channels = {};
|
||||
if (animator.rotation_global) {
|
||||
bone_tag.relative_to = {rotation: 'entity'};
|
||||
}
|
||||
//Saving Keyframes
|
||||
animator.keyframes.forEach(function(kf) {
|
||||
if (!channels[kf.channel]) {
|
||||
@ -1212,32 +1220,31 @@ const Animator = {
|
||||
animation.animators[uuid] = ba;
|
||||
//Channels
|
||||
for (var channel in b) {
|
||||
if (BoneAnimator.prototype.channels[channel]) {
|
||||
if (typeof b[channel] === 'string' || typeof b[channel] === 'number' || b[channel] instanceof Array) {
|
||||
if (!BoneAnimator.prototype.channels[channel]) continue;
|
||||
if (typeof b[channel] === 'string' || typeof b[channel] === 'number' || b[channel] instanceof Array) {
|
||||
ba.addKeyframe({
|
||||
time: 0,
|
||||
channel,
|
||||
uniform: !(b[channel] instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel]),
|
||||
})
|
||||
} else if (typeof b[channel] === 'object' && b[channel].post) {
|
||||
ba.addKeyframe({
|
||||
time: 0,
|
||||
channel,
|
||||
interpolation: b[channel].lerp_mode,
|
||||
uniform: !(b[channel].post instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel]),
|
||||
});
|
||||
} else if (typeof b[channel] === 'object') {
|
||||
for (var timestamp in b[channel]) {
|
||||
ba.addKeyframe({
|
||||
time: 0,
|
||||
time: parseFloat(timestamp),
|
||||
channel,
|
||||
uniform: !(b[channel] instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel]),
|
||||
})
|
||||
} else if (typeof b[channel] === 'object' && b[channel].post) {
|
||||
ba.addKeyframe({
|
||||
time: 0,
|
||||
channel,
|
||||
interpolation: b[channel].lerp_mode,
|
||||
uniform: !(b[channel].post instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel]),
|
||||
interpolation: b[channel][timestamp].lerp_mode,
|
||||
uniform: !(b[channel][timestamp] instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel][timestamp]),
|
||||
});
|
||||
} else if (typeof b[channel] === 'object') {
|
||||
for (var timestamp in b[channel]) {
|
||||
ba.addKeyframe({
|
||||
time: parseFloat(timestamp),
|
||||
channel,
|
||||
interpolation: b[channel][timestamp].lerp_mode,
|
||||
uniform: !(b[channel][timestamp] instanceof Array),
|
||||
data_points: getKeyframeDataPoints(b[channel][timestamp]),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set step interpolation
|
||||
@ -1254,6 +1261,9 @@ const Animator = {
|
||||
}
|
||||
})
|
||||
}
|
||||
if (b.relative_to && b.relative_to.rotation == 'entity') {
|
||||
ba.rotation_global = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (a.sound_effects) {
|
||||
|
@ -1015,7 +1015,7 @@ Interface.definePanels(() => {
|
||||
<div id="timeline_body_inner" v-bind:style="{width: (size*length + head_width)+'px'}" @contextmenu.stop="Timeline.showMenu($event)">
|
||||
<li v-for="animator in animators" class="animator" :class="{selected: animator.selected, boneless: animator.constructor.name == 'BoneAnimator' && !animator.group}" :uuid="animator.uuid" v-on:click="animator.select();">
|
||||
<div class="animator_head_bar">
|
||||
<div class="channel_head" v-bind:style="{left: scroll_left+'px', width: head_width+'px'}" v-on:dblclick.stop="toggleAnimator(animator)">
|
||||
<div class="channel_head" v-bind:style="{left: scroll_left+'px', width: head_width+'px'}" v-on:dblclick.stop="toggleAnimator(animator)" @contextmenu.stop="animator.showContextMenu($event)">
|
||||
<div class="text_button" v-on:click.stop="toggleAnimator(animator)">
|
||||
<i class="icon-open-state fa" v-bind:class="{'fa-angle-right': !animator.expanded, 'fa-angle-down': animator.expanded}"></i>
|
||||
</div>
|
||||
@ -1046,6 +1046,7 @@ Interface.definePanels(() => {
|
||||
:class="{selected: graph_editor_open && animator.selected && graph_editor_channel == channel}"
|
||||
v-bind:style="{left: scroll_left+'px', width: head_width+'px'}"
|
||||
@click.stop="selectChannel(animator, channel);"
|
||||
@contextmenu.stop="animator.showContextMenu($event)"
|
||||
>
|
||||
<div class="text_button" v-if="channel_options.mutable" v-on:click.stop="animator.toggleMuted(channel)">
|
||||
<template v-if="channel === 'sound'">
|
||||
|
@ -100,6 +100,16 @@ class GeneralAnimator {
|
||||
result = before ? before : this.createKeyframe(null, Timeline.time, channel, false, false);
|
||||
return {before, result};
|
||||
}
|
||||
showContextMenu(event) {
|
||||
Prop.active_panel = 'timeline'
|
||||
if (!this.selected) {
|
||||
this.select()
|
||||
}
|
||||
if (this.menu) {
|
||||
this.menu.open(event, this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
toggleMuted(channel) {
|
||||
this.muted[channel] = !this.muted[channel];
|
||||
if (this instanceof BoneAnimator) Animator.preview();
|
||||
@ -152,6 +162,7 @@ class BoneAnimator extends GeneralAnimator {
|
||||
super(uuid, animation);
|
||||
this.uuid = uuid;
|
||||
this._name = name;
|
||||
this.rotation_global = false;
|
||||
|
||||
for (let channel in this.channels) {
|
||||
this[channel] = [];
|
||||
@ -293,10 +304,10 @@ class BoneAnimator extends GeneralAnimator {
|
||||
}
|
||||
}
|
||||
displayRotation(arr, multiplier = 1) {
|
||||
if (!arr) return this;
|
||||
var bone = this.group.mesh
|
||||
|
||||
if (!arr) {
|
||||
} else if (arr.length === 4) {
|
||||
if (arr.length === 4) {
|
||||
var added_rotation = new THREE.Euler().setFromQuaternion(new THREE.Quaternion().fromArray(arr), 'ZYX')
|
||||
bone.rotation.x -= added_rotation.x * multiplier
|
||||
bone.rotation.y -= added_rotation.y * multiplier
|
||||
@ -306,6 +317,12 @@ class BoneAnimator extends GeneralAnimator {
|
||||
bone.rotation[getAxisLetter(i)] += Math.degToRad(n) * (i == 2 ? 1 : -1) * multiplier
|
||||
})
|
||||
}
|
||||
if (this.rotation_global) {
|
||||
let quat = bone.parent.getWorldQuaternion(Reusable.quat1);
|
||||
quat.invert();
|
||||
bone.quaternion.premultiply(quat);
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
displayPosition(arr, multiplier = 1) {
|
||||
@ -416,6 +433,20 @@ class BoneAnimator extends GeneralAnimator {
|
||||
scale: {name: tl('timeline.scale'), mutable: true, transform: true, max_data_points: 2},
|
||||
}
|
||||
Group.animator = BoneAnimator;
|
||||
BoneAnimator.prototype.menu = new Menu('bone_animator', [
|
||||
{
|
||||
id: 'rotation_global',
|
||||
name: 'menu.animator.rotation_global',
|
||||
condition: animator => animator.type == 'bone',
|
||||
icon: (animator) => animator.rotation_global,
|
||||
click(animator) {
|
||||
Undo.initEdit({animations: [Animation.selected]});
|
||||
animator.rotation_global = !animator.rotation_global;
|
||||
Undo.finishEdit('Toggle rotation in global space');
|
||||
Animator.preview();
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
class NullObjectAnimator extends BoneAnimator {
|
||||
constructor(uuid, animation, name) {
|
||||
|
@ -1405,7 +1405,7 @@
|
||||
|
||||
let {mesh} = Group.selected || ((Outliner.selected[0] && Outliner.selected[0].constructor.animator) ? Outliner.selected[0] : undefined);
|
||||
|
||||
if (Toolbox.selected.id === 'rotate_tool' && (BarItems.rotation_space.value === 'global' || scope.axis == 'E')) {
|
||||
if (Toolbox.selected.id === 'rotate_tool' && (BarItems.rotation_space.value === 'global' || scope.axis == 'E' || (Timeline.selected_animator?.rotation_global && Transformer.getTransformSpace() == 2))) {
|
||||
|
||||
let normal = scope.axis == 'E'
|
||||
? rotate_normal
|
||||
@ -1415,8 +1415,10 @@
|
||||
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(difference))
|
||||
rotWorldMatrix.multiply(mesh.matrixWorld)
|
||||
|
||||
let inverse = new THREE.Matrix4().copy(mesh.parent.matrixWorld).invert()
|
||||
rotWorldMatrix.premultiply(inverse)
|
||||
if (Timeline.selected_animator?.rotation_global !== true) {
|
||||
let inverse = new THREE.Matrix4().copy(mesh.parent.matrixWorld).invert()
|
||||
rotWorldMatrix.premultiply(inverse)
|
||||
}
|
||||
|
||||
mesh.matrix.copy(rotWorldMatrix)
|
||||
mesh.setRotationFromMatrix(rotWorldMatrix)
|
||||
|
@ -1617,6 +1617,8 @@
|
||||
"menu.animation_file.unload": "Unload Animation File",
|
||||
"menu.animation_file.import_remaining": "Import Remaining Animations",
|
||||
|
||||
"menu.animator.rotation_global": "Rotate in Global Space",
|
||||
|
||||
"menu.keyframe.quaternion": "Quaternion",
|
||||
|
||||
"menu.timeline_marker.set_time": "Set Time...",
|
||||
|
Loading…
Reference in New Issue
Block a user