diff --git a/css/panels.css b/css/panels.css
index 5c46caf3..8bc4ef07 100644
--- a/css/panels.css
+++ b/css/panels.css
@@ -600,6 +600,7 @@
}
.animation > .animation_name {
width: calc(100% - 50px);
+ user-select: none;
}
.animation .animation_play_toggle {
width: 22px;
diff --git a/index.html b/index.html
index 4960523d..4e9b1ecc 100644
--- a/index.html
+++ b/index.html
@@ -27,7 +27,7 @@
@@ -784,6 +784,7 @@
v-bind:anim_id="animation.uuid"
class="animation"
v-on:click.stop="animation.select()"
+ v-on:dblclick.stop="animation.rename()"
:key="animation.uuid"
@contextmenu.prevent.stop="animation.showContextMenu($event)"
>
diff --git a/js/animations.js b/js/animations.js
index 1eb24154..9c153b18 100644
--- a/js/animations.js
+++ b/js/animations.js
@@ -33,7 +33,7 @@ class Animation {
ba.position.length = 0;
ba.scale.length = 0;
kfs.forEach(kf_data => {
- ba.addKeyframe(kf_data/*, kf_data.uuid*/);
+ ba.addKeyframe(kf_data, kf_data.uuid);
})
}
}
@@ -41,7 +41,7 @@ class Animation {
}
return this;
}
- getUndoCopy(options) {
+ getUndoCopy(options, save) {
var scope = this;
var copy = {
uuid: this.uuid,
@@ -51,8 +51,6 @@ class Animation {
anim_time_update: this.anim_time_update,
length: this.length,
selected: this.selected,
- //particle_effects: this.particle_effects,
- //sound_effects: this.sound_effects,
}
if (Object.keys(this.animators).length) {
copy.animators = {}
@@ -61,11 +59,11 @@ class Animation {
if (kfs && kfs.length) {
if (options && options.bone_names && this.animators[uuid] instanceof BoneAnimator) {
var group = this.animators[uuid].getGroup();
- uuid = group ? group.name : ''
+ uuid = group ? group.name : '';
}
- var kfs_copy = copy.animators[uuid] = []
+ copy.animators[uuid] = [];
kfs.forEach(kf => {
- kfs_copy.push(kf.getUndoCopy())
+ copy.animators[uuid].push(kf.getUndoCopy(save));
})
}
}
@@ -137,7 +135,7 @@ class Animation {
editUpdateVariable() {
var scope = this;
Blockbench.textPrompt('message.animation_update_var', scope.anim_time_update, function(name) {
- if (name && name !== scope.anim_time_update) {
+ if (name !== scope.anim_time_update) {
Undo.initEdit({animations: [scope]})
scope.anim_time_update = name
Undo.finishEdit('change animation variable')
@@ -266,6 +264,7 @@ class GeneralAnimator {
}
addKeyframe(data, uuid) {
var channel = data.channel;
+ if (typeof channel == 'number') channel = this.channels[channel];
if (channel && this[channel]) {
var kf = new Keyframe(data, uuid);
this[channel].push(kf);
@@ -612,8 +611,10 @@ class Keyframe {
}
extend(data) {
Merge.number(this, data, 'time')
+ cl('0' , data)
if (this.transform) {
+ cl('1' , this)
if (data.values != undefined) {
if (typeof data.values == 'number' || typeof data.values == 'string') {
data.x = data.y = data.z = data.values;
@@ -631,6 +632,12 @@ class Keyframe {
Merge.string(this, data, 'w')
Merge.boolean(this, data, 'isQuaternion')
} else {
+ cl('2' , data)
+ if (data.values) {
+ data.effect = data.values.effect;
+ data.locator = data.values.locator;
+ data.file = data.values.file;
+ }
Merge.string(this, data, 'effect')
Merge.string(this, data, 'locator')
Merge.string(this, data, 'file')
@@ -805,9 +812,10 @@ class Keyframe {
}
Timeline.selected.remove(this)
}
- getUndoCopy() {
+ getUndoCopy(save) {
var copy = {
- animator: this.animator && this.animator.uuid,
+ animator: save ? undefined : this.animator && this.animator.uuid,
+ uuid: save && this.uuid,
channel: this.channel,
time: this.time,
x: this.x,
@@ -1578,24 +1586,6 @@ const Timeline = {
Animator.interval = false
}
},
- /*
- addKeyframe(channel, reset) {
- if (!Animator.selected) {
- Blockbench.showQuickMessage('message.no_animation_selected')
- return
- }
- var bone = Animator.selected.getBoneAnimator()
- if (!bone) {
- Blockbench.showQuickMessage('message.no_bone_selected')
- return
- }
- Undo.initEdit({keyframes: bone.keyframes, keep_saved: true})
- var values = reset ? (channel == 'scale' ? 1 : 0) : false;
- var kf = bone.addKeyframe(values, Timeline.snapTime(), channel?channel:'rotation')
- kf.select()
- Animator.preview()
- Undo.finishEdit('add keyframe')
- },*/
get keyframes() {
var keyframes = [];
Timeline.vue._data.animators.forEach(animator => {
@@ -1726,13 +1716,16 @@ BARS.defineActions(function() {
if (m_index > 3) {
path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(ModelMeta.export_path, true)
}
+ path.replace(/\.geo\./, 'animation')
}
Blockbench.export({
type: 'JSON Animation',
extensions: ['json'],
name: Project.geometry_name||'animation',
startpath: path,
- content: content
+ content: content,
+ }, (real_path) => {
+ ModelMeta.animation_path = real_path;
})
}
diff --git a/js/api.js b/js/api.js
index dd39ead9..b366f46b 100644
--- a/js/api.js
+++ b/js/api.js
@@ -246,7 +246,7 @@ const Blockbench = {
options.extensions = ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'tif', 'gif']
}
- electron.dialog.showOpenDialog(
+ ElecDialogs.showOpenDialog(
currentwindow,
{
title: options.title ? options.title : '',
@@ -422,7 +422,7 @@ const Blockbench = {
cb(options)
}
} else {
- electron.dialog.showSaveDialog(currentwindow, {
+ ElecDialogs.showSaveDialog(currentwindow, {
filters: [ {
name: options.type,
extensions: options.extensions
diff --git a/js/desktop.js b/js/desktop.js
index e3382ce7..ee1f583b 100644
--- a/js/desktop.js
+++ b/js/desktop.js
@@ -8,12 +8,38 @@ const originalFs = require('original-fs');
const https = require('https');
const currentwindow = electron.getCurrentWindow();
+const ElecDialogs = {};
var dialog_win = null,
latest_version = false,
recent_projects= undefined;
app.setAppUserModelId('blockbench')
+if (electron.dialog.showMessageBoxSync) {
+ ElecDialogs.showMessageBox = function(a, b, cb) {
+ if (!cb) cb = b;
+ var result = electron.dialog.showMessageBoxSync(a, b);
+ if (typeof cb == 'function') cb(result);
+ return result;
+ }
+ ElecDialogs.showSaveDialog = function(a, b, cb) {
+ if (!cb) cb = b;
+ var result = electron.dialog.showSaveDialogSync(a, b);
+ if (typeof cb == 'function') cb(result);
+ return result;
+ }
+ ElecDialogs.showOpenDialog = function(a, b, cb) {
+ if (!cb) cb = b;
+ var result = electron.dialog.showOpenDialogSync(a, b);
+ if (typeof cb == 'function') cb(result);
+ return result;
+ }
+} else {
+ ElecDialogs.showMessageBox = electron.dialog.showMessageBox;
+ ElecDialogs.showSaveDialog = electron.dialog.showSaveDialog;
+ ElecDialogs.showOpenDialog = electron.dialog.showOpenDialog;
+}
+
$(document).ready(function() {
//Setup
@@ -235,7 +261,7 @@ function changeImageEditor(texture, from_settings) {
}).show()
}
function selectImageEditorFile(texture) {
- electron.dialog.showOpenDialog(currentwindow, {
+ ElecDialogs.showOpenDialog(currentwindow, {
title: tl('message.image_editor.exe'),
filters: [{name: 'Executable Program', extensions: ['exe', 'app']}]
}, function(filePaths) {
@@ -249,7 +275,7 @@ function selectImageEditorFile(texture) {
}
//Default Pack
function openDefaultTexturePath() {
- var answer = electron.dialog.showMessageBox(currentwindow, {
+ var answer = ElecDialogs.showMessageBox(currentwindow, {
type: 'info',
buttons: (
settings.default_path.value ? [tl('dialog.cancel'), tl('dialog.continue'), tl('generic.remove')]
@@ -263,7 +289,7 @@ function openDefaultTexturePath() {
if (answer === 0) {
return;
} else if (answer === 1) {
- electron.dialog.showOpenDialog(currentwindow, {
+ ElecDialogs.showOpenDialog(currentwindow, {
title: tl('message.default_textures.select'),
properties: ['openDirectory'],
}, function(filePaths) {
@@ -339,7 +365,7 @@ function showSaveDialog(close) {
}
})
if ((window.Prop && Prop.project_saved === false && (elements.length > 0 || Group.all.length > 0)) || unsaved_textures) {
- var answer = electron.dialog.showMessageBox(currentwindow, {
+ var answer = ElecDialogs.showMessageBox(currentwindow, {
type: 'question',
buttons: [tl('dialog.save'), tl('dialog.discard'), tl('dialog.cancel')],
title: 'Blockbench',
diff --git a/js/interface/keyboard.js b/js/interface/keyboard.js
index 5a224e18..958383bd 100644
--- a/js/interface/keyboard.js
+++ b/js/interface/keyboard.js
@@ -267,6 +267,18 @@ onVueSetup(function() {
}
})
+setInterval(() => {
+ var focus = document.hasFocus();
+ if (Pressing.shift && !focus) Pressing.shift = false;
+ if (Pressing.alt && !focus) {
+ if (Toolbox.original && Toolbox.original.alt_tool) {
+ Toolbox.original.select()
+ delete Toolbox.original;
+ }
+ Pressing.alt = false;
+ }
+}, 100)
+
$(document).on('keydown mousedown', function(e) {
if (Keybinds.recording || e.which < 4) return;
//Shift
diff --git a/js/io/bbmodel.js b/js/io/bbmodel.js
index 93843663..52d0a4e2 100644
--- a/js/io/bbmodel.js
+++ b/js/io/bbmodel.js
@@ -66,7 +66,7 @@ var codec = new Codec('project', {
if (Animator.animations.length) {
model.animations = [];
Animator.animations.forEach(a => {
- model.animations.push(a.getUndoCopy({bone_names: true}))
+ model.animations.push(a.getUndoCopy({bone_names: true}, true))
})
}
diff --git a/js/io/bedrock.js b/js/io/bedrock.js
index 56b6c9af..2637f007 100644
--- a/js/io/bedrock.js
+++ b/js/io/bedrock.js
@@ -439,7 +439,7 @@ var codec = new Codec('bedrock', {
i++;
}
} catch (err) {
- var answer = electron.dialog.showMessageBox(currentwindow, {
+ var answer = ElecDialogs.showMessageBox(currentwindow, {
type: 'warning',
buttons: [
tl('message.bedrock_overwrite_error.overwrite'),
diff --git a/js/io/bedrock_old.js b/js/io/bedrock_old.js
index e70efb3b..c42818a8 100644
--- a/js/io/bedrock_old.js
+++ b/js/io/bedrock_old.js
@@ -422,7 +422,7 @@ var codec = new Codec('bedrock_old', {
obj = JSON.parse(data.replace(/\/\*[^(\*\/)]*\*\/|\/\/.*/g, ''))
} catch (err) {
err = err+''
- var answer = electron.dialog.showMessageBox(currentwindow, {
+ var answer = ElecDialogs.showMessageBox(currentwindow, {
type: 'warning',
buttons: [
tl('message.bedrock_overwrite_error.backup_overwrite'),
diff --git a/js/io/io.js b/js/io/io.js
index 8faae0bc..e71c18e5 100644
--- a/js/io/io.js
+++ b/js/io/io.js
@@ -944,7 +944,7 @@ BARS.defineActions(function() {
extensions: ['json', 'jem', 'jpm', 'bbmodel'],
type: 'Model'
}, function(files) {
- loadModelFile(files[0])
+ loadModelFile(files[0]);
})
}
})
diff --git a/js/outliner/cube.js b/js/outliner/cube.js
index 035a411b..da6ee9d4 100644
--- a/js/outliner/cube.js
+++ b/js/outliner/cube.js
@@ -479,7 +479,7 @@ class Cube extends NonGroup {
shift.sub(dq)
shift.applyQuaternion(q.inverse())
- this.move(shift)
+ this.moveVector(shift)
this.origin = origin.slice();
@@ -651,17 +651,18 @@ class Cube extends NonGroup {
scope.faces.east.uv = calcAutoUV('east', [scope.size(2), scope.size(1)])
scope.faces.south.uv = calcAutoUV('south', [scope.size(0), scope.size(1)])
scope.faces.west.uv = calcAutoUV('west', [scope.size(2), scope.size(1)])
- scope.faces.up.uv = calcAutoUV('up', [scope.size(0), scope.size(2)])
+ scope.faces.up.uv = calcAutoUV('up', [scope.size(0), scope.size(2)])
scope.faces.down.uv = calcAutoUV('down', [scope.size(0), scope.size(2)])
Canvas.updateUV(scope)
}
}
- move(val, axis, absolute, move_origin, no_update) {
+ move(val, axis, absolute, opts, no_update) {
+ if (!opts) opts = 0;
if (val instanceof THREE.Vector3) {
- return this.move(val.x, 0, absolute, move_origin, true)
- && this.move(val.y, 1, absolute, move_origin, true)
- && this.move(val.z, 2, absolute, move_origin, true);
+ return this.move(val.x, 0, absolute, opts, true)
+ && this.move(val.y, 1, absolute, opts, true)
+ && this.move(val.z, 2, absolute, opts, true);
}
var size = this.size(axis)
if (!absolute) {
@@ -673,7 +674,11 @@ class Cube extends NonGroup {
val -= this.from[axis]
//Move
- if (Blockbench.globalMovement && Format.bone_rig && !move_origin) {
+ if (opts.applyRot) {
+ var m = new THREE.Vector3()
+ m[getAxisLetter(axis)] = val
+ }
+ if (Blockbench.globalMovement && Format.bone_rig && !opts) {
var m = new THREE.Vector3()
m[getAxisLetter(axis)] = val
@@ -693,7 +698,7 @@ class Cube extends NonGroup {
this.from[axis] += val;
}
//Origin
- if (Blockbench.globalMovement && move_origin) {
+ if (Blockbench.globalMovement && opts) {
this.origin[axis] += val
}
if (!no_update) {
@@ -703,6 +708,38 @@ class Cube extends NonGroup {
}
return in_box;
}
+ moveVector(arr, axis) {
+ if (typeof arr == 'number') {
+ var n = arr;
+ arr = [0, 0, 0];
+ arr[axis||0] = n;
+ } else if (arr instanceof THREE.Vector3) {
+ arr = arr.toArray();
+ }
+ var scope = this;
+ var in_box = true;
+ arr.forEach((val, i) => {
+ cl('-------------------' + val);
+
+ var size = scope.size(i);
+ val += scope.from[i];
+ cl(val)
+
+ var val_before = val;
+ val = limitToBox(limitToBox(val, -scope.inflate) + size, scope.inflate) - size
+ if (Math.abs(val_before - val) >= 1e-4) in_box = false;
+ cl(val)
+ val -= scope.from[i]
+
+ scope.from[i] += val;
+ scope.to[i] += val;
+ cl(val)
+ })
+ this.mapAutoUV()
+ Canvas.adaptObjectPosition(this);
+ TickUpdates.selection = true;
+ return in_box;
+ }
resize(val, axis, negative, absolute, allow_negative) {
if (absolute) {
var before = 0;
diff --git a/js/outliner/locator.js b/js/outliner/locator.js
index 3be112f2..33f9b564 100644
--- a/js/outliner/locator.js
+++ b/js/outliner/locator.js
@@ -45,6 +45,21 @@ class Locator extends NonGroup {
TickUpdates.outliner = true;
return this;
}
+ getWorldCenter() {
+ var m = this.parent ? this.parent.mesh : scene;
+ var pos = new THREE.Vector3(
+ this.from[0],
+ this.from[1],
+ this.from[2]
+ )
+
+ var r = m.getWorldQuaternion(new THREE.Quaternion())
+ pos.applyQuaternion(r)
+
+ pos.add(m.getWorldPosition(new THREE.Vector3()))
+
+ return pos;
+ }
move(val, axis, absolute) {
if (absolute) {
this.from[axis] = val
diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js
index 6ee9b22e..84a7c974 100644
--- a/js/outliner/outliner.js
+++ b/js/outliner/outliner.js
@@ -17,7 +17,7 @@ const Outliner = {
}
Undo.initEdit({elements: obj.forSelected(), outliner: true, selection: true})
obj.forSelected().slice().forEach(cube => {
- cube.remove()
+ cube.remove();
})
updateSelection()
Undo.finishEdit('remove', {elements: [], outliner: true, selection: true})
diff --git a/js/preview/preview.js b/js/preview/preview.js
index 52c78db9..be11e264 100644
--- a/js/preview/preview.js
+++ b/js/preview/preview.js
@@ -816,7 +816,7 @@ const Screencam = {
cancel: 0
}, function(result) {
if (result === 1) {
- electron.dialog.showSaveDialog(currentwindow, {filters: [ {name: tl('data.image'), extensions: [is_gif ? 'gif' : 'png']} ]}, function (fileName) {
+ ElecDialogs.showSaveDialog(currentwindow, {filters: [ {name: tl('data.image'), extensions: [is_gif ? 'gif' : 'png']} ]}, function (fileName) {
if (fileName === undefined) {
return;
}
diff --git a/js/preview/transformer.js b/js/preview/transformer.js
index c1fbab5d..12047c4a 100644
--- a/js/preview/transformer.js
+++ b/js/preview/transformer.js
@@ -857,11 +857,14 @@
if (rotation_tool) {
Transformer.rotation_ref = rotation_object.mesh.parent;
- } else if (Group.selected) {
+ } else if (Group.selected && !Blockbench.globalMovement) {
Transformer.rotation_ref = rotation_object.mesh;
} else if (!Blockbench.globalMovement && Cube.selected[0] && Cube.selected[0].mesh) {
Transformer.rotation_ref = Cube.selected[0].mesh;
+
+ } else if (!Blockbench.globalMovement && Locator.selected[0]) {
+ Transformer.rotation_ref = Locator.selected[0].parent.mesh;
}
}
diff --git a/js/textures.js b/js/textures.js
index d080e83b..57cfd4cf 100644
--- a/js/textures.js
+++ b/js/textures.js
@@ -634,7 +634,7 @@ class Texture {
if (fs.existsSync(settings.image_editor.value)) {
require('child_process').spawn(settings.image_editor.value, [this.path])
} else {
- var answer = electron.dialog.showMessageBox(currentwindow, {
+ var answer = ElecDialogs.showMessageBox(currentwindow, {
type: 'info',
noLink: true,
title: tl('message.image_editor_missing.title'),
@@ -1104,7 +1104,7 @@ BARS.defineActions(function() {
path.splice(-1)
path = path.join(osfs)
- electron.dialog.showOpenDialog(currentwindow, {
+ ElecDialogs.showOpenDialog(currentwindow, {
title: tl('message.default_textures.select'),
properties: ['openDirectory'],
defaultPath: path
diff --git a/js/transform.js b/js/transform.js
index 982d9bd8..119d3b83 100644
--- a/js/transform.js
+++ b/js/transform.js
@@ -33,18 +33,27 @@ function origin2geometry() {
function getSelectionCenter() {
var center = [0, 0, 0]
var i = 0;
- selected.forEach(cube => {
- var m = cube.mesh
+ selected.forEach(obj => {
+ var m = obj.mesh
if (m) {
- var pos = cube.getWorldCenter()
+ var pos = obj.getWorldCenter()
center[0] += pos.x
center[1] += pos.y
center[2] += pos.z
- } else if (!m && cube.from) {
- center[0] += cube.from[0]-scene.position.x;
- center[1] += cube.from[1]-scene.position.y;
- center[2] += cube.from[2]-scene.position.z;
+ } else if (!m && obj.from) {
+ var pos = obj.getWorldCenter();
+ center[0] += pos.x
+ center[1] += pos.y
+ center[2] += pos.z
+ /*
+ center[0] += pos.x
+ center[1] += pos.y
+ center[2] += pos.z
+ center[0] -= obj.from[0]//-scene.position.x;
+ center[1] -= obj.from[1]//-scene.position.y;
+ center[2] -= obj.from[2]//-scene.position.z;
+ */
}
})
for (var i = 0; i < 3; i++) {
@@ -362,13 +371,13 @@ const Vertexsnap = {
} else {
Vertexsnap.cubes.forEach(function(obj) {
var cube_pos = new THREE.Vector3().copy(global_delta)
- if (Format.rotate_cubes && !Format.bone_rig) {
+ if (Format.rotate_cubes && !Blockbench.globalMovement) {
obj.origin[0] += cube_pos.getComponent(0)
obj.origin[1] += cube_pos.getComponent(1)
obj.origin[2] += cube_pos.getComponent(2)
}
- var in_box = obj.move(cube_pos);
- if (!in_box) {
+ var in_box = obj.moveVector(cube_pos.toArray());
+ if (!in_box && Format.canvas_limit) {
Blockbench.showMessageBox({translateKey: 'canvas_limit_error'})
}
})
@@ -546,6 +555,7 @@ function getRotationInterval(event) {
function getRotationObject() {
if (Format.bone_rig && Group.selected) return Group.selected;
if (Format.rotate_cubes && Cube.selected.length) return Cube.selected;
+ if (Locator.selected.length) return Locator.selected[0].parent;
}
function rotateOnAxis(value, fixed, axis) {
if (Format.bone_rig && Group.selected) {
@@ -632,9 +642,20 @@ BARS.defineActions(function() {
function moveOnAxis(value, fixed, axis) {
selected.forEach(function(obj, i) {
if (obj.movable) {
- obj.move(value, axis, fixed)
+ var val = value;
+ var size = obj.size(axis)
+ if (!fixed) {
+ val += obj.from[axis]
+ }
+ val = limitToBox(limitToBox(val, -obj.inflate) + size, obj.inflate) - size
+ val -= obj.from[axis]
+ obj.to[axis] += val;
+ obj.from[axis] += val;
+ obj.mapAutoUV()
+ Canvas.adaptObjectPosition(obj);
}
})
+ TickUpdates.selection = true;
}
new NumSlider('slider_pos_x', {
condition: () => (selected.length && Modes.edit),
@@ -747,12 +768,14 @@ BARS.defineActions(function() {
if (!fixed) {
v += obj.inflate
}
- v = obj.from[0] - Math.clamp(obj.from[0]-v, -16, 32);
- v = obj.from[1] - Math.clamp(obj.from[1]-v, -16, 32);
- v = obj.from[2] - Math.clamp(obj.from[2]-v, -16, 32);
- v = Math.clamp(obj.to[0]+v, -16, 32) - obj.to[0];
- v = Math.clamp(obj.to[1]+v, -16, 32) - obj.to[1];
- v = Math.clamp(obj.to[2]+v, -16, 32) - obj.to[2];
+ if (Format.canvas_limit) {
+ v = obj.from[0] - Math.clamp(obj.from[0]-v, -16, 32);
+ v = obj.from[1] - Math.clamp(obj.from[1]-v, -16, 32);
+ v = obj.from[2] - Math.clamp(obj.from[2]-v, -16, 32);
+ v = Math.clamp(obj.to[0]+v, -16, 32) - obj.to[0];
+ v = Math.clamp(obj.to[1]+v, -16, 32) - obj.to[1];
+ v = Math.clamp(obj.to[2]+v, -16, 32) - obj.to[2];
+ }
obj.inflate = v
})
Canvas.updatePositions()
diff --git a/package.json b/package.json
index d1379e0d..130c365d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Model editing and animation software",
- "version": "3.1.0",
+ "version": "3.1.1",
"license": "MIT",
"author": {
"name": "JannisX11",