Add rotate in global space on animators

This commit is contained in:
JannisX11 2022-09-25 19:46:02 +02:00
parent f16b3fb3ce
commit d9261e55dc
5 changed files with 78 additions and 32 deletions

View File

@ -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) {

View File

@ -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'">

View File

@ -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) {

View File

@ -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)

View File

@ -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...",