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