diff --git a/css/style.css b/css/style.css index 941d7fac7..3ca11576c 100644 --- a/css/style.css +++ b/css/style.css @@ -72,6 +72,7 @@ text-transform: none; line-height: 1; font-size: 1.4em; + max-width: 24px; Better Font Rendering =========== -webkit-font-smoothing: antialiased; @@ -656,6 +657,9 @@ height: 100%; width: 100%; } + body.display_mode .single_canvas_wrapper { + position: relative; + } .quad_canvas_wrapper { height: 50%; width: 50%; @@ -714,7 +718,6 @@ .tool { height: 32px; width: 40px; - padding-top: 4px; margin-left: 1px; margin-right: 1px; background: transparent; @@ -725,6 +728,10 @@ float: left; color: var(--color-text); } + .tool i { + display: inline-block; + margin-top: 4px; + } .tool.sel { border-bottom: 4px solid var(--color-accent); @@ -790,7 +797,7 @@ padding-right: 5px; padding-top: 2px; color: var(--color-text_acc); - margin-top: -3px; + margin-top: 32px; display: none; background: var(--color-bright_ui); white-space: nowrap; @@ -871,10 +878,12 @@ cursor: default; text-align: center; font-size: 0.9em; - padding-top: 5px; float: left; overflow: hidden; } + .panel#uv .tabs_small label { + padding-top: 4px; + } .tabs_small { background-color: transparent; @@ -1060,8 +1069,74 @@ background-color: var(--color-text); } +/*Action Select*/ + #action_selector { + position: absolute; + right: 0; + left: 0; + margin-left: auto; + margin-right: auto; + top: 200px; + width: 360px; + height: 42px; + box-shadow: 0 0 5px black; + } + #action_selector > input { + width: 100%; + height: 42px; + padding: 5px; + padding-left: 12px; + background-color: var(--color-ui); + border: 1px solid var(--color-border); + } + #action_selector > i { + position: absolute; + right: 6px; + top: 9px; + } + #action_selector > ul { + background-color: var(--color-bright_ui); + color: var(--color-text_acc); + min-height: 20px; + width: 300px; + margin-left: 8px; + box-shadow: 0 0 5px black; + max-height: 400px; + overflow-y: auto; + overflow-x: hidden; + } + #action_selector > ul > li { + padding: 4px; + } + #action_selector > ul > li.selected { + background-color: var(--color-accent); + } + #action_selector > ul > li div.icon_wrapper { + display: inline-block; + height: 26px; + vertical-align: text-top; + } + + + + + #bar_item_list { + max-height: 400px; + margin-bottom: 20px; + overflow-y: scroll; + min-height: 80px; + border: 1px solid var(--color-border); + border-right: none; + } + #bar_item_list li { + padding: 4px; + } + #bar_item_list li:hover { + color: var(--color-light); + } + /*Textures*/ .texture { height: 50px; @@ -1583,7 +1658,7 @@ color: var(--color-light); } - #keybindlist li div:first-child { + #keybindlist li > div:first-child { background: transparent; width: calc(52% - 28px); text-align: right; @@ -2051,7 +2126,7 @@ font-size: 0.9em; padding: 3px; float: left; - margin-top: 7px; + margin-top: 6px; } #plugin_list li .title i.plugin_expand_icon { display: none; diff --git a/index.html b/index.html index 1be85bcf2..4df8bd027 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ @@ -38,10 +38,10 @@ - - + + @@ -38,10 +38,10 @@ - - + + - +
+
@@ -122,8 +123,8 @@
- +
clear
@@ -160,8 +161,8 @@
  • -
    {{item.name + (BARS.condition(item.condition) ? '' : ' (' + tl('dialog.toolbar_edit.hidden') + ')' )}}
    +
  • @@ -182,7 +183,7 @@
    - +
    clear
    @@ -243,10 +244,10 @@
    - - - -
    delete
    menu.texture.delete
    + + + +
    menu.texture.delete
    delete

    path

    @@ -306,6 +307,7 @@
    +
    clear
    @@ -489,12 +491,12 @@
    {{action.name}}
    {{ action.keybind ? action.keybind.label : '' }}
    - replay
    keybindings.reset
    + replay
    - clear
    keybindings.clear
    + clear
    @@ -696,6 +698,17 @@
    +
    + + search + +
    +
    Blockbench @@ -742,26 +755,26 @@

    display.slot

    - + - + - + - + - + - + - + - +

    display.reference

    @@ -788,7 +801,8 @@

    display.scale

    replay
    - {{ slot.mirror[axis] ? 'check_box' : 'check_box_outline_blank' }}
    display.mirror
    +
    display.mirror
    + {{ slot.mirror[axis] ? 'check_box' : 'check_box_outline_blank' }}
    -
    +
    check close diff --git a/js/OBJExporter.js b/js/OBJExporter.js index ca8208592..bc4ec5d43 100644 --- a/js/OBJExporter.js +++ b/js/OBJExporter.js @@ -8,7 +8,6 @@ THREE.OBJExporter = function () {}; THREE.OBJExporter.prototype = { constructor: THREE.OBJExporter, - parse: function ( object, mtlFileName ) { var output = '# Made in Blockbench '+appVersion+'\n'; @@ -44,13 +43,9 @@ THREE.OBJExporter.prototype = { vertex.applyMatrix4( mesh.matrixWorld ); output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n'; - nbVertex ++; - } - // uvs - var faces = geometry.faces; var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; var hasVertexUvs = faces.length === faceVertexUvs.length; @@ -64,15 +59,10 @@ THREE.OBJExporter.prototype = { for ( var j = 0, jl = vertexUvs.length; j < jl; j ++ ) { var uv = vertexUvs[ j ]; - output += 'vt ' + uv.x + ' ' + uv.y + '\n'; - nbVertexUvs ++; - } - } - } // normals @@ -95,9 +85,7 @@ THREE.OBJExporter.prototype = { output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; nbNormals ++; - } - } else { var normal = face.normal.clone(); @@ -106,69 +94,49 @@ THREE.OBJExporter.prototype = { for ( var j = 0; j < 3; j ++ ) { output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n'; - nbNormals ++; - } - } - } // material - for (var key in element.faces) { - if (element.faces.hasOwnProperty(key)) { - var id = element.faces[key].texture - if (id !== undefined && id !== null) { - id = id.replace('#', '') - if (materials[id] === undefined) { - materials[id] = getTextureById(id) - } - } + for (var face in element.faces) { + var tex = element.faces[face].getTexture() + if (tex && tex.uuid && !materials[tex.id]) { + materials[tex.id] = tex } } - - for ( var i = 0, j = 1, l = faces.length; i < l; i ++, j += 3 ) { var f_mat = getMtlFace(element, i) - if (f_mat) { var face = faces[ i ]; - if (i % 2 === 0) { output += f_mat } - output += 'f '; output += ( indexVertex + face.a + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ) + ' '; output += ( indexVertex + face.b + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 1 ) : '' ) + '/' + ( indexNormals + j + 1 ) + ' '; output += ( indexVertex + face.c + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 2 ) : '' ) + '/' + ( indexNormals + j + 2 ) + '\n'; - } } - } else { - console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', mesh ); // TODO: Support only BufferGeometry and use use setFromObject() - } // update index indexVertex += nbVertex; indexVertexUvs += nbVertexUvs; indexNormals += nbNormals; - }; object.traverse( function ( child ) { if ( child instanceof THREE.Mesh ) parseMesh( child ); - } ); // mtl output @@ -190,7 +158,6 @@ THREE.OBJExporter.prototype = { } } - }; function getMtlFace(obj, index) { if (index % 2 == 1) index--; @@ -204,14 +171,13 @@ function getMtlFace(obj, index) { case 6: key = 'down'; break; } - var id = obj.faces[key].texture + var tex = obj.faces[key].getTexture() - if (id === null) { + if (tex === null) { return false - } else if (id === undefined) { + } else if (typeof tex === 'string') { return 'usemtl none\n' } else { - id = id.replace('#', '') - return 'usemtl ' + id + '\n'; + return 'usemtl ' + tex.id + '\n'; } } \ No newline at end of file diff --git a/js/TransformControls.js b/js/TransformControls.js index a0671fbff..7fb992db7 100644 --- a/js/TransformControls.js +++ b/js/TransformControls.js @@ -548,6 +548,7 @@ var _mode = "translate"; var _dragging = false; + var _has_groups = false; var _plane = "XY"; var _gizmo = { @@ -866,7 +867,16 @@ obj.oldScale = obj.size(axisnr) }) } - Undo.initEdit({cubes: selected}) + _has_groups = Blockbench.entity_mode && selected_group && selected_group.matchesSelection() && Toolbox.selected.transformerMode == 'translate'; + var rotate_group = Blockbench.entity_mode && selected_group && Toolbox.selected.transformerMode == 'rotate'; + + if (rotate_group) { + Undo.initEdit({cubes: selected, group: selected_group}) + } else if (_has_groups) { + Undo.initEdit({cubes: selected, outliner: true}) + } else { + Undo.initEdit({cubes: selected}) + } } else if (Modes.id === 'animate') { @@ -984,15 +994,17 @@ }) } if (!overlapping) { - /*var group = Blockbench.entity_mode && selected_group && selected_group.matchesSelection(); - if (group) { - selected_group.origin[axisNumber] += difference - }*/ + if (_has_groups && Blockbench.globalMovement) { + selected_group.forEachChild(g => { + g.origin[axisNumber] += difference + }, 'group', true) + + } selected.forEach(function(obj, i) { var mesh = scope.objects[i] var valx = obj.from[axisNumber] valx += difference - moveCube(obj, valx, axisNumber/*, group*/) + moveCube(obj, valx, axisNumber, _has_groups) }) Canvas.updatePositions(true) centerTransformer() diff --git a/js/actions.js b/js/actions.js index d9e993b5d..92079b919 100644 --- a/js/actions.js +++ b/js/actions.js @@ -29,29 +29,26 @@ class BarItem { return !!this.condition } } - addLabel(in_bar) { - $(this.node).attr('title', this.description) + addLabel(in_bar, action) { + if (!action || this instanceof BarItem) { + action = this; + } + $(action.node).attr('title', action.description) if (in_bar) { - $(this.node).prepend('') + $(action.node).prepend('') } else { - $(this.node).append('
    '+this.name+'
    ') + $(action.node).prepend('
    '+action.name+'
    ') .on('mouseenter', function() { var tooltip = $(this).find('div.tooltip') if (!tooltip || typeof tooltip.offset() !== 'object') return; - //Left - if (tooltip.css('left') === '-4px') { - tooltip.css('left', 'auto') - } - if (-tooltip.offset().left > 4) { - tooltip.css('left', '-4px') - } - //Right - if (tooltip.css('right') === '-4px') { - tooltip.css('right', 'auto') - } - if ((tooltip.offset().left + tooltip.width()) - $(window).width() > 4) { - tooltip.css('right', '-4px') + + tooltip.css('margin-left', '0') + var offset = tooltip.offset() + offset.right = offset.left + parseInt(tooltip.css('width').replace(/px/, '')) - $(window).width() + + if (offset.right > 4) { + tooltip.css('margin-left', -offset.right+'px') } }) } @@ -127,6 +124,7 @@ class Action extends BarItem { } this.keybind.setAction(this.id) this.work_in_dialog = data.work_in_dialog === true + this.uses = 0; //Icon this.icon = data.icon this.color = data.color @@ -142,9 +140,9 @@ class Action extends BarItem { this.icon_node = Blockbench.getIconNode(this.icon, this.color) this.node = $(`
    `).get(0) this.nodes = [this.node] - this.addLabel(data.label) this.menu_node = $(`
  • ${this.name}
  • `).get(0) - $(this.node).add(this.menu_node).prepend(this.icon_node) + $(this.node).add(this.menu_node).append(this.icon_node) + this.addLabel(data.label) $(this.node).click(function(e) {scope.trigger(e)}) if (data.linked_setting) { @@ -164,6 +162,7 @@ class Action extends BarItem { return true; } scope.click(event) + scope.uses++; $(scope.nodes).each(function() { $(this).css('color', 'var(--color-light)') @@ -227,6 +226,7 @@ class Tool extends Action { } Toolbox.selected = this; delete Toolbox.original; + this.uses++; if (this.transformerMode) { Transformer.setMode(this.transformerMode) @@ -309,8 +309,8 @@ class NumSlider extends Widget { } var scope = this; this.node = $( `
    -
    ${this.name}
    +
    `).get(0); this.jq_outer = $(this.node) this.jq_inner = this.jq_outer.find('.nslide'); @@ -651,10 +651,15 @@ class Toolbar { var scope = this; this.children = []; this.default_children = data.children.slice() - this.node = $('
    '+ + var jq = $('
    '+ '
    '+ - '
    more_vert
    '+tl('data.toolbar')+'
    '+ - '
    ').get(0) + '
    more_vert
    '+ + '
    ') + this.node = jq.get(0) + BarItem.prototype.addLabel(false, { + name: tl('data.toolbar'), + node: jq.find('.tool.toolbar_menu').get(0) + }) $(this.node).find('div.toolbar_menu').click(function(event) {scope.contextmenu(event)}) if (data) { this.id = data.id @@ -928,7 +933,12 @@ const BARS = { id: 'project_window', icon: 'featured_play_list', category: 'file', - click: function () {showDialog('project_settings');} + click: function () { + showDialog('project_settings'); + if (Blockbench.entity_mode) { + Undo.initEdit({resolution: true}) + } + } }) new Action({ id: 'open_model_folder', @@ -1005,10 +1015,18 @@ const BARS = { id: 'duplicate', icon: 'content_copy', category: 'edit', - condition: () => (!display_mode && !Animator.open && selected.length), + condition: () => (!display_mode && !Animator.open && (selected.length || selected_group)), keybind: new Keybind({key: 68, ctrl: true}), click: function () { - duplicateCubes(); + if (selected_group && (selected_group.matchesSelection() || selected.length === 0)) { + var cubes_before = elements.length + Undo.initEdit({outliner: true, cubes: [], selection: true}) + var g = selected_group.duplicate() + g.select().isOpen = true; + Undo.finishEdit('duplicate_group', {outliner: true, cubes: elements.slice().slice(cubes_before), selection: true}) + } else { + duplicateCubes(); + } } }) new Action({ @@ -1080,7 +1098,7 @@ const BARS = { id: 'move_up', icon: 'arrow_upward', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 38, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(-1, 2, e)} }) @@ -1088,7 +1106,7 @@ const BARS = { id: 'move_down', icon: 'arrow_downward', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 40, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(1, 2, e)} }) @@ -1096,7 +1114,7 @@ const BARS = { id: 'move_left', icon: 'arrow_back', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 37, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(-1, 0, e)} }) @@ -1104,7 +1122,7 @@ const BARS = { id: 'move_right', icon: 'arrow_forward', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 39, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(1, 0, e)} }) @@ -1112,7 +1130,7 @@ const BARS = { id: 'move_forth', icon: 'keyboard_arrow_up', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 33, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(-1, 1, e)} }) @@ -1120,7 +1138,7 @@ const BARS = { id: 'move_back', icon: 'keyboard_arrow_down', category: 'transform', - condition: () => (selected.length && !open_interface && !open_menu), + condition: () => (selected.length && !open_menu), keybind: new Keybind({key: 34, ctrl: null, shift: null}), click: function (e) {moveCubesRelative(1, 1, e)} }) @@ -1207,6 +1225,17 @@ const BARS = { click: function () {setZoomLevel('reset')} }) + //Find Action + new Action({ + id: 'select_action', + icon: 'fullscreen', + category: 'blockbench', + condition: isApp, + keybind: new Keybind({key: 70}), + click: function () { + ActionControl.select() + } + }) BARS.action_definers.forEach((definer) => { if (typeof definer === 'function') { @@ -1218,7 +1247,7 @@ const BARS = { // Toolbars = {} var stored = localStorage.getItem('toolbars') - if (stored) { + if (stored && localStorage.getItem('welcomed_version') == appVersion) { stored = JSON.parse(stored) if (typeof stored === 'object') { BARS.stored = stored @@ -1298,6 +1327,10 @@ const BARS = { 'uv_auto', 'uv_transparent', 'uv_rotation', + //Box + 'toggle_uv_overlay', + 'uv_shift', + 'toggle_mirror_uv', ], default_place: true }) @@ -1367,6 +1400,7 @@ const BARS = { id: 'brush', children: [ 'brush_mode', + 'fill_mode', 'brush_color', 'slider_brush_size', 'slider_brush_opacity', @@ -1408,7 +1442,6 @@ const BARS = { }, computed: { searchedBarItems() { - var name = $('#action_search_bar').val().toUpperCase() var list = [{ icon: 'bookmark', @@ -1472,6 +1505,44 @@ const BARS = { BARS.list._data.showAll = !BARS.list._data.showAll BARS.list._data.showAll = !BARS.list._data.showAll } + + ActionControl.vue = new Vue({ + el: '#action_selector', + data: { + open: false, + search_input: '', + index: 0, + length: 0, + list: [] + }, + computed: { + actions: function() { + var search_input = this._data.search_input.toUpperCase() + var list = this._data.list.empty() + for (var i = 0; i < Keybinds.actions.length; i++) { + var item = Keybinds.actions[i]; + if ( + search_input.length == 0 || + item.name.toUpperCase().includes(search_input) || + item.id.toUpperCase().includes(search_input) + ) { + if (item instanceof Action && BARS.condition(item.condition)) { + list.push(item) + if (list.length > ActionControl.max_length) i = Infinity; + } + } + } + this._data.length = list.length; + if (this._data.index < 0) { + this._data.index = 0; + } + if (this._data.index >= list.length) { + this._data.index = list.length-1; + } + return list; + } + } + }) }, updateConditions: function() { for (var key in Toolbars) { @@ -1505,6 +1576,57 @@ const BARS = { } } } +const ActionControl = { + get open() {return ActionControl.vue._data.open}, + set open(state) {ActionControl.vue._data.open = !!state}, + type: 'action_selector', + max_length: 16, + select: function() { + ActionControl.open = true; + open_interface = ActionControl; + ActionControl.vue._data.index = 0; + Vue.nextTick(_ => { + $('#action_selector > input').focus().select(); + }) + }, + hide: function() { + open_interface = false; + ActionControl.open = false; + }, + confirm: function(e) { + var data = ActionControl.vue._data + var action = data.list[data.index] + ActionControl.hide() + if (action) { + action.trigger(e) + } + }, + cancel: function() { + ActionControl.hide() + }, + click: function(action, e) { + action.trigger(e) + ActionControl.hide() + }, + handleKeys: function(e) { + var data = ActionControl.vue._data + + if (e.which === 38) { + data.index--; + if (data.index < 0) { + data.index = data.length-1; + } + } else if (e.which === 40) { + data.index++; + if (data.index >= data.length) { + data.index = 0; + } + } else { + return false; + } + return true; + } +} //Menu class Menu { @@ -1649,7 +1771,7 @@ class Menu { entry = $('
  • ' + tl(s.name) + '
  • ') entry.prepend(icon) if (typeof s.click === 'function') { - entry.click(function() {s.click(context)}) + entry.click(function(e) {s.click(context, e)}) } //Submenu if (typeof s.children == 'function' || typeof s.children == 'object') { @@ -1838,7 +1960,7 @@ const MenuBar = { 'project_window', {name: 'menu.file.new', id: 'new', icon: 'insert_drive_file', children: [ 'new_block_model', - 'new_entity_model' + 'new_entity_model', ]}, {name: 'menu.file.recent', id: 'recent', icon: 'history', condition: function() {return isApp && recent_projects.length}, children: function() { var arr = [] @@ -1848,8 +1970,8 @@ const MenuBar = { name: p.name, path: p.path, icon: entity ? 'view_list' : 'insert_drive_file', - click: function() { - readFile(p.path, true) + click: function(c, event) { + readFile(p.path, !event.shiftKey) } }) }) @@ -1872,7 +1994,6 @@ const MenuBar = { '_', 'settings_window', 'update_window', - 'show_tip', 'donate', 'reload' ]) @@ -1881,9 +2002,9 @@ const MenuBar = { 'redo', '_', 'add_cube', + 'add_group', 'duplicate', 'delete', - 'sort_outliner', '_', 'local_move', '_', @@ -1916,6 +2037,7 @@ const MenuBar = { 'toggle_export', 'toggle_autouv', 'toggle_shade', + 'toggle_mirror_uv', 'rename' ]} @@ -2088,7 +2210,7 @@ const Keybinds = { Keybinds.save() } } -if (localStorage.getItem('keybindings') && localStorage.getItem('welcomed_version') == appVersion) { +if (localStorage.getItem('keybindings')) { try { Keybinds.stored = JSON.parse(localStorage.getItem('keybindings')) } catch (err) {} diff --git a/js/animations.js b/js/animations.js index 238449b9a..5c2f3017e 100644 --- a/js/animations.js +++ b/js/animations.js @@ -120,6 +120,7 @@ class Animation { if (selected_group) { centerTransformer() } + Blockbench.dispatchEvent('display_animation_frame') } add() { if (!Animator.animations.includes(this)) { @@ -132,6 +133,7 @@ class Animation { Animator.selected = false } Animator.animations.remove(this) + Blockbench.dispatchEvent('remove_animation', {animation: this}) return this; } getMaxLength() { @@ -1140,9 +1142,7 @@ const Timeline = { Timeline.loop() }, loop: function() { - if (Animator.selected) { - Animator.selected.displayFrame(Timeline.second) - } + Animator.preview() if (Animator.selected && Timeline.second < (Animator.selected.length||1e3)) { Animator.interval = setTimeout(Timeline.loop, 16.66) Timeline.setTime(Timeline.second + 1/60) @@ -1206,6 +1206,25 @@ const Timeline = { ]) } +onVueSetup(function() { + Animator.vue = new Vue({ + el: '#animations_list', + data: { + animations: Animator.animations + } + }) + Timeline.vue = new Vue({ + el: '#timeline_inner', + data: { + size: 150, + length: 10, + timecodes: [], + keyframes: [], + marker: Timeline.second + } + }) +}) + BARS.defineActions(function() { new Action({ id: 'add_animation', @@ -1230,7 +1249,7 @@ BARS.defineActions(function() { var exp = new RegExp(osfs.replace('\\', '\\\\')+'models'+osfs.replace('\\', '\\\\')) var m_index = path.search(exp) if (m_index > 3) { - path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(Prop.file_path, true) + path = path.substr(0, m_index) + osfs + 'animations' + osfs + pathToName(Prop.file_path).replace(/\.geo/, '.animation') } } Blockbench.import({ @@ -1341,4 +1360,4 @@ BARS.defineActions(function() { keybind: new Keybind({key: 46}), click: function () {removeSelectedKeyframes()} }) -}) \ No newline at end of file +}) diff --git a/js/api.js b/js/api.js index 6f914c3e9..ad12444de 100644 --- a/js/api.js +++ b/js/api.js @@ -10,7 +10,12 @@ class API { this.drag_handlers = {} this.entity_mode = false if (isApp) { - this.platform = require('os').platform() + this.platform = process.platform + switch (this.platform) { + case 'win32': this.operating_system = 'Windows'; break; + case 'darwin': this.operating_system = 'macOS'; break; + default: this.operating_system = 'Linux'; break; + } if (this.platform.includes('win32') === true) osfs = '\\' } } @@ -32,7 +37,7 @@ class API { return Blockbench.entity_mode; } registerEdit() { - console.error('Blockbench.registerEdit is outdated. Please use Undo.initEdit and Undo.finishEdit') + console.warn('Blockbench.registerEdit is outdated. Please use Undo.initEdit and Undo.finishEdit') } //Interface @@ -189,7 +194,8 @@ class API { }) } addMenuEntry(name, icon, click) { - MenuBar.addAction({icon: icon, name: name, id: name, click: click}, 'filter') + var action = new Action({icon: icon, name: name, id: name, click: click}) + MenuBar.addAction(action, 'filter') } removeMenuEntry(name) { MenuBar.removeAction('filter.'+name) @@ -430,15 +436,10 @@ class API { if (options.custom_writer) { options.custom_writer(options.content, file_path) } else { - fs.writeFile(file_path, options.content, function (err) { - if (err) { - console.log('Error exporting file: '+err) - return; - } - if (cb) { - cb(file_path) - } - }) + fs.writeFileSync(file_path, options.content) + if (cb) { + cb(file_path) + } } if (options.project_file) { Prop.file_path = file_path @@ -654,18 +655,20 @@ document.body.ondrop = function(event) { (function() { var path = fileNames[i].path var this_i = i; - fs.readFile(path, 'utf-8', function (err, data) { - if (err) { - console.log(err) - if (!errant && handler.errorbox !== false) { - Blockbench.showMessageBox({ - translateKey: 'file_not_found', - icon: 'error_outline' - }) - } - errant = true - return; + var data; + try { + data = fs.readFileSync(path, 'utf-8') + } catch (err) { + console.log(err) + if (!errant && handler.errorbox !== false) { + Blockbench.showMessageBox({ + translateKey: 'file_not_found', + icon: 'error_outline' + }) } + errant = true + } + if (data) { results[this_i] = { name: pathToName(path, true), path: path, @@ -675,7 +678,7 @@ document.body.ondrop = function(event) { if (result_count === fileNames.length) { handler.cb(results, event) } - }) + } })() } } else { diff --git a/js/app.js b/js/app.js index f3007e8a8..73e694551 100644 --- a/js/app.js +++ b/js/app.js @@ -24,17 +24,20 @@ $(document).ready(function() { shell.openExternal(event.target.href); return true; }); + Prop.zoom = 100 + currentwindow.webContents._getZoomLevel()*12 if (fs.existsSync(app.getPath('userData')+osfs+'backups') === false) { fs.mkdirSync( app.getPath('userData')+osfs+'backups') } createBackup(true) $('.web_only').remove() - if (__dirname.includes('C:\\xampp\\htdocs\\blockbench\\web')) { + if (__dirname.includes('C:\\xampp\\htdocs\\blockbench')) { Blockbench.addFlag('dev') } -}) - -getLatestVersion(true) +}); +(function() { + console.log('Electron '+process.versions.electron+', Node '+process.versions.node) + getLatestVersion(true) +})() //Called on start to show message function getLatestVersion(init) { if (process.platform == 'linux') return; @@ -211,7 +214,7 @@ function changeImageEditor(texture) { function selectImageEditorFile(texture) { electron.dialog.showOpenDialog(currentwindow, { title: tl('message.image_editor.exe'), - filters: [{name: 'Executable Program', extensions: ['exe']}] + filters: [{name: 'Executable Program', extensions: ['exe', 'app']}] }, function(filePaths) { if (filePaths) { settings.image_editor.value = filePaths[0] @@ -251,8 +254,6 @@ function openDefaultTexturePath() { } function findEntityTexture(mob, return_path) { var textures = { - 'chicken': 'chicken', - 'blaze': 'blaze', 'llamaspit': 'llama/spit', 'llama': 'llama/llama_creamy', 'dragon': 'dragon/dragon', @@ -260,7 +261,6 @@ function findEntityTexture(mob, return_path) { 'slime': 'slime/slime', 'slime.armor': 'slime/slime', 'lavaslime': 'slime/magmacube', - 'silverfish': 'silverfish', 'shulker': 'shulker/shulker_undyed', 'rabbit': 'rabbit/brown', 'horse': 'horse/horse_brown', @@ -276,41 +276,30 @@ function findEntityTexture(mob, return_path) { 'skeleton': 'skeleton/skeleton', 'skeleton.wither': 'skeleton/wither_skeleton', 'skeleton.stray': 'skeleton/stray', - 'squid': 'squid', 'spider': 'spider/spider', 'cow': 'cow/cow', 'mooshroom': 'cow/mooshroom', 'sheep.sheared': 'sheep/sheep', 'sheep': 'sheep/sheep', - 'phantom': 'phantom', 'pig': 'pig/pig', - 'bat': 'bat', - 'dolphin': 'dolphin', 'irongolem': 'iron_golem', 'snowgolem': 'snow_golem', 'zombie.villager': 'zombie_villager/zombie_farmer', 'evoker': 'illager/evoker', 'vex': 'vex/vex', - 'vindicator': 'vindicator', 'wolf': 'wolf/wolf', 'ocelot': 'cat/ocelot', 'cat': 'cat/siamese', - 'trident': 'trident', - 'guardian': 'guardian', - 'polarbear': 'polarbear', 'turtle': 'sea_turtle', 'villager': 'villager/farmer', 'villager.witch': 'witch', 'witherBoss': 'wither_boss/wither', - 'agent': 'agent', - 'armor_stand': 'armor_stand', 'parrot': 'parrot/parrot_red_blue', 'bed': 'bed/white', 'player_head': 'steve', 'mob_head': 'skeleton/skeleton', 'dragon_head': 'dragon/dragon', 'boat': 'boat/boat_oak', - 'minecart': 'minecart', 'cod': 'fish/fish', 'pufferfish.small': 'fish/pufferfish', 'pufferfish.mid': 'fish/pufferfish', @@ -318,7 +307,6 @@ function findEntityTexture(mob, return_path) { 'salmon': 'fish/salmon', 'tropicalfish_a': 'fish/tropical_a', 'tropicalfish_b': 'fish/tropical_b', - 'endermite': 'endermite', 'panda': 'panda/panda', } mob = mob.split(':')[0].replace(/^geometry\./, '') @@ -388,85 +376,90 @@ function saveFile(props) { } } function writeFileEntity(content, filepath) { + + Prop.file_path = filepath var model_name = 'geometry.' + (Project.parent.replace(/^geometry\./, '')||'unknown') - fs.readFile(filepath, 'utf-8', function (errx, data) { - var obj = {} - if (content.bones && content.bones.length) { - var has_parents = false; - for (var i = 0; i < content.bones.length && !has_parents; i++) { - if (content.bones[i].parent) has_parents = true; + var data; + try { + data = fs.readFileSync(filepath, 'utf-8') + } catch (err) {} + var obj = {} + if (content.bones && content.bones.length) { + var has_parents = false; + for (var i = 0; i < content.bones.length && !has_parents; i++) { + if (content.bones[i].parent) has_parents = true; + } + if (has_parents) { + obj.format_version = '1.8.0' + } + } + if (data) { + try { + obj = JSON.parse(data.replace(/\/\*[^(\*\/)]*\*\/|\/\/.*/g, '')) + } catch (err) { + err = err+'' + var answer = electron.dialog.showMessageBox(currentwindow, { + type: 'warning', + buttons: [ + tl('message.bedrock_overwrite_error.backup_overwrite'), + tl('message.bedrock_overwrite_error.overwrite'), + tl('dialog.cancel') + ], + title: 'Blockbench', + message: tl('message.bedrock_overwrite_error.message'), + detail: err, + noLink: false + }) + if (answer === 0) { + var backup_file_name = pathToName(filepath, true) + ' backup ' + new Date().toLocaleString().split(':').join('_') + backup_file_name = filepath.replace(pathToName(filepath, false), backup_file_name) + fs.writeFile(backup_file_name, data, function (err2) { + if (err2) { + console.log('Error saving backup model: ', err2) + } + }) } - if (has_parents) { - obj.format_version = '1.8.0' + if (answer === 2) { + return; } } - if (!errx) { - try { - obj = JSON.parse(data.replace(/\/\*[^(\*\/)]*\*\/|\/\/.*/g, '')) - } catch (err) { - err = err+'' - var answer = electron.dialog.showMessageBox(currentwindow, { - type: 'warning', - buttons: [ - tl('message.bedrock_overwrite_error.backup_overwrite'), - tl('message.bedrock_overwrite_error.overwrite'), - tl('dialog.cancel') - ], - title: 'Blockbench', - message: tl('message.bedrock_overwrite_error.message'), - detail: err, - noLink: false - }) - if (answer === 0) { - var backup_file_name = pathToName(filepath, true) + ' backup ' + new Date().toLocaleString().split(':').join('_') - backup_file_name = filepath.replace(pathToName(filepath, false), backup_file_name) - fs.writeFile(backup_file_name, data, function (err2) { - if (err2) { - console.log('Error saving backup model: ', err2) + if (typeof obj === 'object') { + for (var key in obj) { + if (obj.hasOwnProperty(key) && + obj[key].bones && + typeof obj[key].bones === 'object' && + obj[key].bones.constructor.name === 'Array' + ) { + obj[key].bones.forEach(function(bone) { + if (typeof bone.cubes === 'object' && + bone.cubes.constructor.name === 'Array' + ) { + bone.cubes.forEach(function(c, ci) { + bone.cubes[ci] = new oneLiner(c) + }) } - }) - } - if (answer === 2) { - return; - } - } - if (typeof obj === 'object') { - for (var key in obj) { - if (obj.hasOwnProperty(key) && - obj[key].bones && - typeof obj[key].bones === 'object' && - obj[key].bones.constructor.name === 'Array' - ) { - obj[key].bones.forEach(function(bone) { - if (typeof bone.cubes === 'object' && - bone.cubes.constructor.name === 'Array' - ) { - bone.cubes.forEach(function(c, ci) { - bone.cubes[ci] = new oneLiner(c) - }) - } - }) - } + }) } } } - obj[model_name] = content - content = autoStringify(obj) + } + obj[model_name] = content + content = autoStringify(obj) - fs.writeFile(filepath, content, function (err) { - if (err) { - console.log('Error Saving Entity Model: '+err) - } - Blockbench.showQuickMessage('message.save_entity') - Prop.project_saved = true; - setProjectTitle(pathToName(filepath, false)) - addRecentProject({name: pathToName(filepath, 'mobs_id'), path: filepath}) - if (Blockbench.hasFlag('close_after_saving')) { - closeBlockbenchWindow() - } - }) - }) + try { + fs.writeFileSync(filepath, content) + } catch (err) { + console.log('Error Saving Entity Model: '+err) + return; + } + Blockbench.showQuickMessage('message.save_entity') + Prop.project_saved = true; + setProjectTitle(pathToName(filepath, false)) + addRecentProject({name: pathToName(filepath, 'mobs_id'), path: filepath}) + if (Blockbench.hasFlag('close_after_saving')) { + closeBlockbenchWindow() + } } function writeFileObj(content, filepath) { if (filepath === undefined) { @@ -566,7 +559,6 @@ function createBackup(init) { }) //trimBackups } - //Zoom function setZoomLevel(mode) { switch (mode) { @@ -628,6 +620,7 @@ function showSaveDialog(close) { } function closeBlockbenchWindow() { preventClosing = false; + Blockbench.dispatchEvent('before_closing') if (!Blockbench.hasFlag('update_restart')) { return currentwindow.close(); diff --git a/js/blockbench.js b/js/blockbench.js index e8533329a..7e0660bc6 100644 --- a/js/blockbench.js +++ b/js/blockbench.js @@ -77,7 +77,10 @@ function initializeApp() { //Misc translateUI() - console.log('Blockbench ' + appVersion + (isApp ? ' Desktop' : ' Web ('+capitalizeFirstLetter(Blockbench.browser)+')')) + console.log('Blockbench ' + appVersion + (isApp + ? (' Desktop (' + Blockbench.operating_system +')') + : (' Web ('+capitalizeFirstLetter(Blockbench.browser)+')') + )) if (localStorage.getItem('donated') == 'true') { $('#donation_hint').remove() } @@ -95,7 +98,11 @@ function initializeApp() { main_uv = new UVEditor('main_uv', false, true) main_uv.setToMainSlot() - setupVue() + onVueSetup.funcs.forEach((func) => { + if (typeof func === 'function') { + func() + } + }) //JQuery UI $('#cubes_list').droppable({ @@ -114,9 +121,6 @@ function initializeApp() { $('#texture_list').contextmenu(function(event) { Interface.Panels.textures.menu.show(event) }) - $('#status_bar').contextmenu(function(event) { - Interface.status_bar.menu.show(event) - }) $(window).on( "unload", saveLocalStorages) setupInterface() @@ -124,161 +128,13 @@ function initializeApp() { Modes.options.edit.select() Blockbench.setup_successful = true } -function setupVue() { - outliner = new Vue({ - el: '#cubes_list', - data: { - option: { - root: { - name: 'Model', - isParent: true, - isOpen: true, - selected: false, - onOpened: function () {}, - select: function() {}, - children: TreeElements - } - } - } - }) - - texturelist = new Vue({ - el: '#texture_list', - data: {textures}, - methods: { - toggleP: function(texture) { - textures.forEach(function(t) { - if (t !== texture) { - t.particle = false; - } - }) - texture.particle = !texture.particle - }, - selectT: function(item, event) { - var index = textures.indexOf(item) - textures[index].select() - } - } - }) - texturelist._data.elements = textures - - setupKeybindings() - - var structure = {} - for (var key in settings) { - var category = settings[key].category - if (!category) category = 'general' - - if (!structure[category]) { - structure[category] = { - name: tl('settings.category.'+category), - open: category === 'general', - items: {} - } - } - structure[category].items[key] = settings[key] +function onVueSetup(func) { + if (!onVueSetup.funcs) { + onVueSetup.funcs = [] } - var settingslist = new Vue({ - el: 'ul#settingslist', - data: {structure}, - methods: { - saveSettings: function() { - localStorage.setItem('settings', JSON.stringify(settings)) - }, - toggleCategory: function(category) { - if (!category.open) { - for (var ct in structure) { - structure[ct].open = false - } - } - category.open = !category.open - } - } - }) - - var project_vue = new Vue({ - el: '#project_settings', - data: {Project} - }) - - DisplayMode.vue = new Vue({ - el: '#display_sliders', - data: { - slot: new DisplaySlot() - }, - methods: { - isMirrored: (axis) => { - if (display[display_slot]) { - return display[display_slot].scale[axis] < 0; - } - }, - change: (axis, channel) => { - if (channel === 'scale') { - var val = limitNumber(DisplayMode.slot.scale[axis], 0, 4) - DisplayMode.slot.scale[axis] = val; - if (holding_shift) { - DisplayMode.slot.scale[0] = val; - DisplayMode.slot.scale[1] = val; - DisplayMode.slot.scale[2] = val; - } - } else if (channel === 'translation') { - DisplayMode.slot.translation[axis] = limitNumber(DisplayMode.slot.translation[axis], -80, 80)||0; - } else { - DisplayMode.slot.rotation[axis] = Math.trimDeg(DisplayMode.slot.rotation[axis])||0; - } - DisplayMode.updateDisplayBase() - }, - resetChannel: (channel) => { - Undo.initEdit({display_slots: [display_slot]}) - DisplayMode.slot.extend({[channel]: [0, 0, 0]}) - Undo.finishEdit('reset display') - }, - invert: (axis) => { - Undo.initEdit({display_slots: [display_slot]}) - DisplayMode.slot.mirror[axis] = !DisplayMode.slot.mirror[axis]; - DisplayMode.slot.update() - Undo.finishEdit('mirror display') - }, - start: () => { - Undo.initEdit({display_slots: [display_slot]}) - }, - save: () => { - Undo.finishEdit('change_display') - } - } - }) - - Animator.vue = new Vue({ - el: '#animations_list', - data: { - animations: Animator.animations - } - }) - - - Timeline.vue = new Vue({ - el: '#timeline_inner', - data: { - size: 150, - length: 10, - timecodes: [], - keyframes: [], - marker: Timeline.second - } - }) - - var stats_bar_vue = new Vue({ - el: '#status_bar', - data: {Prop} - }) - - Modes.vue = new Vue({ - el: '#mode_selector', - data: { - options: Modes.options - } - }) + onVueSetup.funcs.push(func) } + function canvasGridSize(shift, ctrl) { if (!shift && !ctrl) { return 16 / limitNumber(settings.edit_size.value, 1, 1024) @@ -335,7 +191,6 @@ function updateSelection() { } Transformer.detach() Transformer.hoverAxis = null; - if (display_mode) { DisplayMode.centerTransformer() } @@ -361,12 +216,12 @@ function updateSelection() { //Interface if (selected.length > 0) { $('.selection_only').css('visibility', 'visible') + main_uv.jquery.size.find('.uv_mapping_overlay').remove() main_uv.loadData() - } else if (selected.length === 0) { + } else { $('.selection_only').css('visibility', 'hidden') } BarItems.cube_counter.update() - $('.uv_mapping_overlay').remove() updateNslideValues() //Misc Blockbench.globalMovement = isMovementGlobal() @@ -536,6 +391,14 @@ const Modes = { selected: false, options: {}, }; +onVueSetup(function() { + Modes.vue = new Vue({ + el: '#mode_selector', + data: { + options: Modes.options + } + }) +}); BARS.defineActions(function() { new Mode({ id: 'edit', @@ -987,7 +850,9 @@ var Vertexsnap = { cube.mesh.updateMatrixWorld() o_vertices.forEach(function(v, id) { var outline_color = '0x'+app_colors.accent.hex.replace('#', '') - var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({color: parseInt(outline_color)})) + //Each vertex needs it's own material for hovering + var material = new THREE.MeshBasicMaterial({color: parseInt(outline_color)}) + var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material) var pos = mesh.position.copy(v) pos.applyMatrix4(cube.mesh.matrixWorld) pos.addScalar(8) @@ -1169,7 +1034,7 @@ const entityMode = { $('.entity_mode_only').show() Modes.options.edit.select() //UV - main_uv.buildDom().setToMainSlot().setFace('north') + //main_uv.buildDom(true).setToMainSlot().setFace('north') main_uv.autoGrid = true main_uv.setGrid() //Update @@ -1186,7 +1051,11 @@ const entityMode = { //Rotation Menu $('input#cube_rotate').attr('min', '-67.5').attr('max', '67.5').attr('step', '22.5').removeClass('entity_mode') //UV - main_uv.buildDom(true).setToMainSlot() + //main_uv.buildDom(true).setToMainSlot() + main_uv.autoGrid = false + main_uv.showing_overlays = false + main_uv.displayAllMappingOverlays() + main_uv.setGrid() if (textures[0]) { textures[0].load() } diff --git a/js/display.js b/js/display.js index 7fe799664..a054ca4a1 100644 --- a/js/display.js +++ b/js/display.js @@ -37,9 +37,10 @@ class DisplaySlot { if (!this.scale.allEqual(1) || !this.mirror.allEqual(false)) { build.scale = this.scale.slice() if (!this.mirror.allEqual(false)) { - build.scale.forEach((s, i) => { - build.scale[i] *= s ? -1 : 1; - }) + + for (var i = 0; i < 3; i++) { + build.scale[i] *= this.mirror[i] ? -1 : 1; + } } } if (Object.keys(build).length) { @@ -53,6 +54,7 @@ class DisplaySlot { if (data.translation) Merge.number(this.translation, data.translation, i) if (data.mirror) Merge.boolean(this.mirror, data.mirror, i) if (data.scale) Merge.number(this.scale, data.scale, i) + this.scale[i] = Math.abs(this.scale[i]) if (data.scale && data.scale[i] < 0) this.mirror[i] = true; } this.update() @@ -141,20 +143,14 @@ class refModel { break; case 'monitor': this.onload = function() { - if (display_slot === 'firstperson_righthand') { - setDisplayArea(-20.8, -8.4, 9, 0, 270, 0, 1,1,1) - } else if (display_slot === 'firstperson_lefthand') { - setDisplayArea(-20.5, -8.4, -9, 0, 270, 0, 1,1,1) - } + var side = display_slot.includes('left') ? -1 : 1; + setDisplayArea(side*9, -8.4, 20.8, 0, 0, 0, 1,1,1) } break; case 'bow': this.onload = function() { - if (display_slot === 'firstperson_righthand') { - setDisplayArea(-24.7, -5.6, 5.4, 64, -115, 55, 1,1,1) - } else if (display_slot === 'firstperson_lefthand') { - setDisplayArea(-24.7, -5.6, -5.4, -64, -65, -55, 1,1,1) - } + var side = display_slot.includes('left') ? -1 : 1; + setDisplayArea(side*5.4, -5.6, 24.7, side*64, side*-25, side*55, 1,1,1) } break; } @@ -286,9 +282,9 @@ class refModel { //Body "size": [8, 12, 4], "pos": [0, 18, 0], - "north": {"uv": [6.968, 5.032, 5.032, 7.968]}, + "north": {"uv": [5.032, 5.032, 6.968, 7.968]}, "east": {"uv": [7.968, 5.032, 7.032, 7.968]}, - "south": {"uv": [9.968, 5.032, 8.032, 7.968]}, + "south": {"uv": [8.032, 5.032, 9.968, 7.968]}, "west": {"uv": [4.968, 5.032, 4.032, 7.968]}, "up": {"uv": [5.032, 4.968, 6.968, 4.032]}, "down": {"uv": [7.032, 4.032, 8.968, 4.968]} @@ -297,9 +293,9 @@ class refModel { //Body Layer "size": [8.5, 12.5, 4.5], "pos": [0, 18, 0], - "north": {"uv": [6.968, 9.032, 5.032, 11.968]}, + "north": {"uv": [5.032, 9.032, 6.968, 11.968]}, "east": {"uv": [7.968, 9.032, 7.032, 11.968]}, - "south": {"uv": [9.968, 9.032, 8.032, 11.968]}, + "south": {"uv": [8.032, 9.032, 9.968, 11.968]}, "west": {"uv": [4.968, 9.032, 4.032, 11.968]}, "up": {"uv": [5.032, 8.968, 6.968, 8.032]}, "down": {"uv": [7.032, 8.032, 8.968, 8.968]} @@ -1175,10 +1171,10 @@ class refModel { } buildMonitor() { this.buildModel([ - {"size": [0.1, 8, 8], "pos": [-31.2, 4.93, 0], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, - {"size": [0.1, 8, 8], "pos": [-31.2, -4.93, 0], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, - {"size": [0.1, 8, 8], "pos": [-31.2, 0, 5.65], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, - {"size": [0.1, 8, 8], "pos": [-31.2, 0, -5.65], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}} + {"size": [8, 8, 0.1], "pos": [0, 4.93, 31.20], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, + {"size": [8, 8, 0.1], "pos": [0, -4.93, 31.20], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, + {"size": [8, 8, 0.1], "pos": [5.65, 0, 31.2], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}}, + {"size": [8, 8, 0.1], "pos": [-5.65, 0, 31.2], "origin": [0, 0, 0], "north":{"uv":[0,0,0,0]},"east":{"uv":[0,0,0,0]},"south":{"uv":[0,0,0,0]},"west":{"uv":[0,0,16,16]},"up":{"uv":[0,0,0,0]},"down":{"uv":[0,0,0,0]}} ], 'black') } buildBlock() { @@ -1234,8 +1230,13 @@ window.displayReferenceObjects = { case 'icon-monitor': icon = 'fa fa-asterisk'; break; } var button = $( - '
    '+ - '
    ' + `
    + + +
    ` ) $('#display_ref_bar').append(button) if (i === 0) { @@ -1411,7 +1412,7 @@ DisplayMode.createPreset = function() { display_presets.push(preset) displayReferenceObjects.slots.forEach(function(s) { - if ($('#'+s+'_save').is(':checked')) { + if ($('#'+s+'_save').is(':checked') && display[s]) { preset.areas[s] = display[s].copy() } }) @@ -1459,7 +1460,6 @@ function loadDisp(key) { //Loads The Menu and slider values, common for all Radi } display_preview.controls.enabled = true; ground_animation = false; - $('input#translation_z').prop('disabled', false) $('#display_crosshair').detach() display_preview.camPers.setFocalLength(45) @@ -1481,9 +1481,8 @@ DisplayMode.loadThirdLeft = function() { //Loader } DisplayMode.loadFirstRight = function() { //Loader loadDisp('firstperson_righthand') - setDisplayArea(-20.8, -8.4, 9, 0, 270, 0, 1,1,1) display_preview.camPers.setFocalLength(12) - display_preview.camPers.position.set(-32.4, 0, 0) + display_preview.camPers.position.set(0, 0, 32.4) display_preview.controls.target.set(0,0,0) display_preview.controls.enabled = false displayReferenceObjects.bar(['monitor', 'bow']) @@ -1491,9 +1490,8 @@ DisplayMode.loadFirstRight = function() { //Loader } DisplayMode.loadFirstLeft = function() { //Loader loadDisp('firstperson_lefthand') - setDisplayArea(-20.5, -8.4, -9, 0, 270, 0, 1,1,1) display_preview.camPers.setFocalLength(12) - display_preview.camPers.position.set(-32.4, 0, 0) + display_preview.camPers.position.set(0, 0, 32.4) display_preview.controls.target.set(0,0,0) display_preview.controls.enabled = false displayReferenceObjects.bar(['monitor', 'bow']) @@ -1508,7 +1506,6 @@ DisplayMode.loadGUI = function() { //Loader setDisplayArea(0, 0, 0, 0, 0, 0, 0.4, 0.4, 0.4) display_preview.camOrtho.zoom = 1 display_preview.controls.target.set(0,0,0) - //controls.enabled = false display_preview.setOrthographicCamera(2) display_preview.camOrtho.position.set(0,0,32) displayReferenceObjects.bar(['inventory_nine', 'inventory_full', 'hud']) @@ -1688,11 +1685,67 @@ function updateDisplaySkin() { } //displayReferenceObjects.refmodels.player.material } + + +onVueSetup(function() { + DisplayMode.vue = new Vue({ + el: '#display_sliders', + data: { + slot: new DisplaySlot() + }, + methods: { + isMirrored: (axis) => { + if (display[display_slot]) { + return display[display_slot].scale[axis] < 0; + } + }, + change: (axis, channel) => { + if (channel === 'scale') { + var val = limitNumber(DisplayMode.slot.scale[axis], 0, 4) + DisplayMode.slot.scale[axis] = val; + if (holding_shift) { + DisplayMode.slot.scale[0] = val; + DisplayMode.slot.scale[1] = val; + DisplayMode.slot.scale[2] = val; + } + } else if (channel === 'translation') { + DisplayMode.slot.translation[axis] = limitNumber(DisplayMode.slot.translation[axis], -80, 80)||0; + } else { + DisplayMode.slot.rotation[axis] = Math.trimDeg(DisplayMode.slot.rotation[axis])||0; + } + DisplayMode.updateDisplayBase() + }, + resetChannel: (channel) => { + var v = channel === 'scale' ? 1 : 0; + Undo.initEdit({display_slots: [display_slot]}) + DisplayMode.slot.extend({[channel]: [v, v, v]}) + if (channel === 'scale') { + DisplayMode.slot.extend({mirror: [false, false, false]}) + } + Undo.finishEdit('reset display') + }, + invert: (axis) => { + Undo.initEdit({display_slots: [display_slot]}) + DisplayMode.slot.mirror[axis] = !DisplayMode.slot.mirror[axis]; + DisplayMode.slot.update() + Undo.finishEdit('mirror display') + }, + start: () => { + Undo.initEdit({display_slots: [display_slot]}) + }, + save: () => { + Undo.finishEdit('change_display') + } + } + }) +}) + BARS.defineActions(function() { new Action({ id: 'add_display_preset', icon: 'add', category: 'display', + condition: () => display_mode, click: function () {showDialog('create_preset')} }) }) diff --git a/js/elements.js b/js/elements.js index 033a33e82..91d6a4e83 100644 --- a/js/elements.js +++ b/js/elements.js @@ -81,8 +81,46 @@ var selected_group; //Cubes class Face { - constructor() { + constructor(direction, data) { + this.direction = direction || 'north'; + this.reset() this.uv = [0, 0, canvasGridSize(), canvasGridSize()] + if (data) { + this.extend(data) + } + } + extend(object) { + if (object.texture === null) { + this.texture = null + } else { + Merge.string(this, object, 'texture') + } + Merge.string(this, object, 'cullface') + Merge.number(this, object, 'rotation') + Merge.number(this, object, 'tint') + if (object.uv) { + Merge.number(this.uv, object.uv, 0) + Merge.number(this.uv, object.uv, 1) + Merge.number(this.uv, object.uv, 2) + Merge.number(this.uv, object.uv, 3) + } + return this; + } + reset() { + this.uv = [0, 0, 0, 0]; + this.rotation = 0; + this.texture = false; + this.cullface = ''; + this.enabled = true; + this.tint = false; + return this; + } + getTexture() { + if (typeof this.texture === 'string') { + return textures.findInArray('uuid', this.texture) + } else { + return this.texture; + } } } class OutlinerElement { @@ -129,14 +167,20 @@ class OutlinerElement { if (group.type === 'cube') { if (group.parent === 'root') { index = TreeElements.indexOf(group) + if (index === TreeElements.length-1) { + index++; + } group = 'root' } else { index = group.parent.children.indexOf(group) + if (index === group.parent.children.length-1) { + index++; + } group = group.parent } } } - if (group != 'root' && group.type === 'group') { + if (group.type === 'group') { var i = 0 var level = group; while (i < 50) { @@ -312,6 +356,12 @@ class OutlinerElement { } return iterate(this.parent, 0) } + get mirror_uv() { + return !this.shade; + } + set mirror_uv(val) { + this.shade = !val; + } } class Cube extends OutlinerElement { constructor(data, uuid) { @@ -336,7 +386,14 @@ class Cube extends OutlinerElement { this.export = true; this.parent = 'root'; - this.faces = {north: new Face(), east: new Face(), south: new Face(), west: new Face(), up: new Face(), down: new Face()} + this.faces = { + north: new Face('north'), + east: new Face('east'), + south: new Face('south'), + west: new Face('west'), + up: new Face('up'), + down: new Face('down') + } if (data && typeof data === 'object') { this.extend(data) } @@ -522,20 +579,7 @@ class Cube extends OutlinerElement { if (object.faces) { for (var face in this.faces) { if (this.faces.hasOwnProperty(face) && object.faces.hasOwnProperty(face)) { - if (object.faces[face].texture === null) { - this.faces[face].texture = null - } else { - Merge.string(this.faces[face], object.faces[face], 'texture') - } - Merge.string(this.faces[face], object.faces[face], 'cullface') - Merge.number(this.faces[face], object.faces[face], 'rotation') - Merge.number(this.faces[face], object.faces[face], 'tintindex') - if (object.faces[face].uv) { - Merge.number(this.faces[face].uv, object.faces[face].uv, 0) - Merge.number(this.faces[face].uv, object.faces[face].uv, 1) - Merge.number(this.faces[face].uv, object.faces[face].uv, 2) - Merge.number(this.faces[face].uv, object.faces[face].uv, 3) - } + this.faces[face].extend(object.faces[face]) } } } @@ -614,22 +658,22 @@ class Cube extends OutlinerElement { this.faces.north.rotation= rotateUVFace(this.faces.north.rotation, 2) this.faces.down.rotation = rotateUVFace(this.faces.down.rotation, 2) - var temp = this.faces.north - this.faces.north = this.faces.down - this.faces.down = this.faces.south - this.faces.south = this.faces.up - this.faces.up = temp + var temp = new Face(true, this.faces.north) + this.faces.north.extend(this.faces.down) + this.faces.down.extend(this.faces.south) + this.faces.south.extend(this.faces.up) + this.faces.up.extend(temp) } else if (axis === 1) { this.faces.up.rotation= rotateUVFace(this.faces.up.rotation, 1) this.faces.down.rotation = rotateUVFace(this.faces.down.rotation, 3) - var temp = this.faces.north - this.faces.north = this.faces.west - this.faces.west = this.faces.south - this.faces.south = this.faces.east - this.faces.east = temp + var temp = new Face(true, this.faces.north) + this.faces.north.extend(this.faces.west) + this.faces.west.extend(this.faces.south) + this.faces.south.extend(this.faces.east) + this.faces.east.extend(temp) } else if (axis === 2) { @@ -641,11 +685,11 @@ class Cube extends OutlinerElement { this.faces.west.rotation = rotateUVFace(this.faces.west.rotation, 3) this.faces.down.rotation = rotateUVFace(this.faces.down.rotation, 3) - var temp = this.faces.east - this.faces.east = this.faces.down - this.faces.down = this.faces.west - this.faces.west = this.faces.up - this.faces.up = temp + var temp = new Face(true, this.faces.east) + this.faces.east.extend(this.faces.down) + this.faces.down.extend(this.faces.west) + this.faces.west.extend(this.faces.up) + this.faces.up.extend(temp) } @@ -708,8 +752,8 @@ class Cube extends OutlinerElement { case 2: switchFaces = ['south', 'north']; break; } var x = this.faces[switchFaces[0]] - this.faces[switchFaces[0]] = this.faces[switchFaces[1]] - this.faces[switchFaces[1]] = x + this.faces[switchFaces[0]].extend(this.faces[switchFaces[1]]) + this.faces[switchFaces[1]].extend(x) //UV if (axis === 1) { @@ -735,6 +779,58 @@ class Cube extends OutlinerElement { Canvas.adaptObjectFaces(this) Canvas.updateUV(this) } + transferOrigin(origin) { + if (!this.mesh) return; + var q = this.mesh.getWorldQuaternion(new THREE.Quaternion()) + var shift = new THREE.Vector3( + this.origin[0] - origin[0], + this.origin[1] - origin[1], + this.origin[2] - origin[2], + ) + var dq = new THREE.Vector3().copy(shift) + dq.applyQuaternion(q) + shift.sub(dq) + shift.applyQuaternion(q.inverse()) + + this.from[0] += shift.x; + this.from[1] += shift.y; + this.from[2] += shift.z; + this.to[0] += shift.x; + this.to[1] += shift.y; + this.to[2] += shift.z; + + this.origin = origin.slice(); + + Canvas.adaptObjectPosition(this) + return this; + } + getWorldCenter() { + var m = this.mesh; + var pos = new THREE.Vector3( + this.from[0] + this.size(0)/2, + this.from[1] + this.size(1)/2, + this.from[2] + this.size(2)/2 + ) + if (!Blockbench.entity_mode) { + + pos.x -= this.origin[0] + pos.y -= this.origin[1] + pos.z -= this.origin[2] + var r = m.getWorldQuaternion(new THREE.Quaternion()) + pos.applyQuaternion(r) + pos.x += this.origin[0] + pos.y += this.origin[1] + pos.z += this.origin[2] + } else { + var r = m.getWorldQuaternion(new THREE.Quaternion()) + pos.applyQuaternion(r) + pos.add(m.getWorldPosition(new THREE.Vector3())) + pos.x += 8 + pos.y += 8 + pos.z += 8 + } + return pos; + } setColor(index) { this.color = index; if (this.visibility) { @@ -797,8 +893,8 @@ class Cube extends OutlinerElement { var sides = faces } var id = null - if (texture && texture.id !== undefined) { - id = '#'+texture.id + if (texture) { + id = texture.uuid } else if (texture === 'blank') { id = undefined; } @@ -888,14 +984,6 @@ class Cube extends OutlinerElement { var sy = scope.faces[face].uv[1] var rot = scope.faces[face].rotation - //Use Texture resolution - /* - var tex = getTextureById(scope.faces[face].texture) - if (tex && tex.res && tex.res != 16) { - size[0] *= 16 / tex.res - size[1] *= 16 / tex.res - }*/ - //Match To Rotation if (rot === 90 || rot === 270) { size.reverse() @@ -1046,6 +1134,7 @@ class Group extends OutlinerElement { this.export = true; this.autouv = 0; this.parent = 'root'; + this.isOpen = false; if (typeof data === 'object') { this.extend(data) @@ -1096,6 +1185,7 @@ class Group extends OutlinerElement { }) } updateSelection() + return this; } selectChildren(event) { var scope = this; @@ -1118,6 +1208,7 @@ class Group extends OutlinerElement { s.selectLow() }) updateSelection() + return this; } selectLow(highlight) { //Only Select @@ -1127,6 +1218,7 @@ class Group extends OutlinerElement { this.children.forEach(function(s) { s.selectLow(highlight) }) + return this; } unselect() { if (this.selected === false) return; @@ -1167,6 +1259,7 @@ class Group extends OutlinerElement { Merge.number(this, object, 'autouv') Merge.boolean(this, object, 'export') Merge.boolean(this, object, 'visibility') + return this; } openUp() { this.isOpen = true @@ -1247,6 +1340,7 @@ class Group extends OutlinerElement { } showContextMenu(event) { this.menu.open(event, this) + return this; } setMaterial(material) { var scope = this; @@ -1259,6 +1353,7 @@ class Group extends OutlinerElement { } Undo.finishEdit('bone_material') }) + return this; } sortContent() { Undo.initEdit({outliner: true}) @@ -1267,22 +1362,26 @@ class Group extends OutlinerElement { return sort_collator.compare(a.name, b.name) }); Undo.finishEdit('sort') + return this; } duplicate(destination) { function duplicateArray(g1, g2) { var array = g1.children var i = 0; while (i < array.length) { - if (array[i].type === 'cube') { + if (array[i].type !== 'group') { var dupl = new Cube(array[i]) dupl.addTo(g2, false) if (destination !== 'cache') { - dupl.init() + dupl.init(false) } else { dupl.parent = undefined } } else { var dupl = array[i].getChildlessCopy() + if (Blockbench.entity_mode && destination !== 'cache') { + dupl.createUniqueName() + } duplicateArray(array[i], dupl) dupl.addTo(g2, false) } @@ -1335,10 +1434,10 @@ class Group extends OutlinerElement { obj.autouv = this.autouv; } if (this.origin.join('_') !== '8_8_8' || Blockbench.entity_mode) { - obj.origin = this.origin + obj.origin = this.origin.slice() } if (this.rotation.join('_') !== '0_0_0') { - obj.rotation = this.rotation + obj.rotation = this.rotation.slice() } if (this.reset) { obj.reset = true @@ -1657,13 +1756,12 @@ function loadOutlinerDraggable() { } else if ($(ui.draggable).hasClass('texture')) { //Texture - var id = $(ui.helper).attr('texid') - var sides = ['north', 'east', 'south', 'west', 'up', 'down'] + var uuid = $(ui.helper).attr('texid') if (target.type === 'group') { - target.forEachChild(function(s) { - sides.forEach(function(side) { - s.faces[side].texture = '#'+id - }) + target.forEachChild(function(cube) { + for (var face in cube.faces) { + cube.faces[face].texture = uuid + } }, 'cube') } else { var targets; @@ -1673,10 +1771,10 @@ function loadOutlinerDraggable() { targets = [target] } - targets.forEach(function(target) { - sides.forEach(function(side) { - target.faces[side].texture = '#'+id - }) + targets.forEach(function(cube) { + for (var face in cube.faces) { + cube.faces[face].texture = uuid + } }) } main_uv.loadData() @@ -1718,19 +1816,20 @@ function dropOutlinerObjects(item, target, event) { if (item && item !== target) { if (event.altKey) { if (item.type === 'cube') { - var cube = new Cube(item).addTo(target).init() + var cube = new Cube(item).addTo(target, false).init(false) selected.push(cube) } else { - item.duplicate().addTo(target).select() + item.duplicate().addTo(target, false).select() } } else { - item.addTo(target) + item.addTo(target, false) if (Blockbench.entity_mode) { updatePosRecursive(item) } } } }) + loadOutlinerDraggable() if (event.altKey) { updateSelection() Undo.finishEdit('drag', {cubes: selected, outliner: true, selection: true}) @@ -1753,10 +1852,9 @@ function addCube() { } if (textures.length && Blockbench.entity_mode) { - var sides = ['north', 'east', 'south', 'west', 'up', 'down'] - sides.forEach(function(side) { - base_cube.faces[side].texture = '#'+textures[0].id - }) + for (var face in base_cube.faces) { + base_cube.faces[face].texture = textures[0].uuid + } main_uv.loadData() } if (Blockbench.entity_mode) { @@ -1878,6 +1976,11 @@ function stopRenameCubes(save) { } $('.outliner_object input.renaming').attr('disabled', true).removeClass('renaming') $('body').focus() + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } Blockbench.removeFlag('renaming') } } @@ -1906,9 +2009,31 @@ function toggleCubeProperty(key) { if (key === 'visibility') { Canvas.updateVisibility() } + if (key === 'shade' && Blockbench.entity_mode) { + Canvas.updateUVs() + } Undo.finishEdit('toggle_prop') } +onVueSetup(function() { + outliner = new Vue({ + el: '#cubes_list', + data: { + option: { + root: { + name: 'Model', + isParent: true, + isOpen: true, + selected: false, + onOpened: function () {}, + select: function() {}, + children: TreeElements + } + } + } + }) +}) + BARS.defineActions(function() { new Action({ id: 'add_cube', diff --git a/js/interface.js b/js/interface.js index 9a6069737..a6df59de0 100644 --- a/js/interface.js +++ b/js/interface.js @@ -384,6 +384,11 @@ function setupInterface() { } }) + var stats_bar_vue = new Vue({ + el: '#status_bar', + data: {Prop} + }) + //Clickbinds $('header' ).click( function() { setActivePanel('header' )}) $('#preview' ).click(function() { setActivePanel('preview' )}) @@ -400,6 +405,9 @@ function setupInterface() { if (open_menu && $('.contextMenu').find(event.target).length === 0 && $('.menu_bar_point.opened:hover').length === 0) { open_menu.hide(); } + if (ActionControl.open && $('#action_selector').find(event.target).length === 0) { + ActionControl.hide(); + } if ($(event.target).is('input.cube_name:not([disabled])') === false) { stopRenameCubes() } @@ -743,6 +751,7 @@ var splashScreen = { }); } showDialog('welcome_screen') + Blockbench.dispatchEvent('show_splash_screen') localStorage.setItem('welcomed_version', appVersion) }) }, diff --git a/js/io.js b/js/io.js index a94bc4c81..f0e377e5e 100644 --- a/js/io.js +++ b/js/io.js @@ -33,6 +33,7 @@ function newProject(entity_mode) { entityMode.leave(); } $('#var_placeholder_area').val('') + Blockbench.dispatchEvent('new_project') return true; } else { return false; @@ -70,15 +71,15 @@ function setupDragHandlers() { function(files, event) { var texture_li = $(event.target).parents('li.texture') if (texture_li.length) { - var tex = getTextureById(texture_li.attr('texid')) + var tex = textures.findInArray('uuid', texture_li.attr('texid')) if (tex) { tex.fromFile(files[0]) + return; } - } else { - files.forEach(function(f) { - new Texture().fromFile(f).add().fillParticle() - }) } + files.forEach(function(f) { + new Texture().fromFile(f).add().fillParticle() + }) } ) } @@ -162,6 +163,9 @@ function loadBlockModel(model, filepath, add) { if (obj.faces[face].texture === '#missing') { delete base_cube.faces[face].texture; } + if (obj.faces[face].tintindex !== undefined) { + base_cube.faces[face].tint = true; + } } } if (!uv_stated) { @@ -247,29 +251,23 @@ function loadBlockModel(model, filepath, add) { } } //Get Rid Of ID overlapping - textures.forEach(function(t, i) { - if (i >= previous_texture_length) { - if (getTexturesById(t.id).length > 1) { - var before = t.id - t.id = Prop.added_models + '_' + t.id - elements.forEach(function(s, si) { - if (si >= previous_length) { - for (var face in s.faces) { - if (s.faces[face].texture === '#'+before) { - s.faces[face].texture = '#'+t.id - } - } - } - }) - } + for (var i = previous_texture_length; i < textures.length; i++) { + var t = textures[i] + var import_id = t.id; + if (getTexturesById(t.id).length > 1) { + t.id = Prop.added_models + '_' + t.id } - }) + elements.forEach(function(s, si) { + for (var face in s.faces) { + if (s.faces[face].texture === '#'+import_id) { + s.faces[face].texture = t.uuid + } + } + }) + } //Select Last Texture if (textures.length > 0) { - textures.forEach(function(s) { - s.selected = false; - }) - textures[textures.length-1].selected = true; + textures[textures.length-1].select(); } } @@ -336,7 +334,7 @@ function loadJEMModel(model) { name: b.part, origin: b.translate, rotation: b.rotate, - shade: !(b.mirrorTexture && b.mirrorTexture.includes('u')) + mirror_uv: (b.mirrorTexture && b.mirrorTexture.includes('u')) }) group.origin[1] *= -1 group.origin[2] *= -1 @@ -595,7 +593,7 @@ function loadPEModel(data) { group.rotation[ri] *= -1 }) - group.shade = !b.mirror + group.mirror_uv = b.mirror === true group.reset = b.reset === true if (b.cubes) { @@ -618,9 +616,9 @@ function loadPEModel(data) { base_cube.inflate = s.inflate } if (s.mirror === undefined) { - base_cube.shade = group.shade + base_cube.mirror_uv = group.mirror_uv } else { - base_cube.shade = !s.mirror + base_cube.mirror_uv = s.mirror === true } elements.push(base_cube) base_cube.addTo(group, false) @@ -805,20 +803,20 @@ var Extruder = { } draw_y++; } - var current_cube = new Cube({name: cube_name+'_'+cube_nr, autouv: 0}) - - current_cube.from = [rect.x*scale_i, 0, rect.y*scale_i] - current_cube.to = [(rect.x2+1)*scale_i, scale_i, (rect.y2+1)*scale_i] - - //Sides - current_cube.faces.up = {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index} - current_cube.faces.down = {uv:[rect.x*scale_i, (rect.y2+1)*scale_i, (rect.x2+1)*scale_i, rect.y*scale_i], texture: texture_index} - - current_cube.faces.north = {uv:[(rect.x2+1)*scale_i, rect.y*scale_i, rect.x*scale_i, (rect.y+1)*scale_i], texture: texture_index} - current_cube.faces.south = {uv:[rect.x*scale_i, rect.y2*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index} - - current_cube.faces.east = {uv:[rect.x2*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 90} - current_cube.faces.west = {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 270} + var current_cube = new Cube({ + name: cube_name+'_'+cube_nr, + autouv: 0, + from: [rect.x*scale_i, 0, rect.y*scale_i], + to: [(rect.x2+1)*scale_i, scale_i, (rect.y2+1)*scale_i], + faces: { + up: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index}, + down: {uv:[rect.x*scale_i, (rect.y2+1)*scale_i, (rect.x2+1)*scale_i, rect.y*scale_i], texture: texture_index}, + north: {uv:[(rect.x2+1)*scale_i, rect.y*scale_i, rect.x*scale_i, (rect.y+1)*scale_i], texture: texture_index}, + south: {uv:[rect.x*scale_i, rect.y2*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index}, + east: {uv:[rect.x2*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 90}, + west: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 270} + } + }) elements.push(current_cube) selected.push(elements[elements.length-1]) @@ -847,17 +845,6 @@ var Extruder = { } } //Export -class oneLiner { - constructor(data) { - if (data !== undefined) { - for (var key in data) { - if (data.hasOwnProperty(key)) { - this[key] = data[key] - } - } - } - } -} function buildBlockModel(options) { if (options === undefined) options = {} var clear_elements = [] @@ -917,31 +904,28 @@ function buildBlockModel(options) { tag.rotation = s.faces[face].rotation } if (s.faces[face].texture) { - tag.texture = s.faces[face].texture + var tex = s.faces[face].getTexture() + if (tex) { + tag.texture = '#' + tex.id + textures_used.safePush(tex) + } element_has_texture = true - } else { + } + if (!tag.texture) { tag.texture = '#missing' } - if (s.faces[face].cullface !== undefined) { + if (s.faces[face].cullface) { tag.cullface = s.faces[face].cullface } - if (s.faces[face].tintindex !== undefined) { - tag.tintindex = s.faces[face].tintindex + if (s.faces[face].tint) { + tag.tintindex = 0 } e_faces[face] = tag } } } //Gather Textures - if (element_has_texture) { - for (var face in s.faces) { - if (s.faces.hasOwnProperty(face)) { - if (!textures_used.includes(s.faces[face].texture)) { - textures_used.push(s.faces[face].texture) - } - } - } - } else { + if (!element_has_texture) { element.color = s.color } element.faces = e_faces @@ -992,7 +976,7 @@ function buildBlockModel(options) { var texturesObj = {} var hasUnsavedTextures = false textures.forEach(function(t, i){ - if (!textures_used.includes('#'+t.id) && !isTexturesOnlyModel) return; + if (!textures_used.includes(t) && !isTexturesOnlyModel) return; texturesObj[t.id] = t.javaTextureLink(options.backup) if (t.particle) { @@ -1038,12 +1022,9 @@ function buildBlockModel(options) { var entries = 0; for (var i in DisplayMode.slots) { var key = DisplayMode.slots[i] - if (DisplayMode.slots.hasOwnProperty(i) && display[key]) { - var slot = display[key].export() - if (slot) { - new_display[key] = display[key].export() - entries++; - } + if (DisplayMode.slots.hasOwnProperty(i) && display[key] && display[key].export) { + new_display[key] = display[key].export() + entries++; } } if (entries) { @@ -1081,7 +1062,25 @@ function buildEntityModel(options) { var cube_count = 0; var visible_box = new THREE.Box3() - getAllOutlinerGroups().forEach(function(g) { + var groups = getAllOutlinerGroups() + var loose_cubes = []; + TreeElements.forEach(obj => { + if (obj.type === 'cube') { + loose_cubes.push(obj) + } + }) + if (loose_cubes.length) { + groups.splice(0, 0, { + type: 'group', + parent: 'root', + name: 'unknown_bone', + origin: [0, 0, 0], + rotation: [0], + children: loose_cubes + }) + } + + groups.forEach(function(g) { if (g.type !== 'group') return; //Bone var bone = {} @@ -1091,7 +1090,7 @@ function buildEntityModel(options) { } bone.pivot = g.origin.slice() bone.pivot[0] *= -1 - if (g.rotation.join('_') !== '0_0_0') { + if (!g.rotation.allEqual(0)) { bone.rotation = g.rotation.slice() bone.rotation.forEach(function(br, ri) { bone.rotation[ri] *= -1 @@ -1100,7 +1099,7 @@ function buildEntityModel(options) { if (g.reset) { bone.reset = true } - if (!g.shade) { + if (g.mirror_uv) { bone.mirror = true } if (g.material) { @@ -1121,8 +1120,8 @@ function buildEntityModel(options) { if (s.inflate && typeof s.inflate === 'number') { cube.inflate = s.inflate } - if (s.shade === !!bone.mirror) { - cube.mirror = !s.shade + if (s.mirror_uv === !bone.mirror) { + cube.mirror = s.mirror_uv } //Visible Bounds var mesh = s.mesh @@ -1212,7 +1211,6 @@ function buildJPMModel(options) { } else { boxes.push(box) } - submodels.push(submodel) }) if (boxes.length) { jpm.boxes = boxes @@ -1250,7 +1248,7 @@ function buildJEMModel(options) { if (g.rotation.join('_') !== '0_0_0') { bone.rotate = g.rotation.slice() } - if (g.shade === false) { + if (g.mirror_uv) { bone.mirrorTexture = 'u' } //Cubes @@ -1282,7 +1280,7 @@ function buildJEMModel(options) { if (s.inflate && typeof s.inflate === 'number') { cube.sizeAdd = s.inflate } - if (s.shade === g.shade) { + if (s.mirror_uv === g.mirror_uv) { bone.boxes.push(cube) } else { mirrored_boxes.push(cube) @@ -1397,7 +1395,7 @@ function buildClassModel(options) { obj.size(1, true), obj.size(2, true), F(obj.inflate), - !obj.shade + obj.mirror_uv ] bone.lines.push( `${id}.cubeList.add(new ModelBox(${ values.join(', ') }));` diff --git a/js/keyboard.js b/js/keyboard.js index 47a07a9db..177927a12 100644 --- a/js/keyboard.js +++ b/js/keyboard.js @@ -165,7 +165,7 @@ class Keybind { } } -function setupKeybindings() { +onVueSetup(function() { Keybinds.vue = new Vue({ el: 'ul#keybindlist', data: {structure: Keybinds.structure}, @@ -203,14 +203,9 @@ function setupKeybindings() { } category.open = !category.open } - /* - change - reset - clear - */ } }) -} +}) $(document).keydown(function(e) { if (Keybinds.recording) return; @@ -283,13 +278,16 @@ $(document).keydown(function(e) { } else if (open_interface && typeof open_interface == 'object' && open_interface.hide) { if (Keybinds.extra.confirm.keybind.isTriggered(e)) { - open_interface.confirm() + open_interface.confirm(e) used = true } else if (Keybinds.extra.cancel.keybind.isTriggered(e)) { - open_interface.hide() + open_interface.hide(e) used = true } } + if (ActionControl.open) { + used = ActionControl.handleKeys(e) || used + } if (used) { e.preventDefault() } diff --git a/js/molang.js b/js/molang.js index ee475c6df..00fd46fab 100644 --- a/js/molang.js +++ b/js/molang.js @@ -250,7 +250,7 @@ function previewVariableValue(name, time) { return 1 } else if (name === 'false') { return 0 - } else if (name === 'global.anim_time' || name === 'Params.AnimTime' || name === 'Params.LifeTime' || name === 'global.life_time' ) { + } else if (name === 'global.anim_time' || name === 'time' || name === 'query.life_time' ) { return time } else { var inputs = $('#var_placeholder_area').val().split('\n') diff --git a/js/painter.js b/js/painter.js index 59039adb7..15edd5be0 100644 --- a/js/painter.js +++ b/js/painter.js @@ -59,12 +59,7 @@ class BBPainter { }) } else { Painter.current.texture = texture - var c = Painter.current.canvas = document.createElement('canvas') - var ctx = c.getContext('2d'); - c.width = texture.res; - c.height = texture.img.naturalHeight; - ctx.drawImage(texture.img, 0, 0) - + var c = Painter.current.canvas = Painter.getCanvas(texture) cb(c) texture.updateSource(c.toDataURL()) @@ -76,7 +71,7 @@ class BBPainter { } startBrushCanvas(data, event) { Painter.current.x = Painter.current.y = 0 - var texture = getTextureById(data.cube.faces[data.face].texture) + var texture = data.cube.faces[data.face].getTexture() if (!texture) { Blockbench.showQuickMessage('message.untextured') } @@ -93,7 +88,7 @@ class BBPainter { moveBrushCanvas(force) { var data = Canvas.raycast() if (data) { - var texture = getTextureById(data.cube.faces[data.face].texture) + var texture = data.cube.faces[data.face].getTexture() if (texture) { var x, y, new_face; var end_x = x = Math.floor( data.intersects[0].uv.x * texture.img.naturalWidth ) @@ -148,17 +143,25 @@ class BBPainter { Painter.colorPicker(texture, x, y) } } + getCanvas(texture) { + var c = document.createElement('canvas') + var ctx = c.getContext('2d'); + c.width = texture.res; + c.height = texture.img.naturalHeight; + ctx.drawImage(texture.img, 0, 0) + return c; + } colorPicker(texture, x, y) { - function getPxColor(image) { - var c = image.getPixelColor(x,y) - c = tinycolor(Jimp.intToRGBA(c)) - BarItems.brush_color.set(c) - } - if (texture.mode == 'bitmap') { - Jimp.read(Buffer.from(texture.source.replace('data:image/png;base64,', ''), 'base64')).then(getPxColor) - } else { - Jimp.read(texture.source).then(getPxColor) - } + var ctx = Painter.getCanvas(texture).getContext('2d') + Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => { + var t = tinycolor({ + r: px[0], + g: px[1], + b: px[2], + a: px[3]/256 + }) + BarItems.brush_color.set(t) + }) } useBrush(texture, x, y, uvTag, no_update) { if ((Painter.currentPixel[0] !== x || Painter.currentPixel[1] !== y)) { @@ -185,39 +188,90 @@ class BBPainter { uvTag[3] / 16 * texture.img.naturalHeight ] } else { - var rect = Painter.editing_area = [0, 0, texture.red, texture.red] + var rect = Painter.editing_area = [0, 0, texture.img.naturalWidth, texture.img.naturalHeight] } for (var t = 0; t < 2; t++) { if (rect[t] > rect[t+2]) { [rect[t], rect[t+2]] = [rect[t+2], rect[t]] } + rect[t] = Math.floor(rect[t]) + rect[t+2] = Math.ceil(rect[t+2]) } - ctx.rect(rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]) + var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]] + ctx.rect(rect[0], rect[1], w, h) if (tool === 'fill_tool') { ctx.fillStyle = BarItems.brush_color.get().toRgbString() - ctx.fill() - } else { - ctx.clip() - /*ctx.beginPath(); - ctx.moveTo((Painter.current.x||x)+.5, (Painter.current.y||y)+.5) - ctx.lineTo(x+.5, y+.5) - if (softness) { - ctx.filter = `blur(${ softness*size/2 }px)`; - } else { - ctx.imageSmoothingEnabled = false - } - ctx.lineWidth = size - ctx.lineCap = 'round' - if (brush_mode === 'eraser') { - ctx.globalCompositeOperation = 'destination-out' - ctx.strokeStyle = 'rgba(0,0,0,0)'; + var fill_mode = BarItems.fill_mode.get() + var cube = selected[0] + + if (cube && fill_mode === 'cube') { + for (var face in cube.faces) { + var tag = cube.faces[face] + if (tag.texture === Painter.current.texture.uuid) { + var rect = getRectangle( + Math.floor(tag.uv[0] / 16 * texture.img.naturalWidth), + Math.floor(tag.uv[1] / 16 * texture.img.naturalHeight), + Math.ceil(tag.uv[2] / 16 * texture.img.naturalWidth), + Math.ceil(tag.uv[3] / 16 * texture.img.naturalHeight) + ) + ctx.rect(rect.ax, rect.ay, rect.x, rect.y) + ctx.fill() + } + } + + } else if (fill_mode === 'face') { + ctx.fill() } else { - ctx.strokeStyle = color + + var pxcol = []; + var map = {} + Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => { + px.forEach((val, i) => { + pxcol[i] = val + }) + }) + Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => { + if (pxcol.equals(px)) { + if (!map[x]) map[x] = {} + map[x][y] = true + } + }) + function checkPx(x, y) { + if (map[x] && map[x][y]) { + map[x][y] = false; + + checkPx(x+1, y) + checkPx(x-1, y) + checkPx(x, y+1) + checkPx(x, y-1) + } + } + checkPx(x, y) + Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => { + if (map[x] && map[x][y] === false) { + var pxcolor = { + r: px[0], + g: px[1], + b: px[2], + a: px[3]/255 + } + var result_color = Painter.combineColors(pxcolor, color, 1); + px[0] = result_color.r + px[1] = result_color.g + px[2] = result_color.b + px[3] = result_color.a*255 + } + }) + } - ctx.stroke()*/ + + + } else { + ctx.clip() + if (tool === 'brush_tool') { Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity) { var result_color = Painter.combineColors(pxcolor, color, opacity*b_opacity*(noise?Math.random():1)); @@ -417,6 +471,7 @@ class BBPainter { lines.push({label: 'dialog.create_texture.folder', node: ''}) if (elements.length > 0) { lines.push({label: 'dialog.create_texture.template', node: ''}) + lines.push({label: 'dialog.create_texture.compress', node: ''}) } lines.push({widget: Painter.background_color}) lines.push({label: 'dialog.create_texture.resolution', node: ''}) @@ -433,7 +488,11 @@ class BBPainter { } }) dialog.show() + $('#bitmap_compressTemplate').parent().hide() + $('.dialog#add_bitmap input#bitmap_doTemplate').click(function() { + var checked = $('.dialog#add_bitmap input#bitmap_doTemplate').is(':checked') + $('#bitmap_compressTemplate').parent()[ checked ? 'show' : 'hide' ]() if (Painter.background_color.get().toHex8() === 'ffffffff') { Painter.background_color.set('#00000000') } @@ -457,7 +516,8 @@ class BBPainter { name: $('.dialog#add_bitmap input#bitmap_name').val(), folder: $('.dialog#add_bitmap input#bitmap_folder').val(), particle: 'auto', - entity_template: $('.dialog#add_bitmap input#bitmap_doTemplate').is(':checked') + entity_template: $('.dialog#add_bitmap input#bitmap_doTemplate').is(':checked'), + compress: $('.dialog#add_bitmap input#bitmap_compressTemplate').is(':checked') }) } addBitmap(options, after) { @@ -493,16 +553,19 @@ class BBPainter { if (typeof after === 'function') { after(texture) } + if (options.entity_template) { + Undo.finishEdit('create template', {textures: [texture], bitmap: true, cubes: Blockbench.entity_mode ? elements : selected, uv_only: true}) + } else { + Undo.finishEdit('create blank texture', {textures: [texture], bitmap: true}) + } return texture.add(false); } if (options.entity_template === true) { - Undo.initEdit({textures: [], cubes: Blockbench.entity_mode ? elements : selected, uv_only: true}) - Painter.generateTemplate(options.res, options.color, makeTexture, options.texture) - Undo.finishEdit({textures: [texture], cubes: Blockbench.entity_mode ? elements : selected, uv_only: true}) + Undo.initEdit({textures: Blockbench.entity_mode ? textures : [], cubes: Blockbench.entity_mode ? elements : selected, uv_only: true}) + Painter.generateTemplate(options, makeTexture) } else { Undo.initEdit({textures: []}) Painter.generateBlank(options.res, options.res, options.color, makeTexture) - Undo.finishEdit({textures: [texture]}) } } generateBlank(height, width, color, cb) { @@ -515,15 +578,18 @@ class BBPainter { ctx.fillRect(0, 0, width, height) cb(canvas.toDataURL()) - } - generateTemplate(res, background_color, cb, texture) { + generateTemplate(options, cb) { + var res = options.res + var background_color = options.color + var texture = options.texture function cubeTempl(obj) { var min = Blockbench.entity_mode ? 0 : 1 this.x = obj.size(0, true) || min this.y = obj.size(1, true) || min this.z = obj.size(2, true) || min this.obj = obj + this.template_size = (obj.size(2, true) + obj.size(1, true)) + (obj.size(2, true) + obj.size(0, true))*2 this.height = this.z + this.y this.width = 2* (this.x + this.z) @@ -532,103 +598,143 @@ class BBPainter { var res_multiple = res / 16 var templates = [] - var max_x_pos = 0 - var line_y_pos = 0; - var valid_cubes = 0; - - var lines = [[]] - var line_length = Math.sqrt(elements.length/2) - var o = 0 - - var cubes = Blockbench.entity_mode ? elements.slice() : selected.slice() + var extend_x = 0; + var extend_y = 0; var avg_size = 0; + var cubes = Blockbench.entity_mode ? elements.slice() : selected.slice() var i = cubes.length-1 while (i >= 0) { let obj = cubes[i] - if (obj.visibility === false) { - cubes.splice(i,1) - } else { - obj.template_size = (obj.size(2, true) + obj.size(1, true)) + (obj.size(2, true) + obj.size(0, true))*2 - avg_size += obj.template_size + if (obj.visibility === true) { + templates.push(new cubeTempl(obj)) + avg_size += templates[templates.length-1].template_size } i--; } - avg_size /= cubes.length - cubes.sort(function(a,b) { - return b.template_size - a.template_size - }) - - i = 0 - var ox = 0 - cubes.forEach(function(obj) { - if (ox >= line_length) { - o = 0 - ox = 0 - i++ - lines[i] = [] - } - lines[i][o] = obj - o++; - ox += obj.template_size/avg_size - }) - - lines.forEach(function(b) { - - //Data - var temps = [] - b.forEach(function(s, si) { - if (s.type === 'cube') { - temps.push(new cubeTempl(s)) - valid_cubes++; - } - }) - //Defaults - //Find the maximum height of the line - var max_height = 0 - temps.forEach(function(t) { - max_height = Math.max(max_height, t.height) - }) - var x_pos = 0 - var y_pos = 0 //Y Position of current area relative to this bone - var filled_x_pos = 0; - //Algorithm - temps.forEach(function(t) { - if (y_pos > 0 && (y_pos + t.height) <= max_height) { - //same column - t.posx = x_pos - t.posy = y_pos + line_y_pos - filled_x_pos = Math.max(filled_x_pos, x_pos+t.width) - y_pos += t.height - } else { - //new column - x_pos = filled_x_pos - y_pos = t.height - t.posx = x_pos - t.posy = line_y_pos - filled_x_pos = Math.max(filled_x_pos, x_pos+t.width) - } - //size of widest bone - max_x_pos = Math.max(max_x_pos, filled_x_pos) - templates.push(t) - }) - line_y_pos += max_height + templates.sort(function(a,b) { + return b.template_size - a.template_size; }) //Cancel if no cubes - if (valid_cubes == 0) { + if (templates.length == 0) { Blockbench.showMessage('No valid cubes', 'center') return; } - function getNextPower(num, min) { - var i = min ? min : 2 - while (i < num && i < 4000) { - i *= 2 + /* + TEMPLATE MENU + condensed + use old texture + */ + if (options.compress) { + + var fill_map = {} + function occupy(x, y) { + if (!fill_map[x]) fill_map[x] = {} + fill_map[x][y] = true } - return i; + function check(x, y) { + return fill_map[x] && fill_map[x][y] + } + function forTemplatePixel(tpl, sx, sy, cb) { + for (var x = 0; x < tpl.width; x++) { + for (var y = 0; y < tpl.height; y++) { + if (y >= tpl.z || (x >= tpl.z && x < (tpl.z + 2*tpl.x))) { + if (cb(sx+x, sy+y)) return; + } + } + } + } + function place(tpl, x, y) { + var works = true; + forTemplatePixel(tpl, x, y, (tx, ty) => { + if (check(tx, ty)) { + works = false; + return true; + } + }) + if (works) { + forTemplatePixel(tpl, x, y, occupy) + tpl.posx = x; + tpl.posy = y; + extend_x = Math.max(extend_x, x + tpl.width); + extend_y = Math.max(extend_y, y + tpl.height); + return true; + } + } + templates.forEach(tpl => { + var vert = extend_x > extend_y; + //Scan for empty spot + for (var line = 0; line < 2e3; line++) { + for (var x = 0; x < line; x++) { + if (place(tpl, x, line)) return; + } + for (var y = 0; y < line; y++) { + if (place(tpl, line, y)) return; + } + } + }) + } else { + //OLD ------------------------------------------- + var lines = [[]] + var line_length = Math.sqrt(elements.length/2) + avg_size /= templates.length + var o = 0 + var i = 0 + var ox = 0 + templates.forEach(function(tpl) { + if (ox >= line_length) { + o = ox = 0 + i++ + lines[i] = [] + } + lines[i][o] = tpl + o++; + ox += tpl.template_size/avg_size + }) + + lines.forEach(function(temps) { + + var x_pos = 0 + var y_pos = 0 //Y Position of current area relative to this bone + var filled_x_pos = 0; + var max_height = 0 + //Find the maximum height of the line + temps.forEach(function(t) { + max_height = Math.max(max_height, t.height) + }) + //Place + temps.forEach(function(t) { + if (y_pos > 0 && (y_pos + t.height) <= max_height) { + //same column + t.posx = x_pos + t.posy = y_pos + extend_y + filled_x_pos = Math.max(filled_x_pos, x_pos+t.width) + y_pos += t.height + } else { + //new column + x_pos = filled_x_pos + y_pos = t.height + t.posx = x_pos + t.posy = extend_y + filled_x_pos = Math.max(filled_x_pos, x_pos+t.width) + } + //size of widest bone + extend_x = Math.max(extend_x, filled_x_pos) + }) + extend_y += max_height + }) } + //Size - var max_size = Math.max(max_x_pos, line_y_pos) - max_size = Math.ceil(max_size/16)*16//getNextPower(max_size, 16) + //function getNextPower(num, min) { + // var i = min ? min : 2 + // while (i < num && i < 4000) { + // i *= 2 + // } + // return i; + //} + var max_size = Math.max(extend_x, extend_y) + max_size = Math.ceil(max_size/16)*16 if (background_color.getAlpha() != 0) { background_color = background_color.toInteger() @@ -664,22 +770,52 @@ class BBPainter { function drawTexture(face, coords) { if (!Blockbench.entity_mode) { if (face.texture === undefined || face.texture === null) return false; - texture = getTextureById(face.texture) + texture = face.getTexture() } if (!texture || !texture.img) return false; - var uv = face.uv; + + ctx.save() + var uv = face.uv.slice(); + + if (face.direction === 'up') { + uv = [uv[2], uv[3], uv[0], uv[1]] + } else if (face.direction === 'down') { + uv = [uv[2], uv[1], uv[0], uv[3]] + } + var src = getRectangle(uv[0], uv[1], uv[2], uv[3]) + var flip = [ + uv[0] > uv[2] ? -1 : 1, + uv[1] > uv[3] ? -1 : 1 + ] + if (flip[0] + flip[1] < 1) { + ctx.scale(flip[0], flip[1]) + } + if (face.rotation) { + ctx.rotate(Math.degToRad(face.rotation)) + let rot = face.rotation + + if (rot <= 180) flip[1] *= -1; + if (rot >= 180) flip[0] *= -1; + + while (rot > 0) { + [coords.x, coords.y] = [coords.y, coords.x]; + [coords.w, coords.h] = [coords.h, coords.w]; + rot -= 90; + } + } ctx.drawImage( texture.img, src.ax/16 * texture.img.naturalWidth, src.ay/16 * texture.img.naturalHeight, src.x /16 * texture.img.naturalWidth, src.y /16 * texture.img.naturalHeight, - coords.x*res_multiple, - coords.y*res_multiple, - coords.w*res_multiple, - coords.h*res_multiple + coords.x*res_multiple*flip[0], + coords.y*res_multiple*flip[1], + coords.w*res_multiple*flip[0], + coords.h*res_multiple*flip[1] ) + ctx.restore() return true; } @@ -693,8 +829,9 @@ class BBPainter { } //Drawing - templates.forEach(function(t) { + let obj = t.obj + for (var face in face_data) { let d = face_data[face] @@ -704,7 +841,6 @@ class BBPainter { drawTemplateRectangle(d.c1, d.c2, d.place(t)) } } - let obj = t.obj obj.uv_offset[0] = t.posx obj.uv_offset[1] = t.posy @@ -719,15 +855,16 @@ class BBPainter { {face: 'up', fIndex: 4, from: [size[2]+size[0], size[2]], size: [-size[0], -size[2]]}, {face: 'down', fIndex: 6, from: [size[2]+size[0]*2, 0], size: [-size[0], size[2]]} ] - face_list.forEach(function(f) { - obj.faces[f.face].uv[0] = (f.from[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16, - obj.faces[f.face].uv[1] = (f.from[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16, - obj.faces[f.face].uv[2] = (f.from[0] + f.size[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16, - obj.faces[f.face].uv[3] = (f.from[1] + f.size[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16 - + obj.faces[f.face].uv[0] = (f.from[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16; + obj.faces[f.face].uv[1] = (f.from[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16; + obj.faces[f.face].uv[2] = (f.from[0] + f.size[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16; + obj.faces[f.face].uv[3] = (f.from[1] + f.size[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16; + obj.faces[f.face].rotation = 0; }) + } else { + obj.mirror_uv = false } }) var dataUrl = canvas.toDataURL() @@ -854,6 +991,15 @@ BARS.defineActions(function() { noise: true } }) + new BarSelect({ + id: 'fill_mode', + condition: () => Toolbox && Toolbox.selected.id === 'fill_tool', + options: { + face: true, + color: true, + cube: true + } + }) new NumSlider({ id: 'slider_brush_size', diff --git a/js/plugin_loader.js b/js/plugin_loader.js index 99c28a870..d79b02023 100644 --- a/js/plugin_loader.js +++ b/js/plugin_loader.js @@ -1,14 +1,10 @@ -/* -Plugin Loader for Blockbench -By JannisX11 -*/ var onUninstall, onInstall; const Plugins = { apipath: 'https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins.json', Vue: [], //Vue Object installed: [], //Simple List of Names json: undefined, //Json from website - data: [], //Vue Object Data + all: [], //Vue Object Data loadingStep: false, updateSearch: function() { Plugins.Vue._data.showAll = !Plugins.Vue._data.showAll @@ -16,16 +12,182 @@ const Plugins = { }, devReload: function() { var reloads = 0; - Plugins.data.forEach(function(pl) { - if (pl.fromFile) { - pl.reload() + for (var i = Plugins.all.length-1; i >= 0; i--) { + if (Plugins.all[i].fromFile) { + Plugins.all[i].reload() reloads++; } - }) + } + Blockbench.showQuickMessage(tl('message.plugin_reload', [reloads])) console.log('Reloaded '+reloads+ ' plugin'+pluralS(reloads)) } } +class Plugin { + constructor(id, data) { + this.id = id; + this.installed = false; + this.expanded = false; + this.title = ''; + this.author = ''; + this.description = ''; + this.about = ''; + this.icon = ''; + this.variant = ''; + this.min_version = ''; + if (data) { + this.extend(data) + } + } + extend(data) { + Merge.boolean(this, data, 'installed') + Merge.boolean(this, data, 'expanded') + Merge.string(this, data, 'title') + Merge.string(this, data, 'author') + Merge.string(this, data, 'description') + Merge.string(this, data, 'about') + Merge.string(this, data, 'icon') + Merge.string(this, data, 'variant') + Merge.string(this, data, 'min_version') + return this; + } + install(first, cb) { + var scope = this; + $.getScript(Plugins.path + scope.id + '.js', function() { + scope.bindGlobalData(first) + if (cb) cb() + }).fail(function() { + if (isApp) { + console.log('Could not find file of plugin "'+scope.id+'". Uninstalling it instead.') + scope.uninstall() + } + }) + Plugins.installed.safePush(scope.id) + scope.installed = true; + return scope; + } + bindGlobalData(first) { + var scope = this; + if (onUninstall) { + scope.onUninstall = onUninstall + } + if (first && onInstall) { + onInstall() + } + window.onInstall = window.onUninstall = window.plugin_data = undefined + return this; + } + download(first) { + var scope = this; + if (!isApp) { + scope.install(first) + return this; + } + var file = originalFs.createWriteStream(Plugins.path+this.id+'.js') + var request = https.get('https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins/'+this.id+'.js', function(response) { + response.pipe(file); + response.on('end', function() { + setTimeout(function() { + scope.install(first) + }, 50) + }) + }); + return this; + } + loadFromFile(file, hideWarning) { + var scope = this; + var path = file.path + localStorage.setItem('plugin_dev_path', file.path) + onInstall = undefined + + if (!hideWarning) { + if (isApp) { + if (!confirm(tl('message.load_plugin_app'))) return; + } else { + if (!confirm(tl('message.load_plugin_web'))) return; + } + } + $.getScript(file.path, function() { + scope.id = (plugin_data && plugin_data.id)||pathToName(file.path) + scope.installed = true + scope.fromFile = true + scope.path = file.path + scope.extend(plugin_data) + scope.bindGlobalData(true) + Plugins.installed.safePush(scope.path) + saveInstalledPlugins() + Plugins.all.sort(function(a,b) { + return sort_collator.compare(a.title, b.title) + }); + }) + Plugins.all.safePush(this) + return this; + } + uninstall() { + var scope = this; + if (isApp && this.fromFile) { + if (this.onUninstall) { + this.onUninstall() + } + Plugins.all.remove(this) + Plugins.installed.remove(this.path) + } else { + if (isApp) { + var filepath = Plugins.path + scope.id + '.js' + if (fs.existsSync(filepath)) { + fs.unlink(filepath, (err) => { + if (err) { + console.log(err); + } + }); + } + } + Plugins.installed.remove(scope.id) + scope.installed = false + if (scope.onUninstall) { + scope.onUninstall() + } + } + saveInstalledPlugins() + return this; + } + reload() { + if (!isApp) return this; + this.uninstall() + this.loadFromFile({path: this.path}, true) + return this; + } + isInstallable() { + var scope = this; + var result = + scope.variant === 'both' || + ( + isApp === (scope.variant === 'desktop') && + isApp !== (scope.variant === 'web') + ); + if (result && scope.min_version) { + result = compareVersions(scope.min_version, appVersion) ? 'outdated' : true + } else if (result === false) { + result = (scope.variant === 'web') ? 'web_only' : 'app_only' + } + return (result === true) ? true : tl('dialog.plugins.'+result); + } + toggleInfo(force) { + var scope = this; + Plugins.all.forEach(function(p) { + if (p !== scope && p.expanded) p.expanded = false; + }) + if (force !== undefined) { + this.expanded = force === true + } else { + this.expanded = this.expanded !== true + } + } + get expandicon() { + return this.expanded ? 'expand_less' : 'expand_more' + } +} + if (isApp) { Plugins.path = app.getPath('userData')+osfs+'plugins'+osfs fs.readdir(Plugins.path, function(err) { @@ -33,102 +195,78 @@ if (isApp) { fs.mkdir(Plugins.path, function(a) {}) } }) +} else { + Plugins.path = 'https://cdn.jsdelivr.net/gh/JannisX11/blockbench-plugins/plugins/'; } $.getJSON(Plugins.apipath, function(data) { Plugins.json = data - if (Plugins.loadingStep === true) { - loadInstalledPlugins() - } else { - Plugins.loadingStep = true - } + loadInstalledPlugins() }).fail(function() { console.log('Could not connect to plugin server') $('#plugin_available_empty').text('Could not connect to plugin server') - if (Plugins.loadingStep === true) { - loadInstalledPlugins() - } else { - Plugins.loadingStep = true - } + loadInstalledPlugins() }) - $(document).ready(function() { - if (Plugins.loadingStep === true) { - loadInstalledPlugins() - } else { - Plugins.loadingStep = true - } + loadInstalledPlugins() }) function loadInstalledPlugins() { + if (!Plugins.loadingStep) { + Plugins.loadingStep = true + return; + } var storage_data = localStorage.getItem('installed_plugins') if (storage_data !== null) { Plugins.installed = JSON.parse(storage_data) } if (Plugins.json !== undefined) { + //From Store for (var id in Plugins.json) { - var plugin = Plugins.json[id] - var obj = { - id: id, - title: plugin.title, - author: plugin.author, - description: plugin.description, - about: plugin.about, - icon: plugin.icon, - variant: plugin.variant, - min_version: plugin.min_version, - installed: Plugins.installed.includes(id), - expanded: false - } - if (obj.installed) { - if (isApp) { - downloadPlugin(id) - } else { - loadPlugin(id) - } + var plugin = new Plugin(id, Plugins.json[id]) + if (Plugins.installed.includes(id)) { + plugin.download() } - Plugins.data.push(obj) - Plugins.data.sort(function(a,b) { - return sort_collator.compare(a.title, b.title) - }); + Plugins.all.push(plugin) } - } else if (Plugins.installed.length > 0) { - //Only show downloaded plugins in the plugin window + Plugins.all.sort(function(a,b) { + return sort_collator.compare(a.title, b.title) + }); + } else if (Plugins.installed.length > 0 && isApp) { Plugins.installed.forEach(function(id) { - loadPlugin(id, function() { - //Plugin Data Comes from the plugin file - if (plugin_data === undefined) return; - var obj = { - id: id, - title: plugin_data.title, - author: plugin_data.author, - description: plugin_data.description, - about: plugin_data.about, - icon: plugin_data.icon, - variant: plugin_data.variant, - min_version: plugin_data.min_version, - installed: true, - expanded: false - } - Plugins.data.push(obj) - Plugins.data.sort(function(a,b) { - return sort_collator.compare(a.title, b.title) - }); - }) + + if (id.substr(-3) !== '.js') { + //downloaded public plugin + var plugin = new Plugin(id).install(false, () => { + if (typeof plugin_data === 'object') { + plugin.extend(plugin_data) + Plugins.all.push(plugin) + Plugins.all.sort(function(a,b) { + return sort_collator.compare(a.title, b.title) + }); + } + }) + } }) } if (Plugins.installed.length > 0) { + Plugins.installed.forEach(function(id) { + + if (id.substr(-3) === '.js') { + //Dev Plugins + var plugin = new Plugin().loadFromFile({path: id}, true) + } + }) console.log('Loaded '+Plugins.installed.length+' plugin'+pluralS(Plugins.installed.length)) } - Plugins.Vue = new Vue({ el: '#plugin_list', data: { showAll: false, - items: Plugins.data + items: Plugins.all }, computed: { - installedPlugins() { + plugin_search() { var name = $('#plugin_search_bar').val().toUpperCase() return this.items.filter(item => { if (this.showAll !== item.installed) { @@ -145,196 +283,14 @@ function loadInstalledPlugins() { return false; }) } - }, - methods: { - install: function(plugin) { - if (isApp) { - downloadPlugin(plugin.id) - } else { - loadPlugin(plugin.id) - } - }, - uninstall: function(plugin) { - uninstallPlugin(plugin.id) - }, - update: function(plugin) { - if (isApp) { - downloadPlugin(plugin.id) - } - }, - checkIfInstallable: function(plugin) { - var result = - plugin.variant === 'both' || - ( - isApp === (plugin.variant === 'desktop') && - isApp !== (plugin.variant === 'web') - ); - if (result && plugin.min_version) { - result = compareVersions(plugin.min_version, appVersion) ? 'outdated' : true - } else if (result === false) { - result = (plugin.variant === 'web') ? 'web_only' : 'app_only' - } - return (result === true) ? true : tl('dialog.plugins.'+result); - }, - toggleInfo: function(plugin, force) { - Plugins.data.forEach(function(p) { - if (p !== plugin && p.expanded) p.expanded = false; - }) - plugin.expanded = plugin.expanded !== true - if (force !== undefined) { - plugin.expanded = force === true - } - if (plugin.expanded) { - plugin.expandicon = 'expand_less' - } else { - plugin.expandicon = 'expand_more' - } - } } }) } function saveInstalledPlugins() { localStorage.setItem('installed_plugins', JSON.stringify(Plugins.installed)) - hideDialog() -} -function loadPlugin(id, cb, install) { - if (isApp === true) { - $.getScript(Plugins.path + id + '.js', function(a) { - if (onUninstall) { - Plugins.data.findInArray('id', id).uninstall = onUninstall - onUninstall = undefined - } - if (install && onInstall) { - onInstall() - } - onInstall = undefined - if (cb !== undefined) cb() - }).fail(function() { - console.log('Could not find file of plugin "'+id+'". Uninstalling it instead.') - uninstallPlugin(id) - saveInstalledPlugins() - }) - } else { - $.getScript('https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins/'+id+'.js', function() { - if (onUninstall) { - Plugins.data.findInArray('id', id).uninstall = onUninstall - onUninstall = undefined - } - if (install && onInstall) { - onInstall() - } - onInstall = undefined - if (cb) cb() - }) - } - if (Plugins.installed.includes(id) === false) { - Plugins.installed.push(id) - } - Plugins.data.findInArray('id', id).installed = true } -function loadPluginFromFile(file, hideWarning) { - var hideWarning; - var content = file.content - var path = file.path - localStorage.setItem('plugin_dev_path', path) - onInstall = undefined - - if (!hideWarning) { - if (isApp) { - if (!confirm(tl('message.load_plugin_app'))) return; - } else { - if (!confirm(tl('message.load_plugin_web'))) return; - } - } - try { - eval(content) - } catch (err) { - Blockbench.showQuickMessage('message.invalid_plugin') - console.error(err) - return; - } - var obj = { - author: 'unknown', - icon: 'extension', - installed: true, - id: 'test', - title: 'Plugin', - variant: 'both', - description: '', - about: '', - fromFile: true, - filePath: path, - expanded: false, - uninstall: function() { - var index = Plugins.data.indexOf(this) - if (index >= 0) Plugins.data.splice(index, 1) - if (this.uninstallMethod) { - this.uninstallMethod() - } - }, - reload: function() { - if (isApp) { - obj.uninstall() - fs.readFile(path, 'utf-8', function (err, data) { - if (err) { - console.log(err) - return; - } - loadPluginFromFile({ - content: data, - path: path - }, true) - }) - } - }, - uninstallMethod: false - } - $.extend(true, obj, plugin_data) - obj.uninstallMethod = onUninstall - onUninstall = undefined - if (onInstall) onInstall() - onInstall = undefined - Plugins.data.push(obj) - Plugins.data.sort(function(a,b) { - return sort_collator.compare(a.title, b.title) - }); -} -function downloadPlugin(id, is_install) { - //$('.uc_btn').attr('disabled', true) - - var file = originalFs.createWriteStream(Plugins.path+id+'.js') - var request = https.get('https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins/'+id+'.js', function(response) { - response.pipe(file); - response.on('end', function() { - setTimeout(function() { - loadPlugin(id, undefined, is_install) - }, 100) - }) - }); -} -function uninstallPlugin(id) { - if (isApp) { - var filepath = Plugins.path + id + '.js' - if (fs.existsSync(filepath)) { - fs.unlink(filepath, (err) => { - if (err) { - console.log(err); - return; - } - }); - } else { - //File does not exist - } - } - var index = Plugins.installed.indexOf(id) - if (index > -1) { - Plugins.installed.splice(index, 1) - } - var data_obj = Plugins.data.findInArray('id', id) - data_obj.installed = false - if (data_obj.uninstall) { - data_obj.uninstall() - } +function loadPluginFromFile(file) { + var plugin = new Plugin().loadFromFile(file, false) } function switchPluginTabs(installed) { $('#plugins .tab').removeClass('open') @@ -357,13 +313,22 @@ BARS.defineActions(function() { $('#plugin_list').css('max-height', limitNumber($(window).height()-300, 80, 600)+'px') } }) + new Action({ + id: 'reload_plugins', + icon: 'sync', + category: 'blockbench', + keybind: new Keybind({ctrl: true, key: 74}), + click: function () { + Plugins.devReload() + } + }) new Action({ id: 'load_plugin', icon: 'fa-file-code-o', category: 'blockbench', click: function () { Blockbench.import({ - extensions: ['bbplugin', 'js'], + extensions: ['js'], type: 'Blockbench Plugin', startpath: localStorage.getItem('plugin_dev_path') }, function(files) { diff --git a/js/preview.js b/js/preview.js index d13f560e4..f9747f187 100644 --- a/js/preview.js +++ b/js/preview.js @@ -35,7 +35,6 @@ class Preview { this.camOrtho = new THREE.OrthographicCamera(-600, 600, -400, 400, 1, 100) this.camOrtho.backgroundHandle = [{n: false, a: 'x'}, {n: false, a: 'y'}] this.camOrtho.axis = null - this.camPers.position.set(-20, 20, -20) this.camPers.preview = this.camOrtho.preview = this; //Controls @@ -46,6 +45,8 @@ class Preview { this.controls.enableKeys = false; this.controls.zoomSpeed = 1.5 + this.resetCamera(true) + //Keybinds this.controls.mouseButtons.ZOOM = undefined; @@ -255,10 +256,13 @@ class Preview { this.controls.updateSceneScale(); return this; } - resetCamera() { + resetCamera(init) { + var dis = 24 this.controls.target.set(0, -3, 0); - this.camPers.position.set(-20, 20, -20) - this.setNormalCamera() + this.camPers.position.set(-dis, dis*0.8, -dis) + if (!init) { + this.setNormalCamera() + } return this; } getFacingDirection() { @@ -303,7 +307,7 @@ class Preview { this.static_rclick = false if (Toolbox.selected.selectCubes && Modes.selected.selectCubes && data.type === 'cube') { if (Toolbox.selected.selectFace) { - main_uv.setFace(data.face) + main_uv.setFace(data.face, false) } Blockbench.dispatchEvent( 'canvas_select', data ) if (Animator.open || (Toolbox.selected.id === 'rotate_tool' && Blockbench.entity_mode)) { @@ -441,10 +445,10 @@ class Preview { ) selected.length = 0; elements.forEach(function(cube) { - + if ((event.shiftKey || event.ctrlKey) && scope.selection.old_selected.indexOf(cube) >= 0) { var isSelected = true - } else { + } else if (cube.visibility) { var mesh = cube.mesh var from = new THREE.Vector3().copy(mesh.geometry.vertices[6]).applyMatrix4(mesh.matrixWorld) var to = new THREE.Vector3().copy(mesh.geometry.vertices[0]).applyMatrix4(mesh.matrixWorld) @@ -711,6 +715,7 @@ class Preview { } return [ {icon: getBtn(0, true), name: 'menu.preview.perspective.normal', click: function(preview) {preview.setNormalCamera()}}, + 'camera_reset', {icon: getBtn(0), name: 'direction.top', color: 'y', click: function(preview) {preview.setOrthographicCamera(0)}}, {icon: getBtn(1), name: 'direction.bottom', color: 'y', click: function(preview) {preview.setOrthographicCamera(1)}}, {icon: getBtn(2), name: 'direction.south', color: 'z', click: function(preview) {preview.setOrthographicCamera(2)}}, @@ -847,29 +852,41 @@ function initCanvas() { Sun = new THREE.AmbientLight( 0xffffff ); Sun.name = 'sun' scene.add(Sun); + Sun.intensity = 0.44 lights = new THREE.Object3D() lights.name = 'lights' - var light_top = new THREE.DirectionalLight( 0x777777 ); + var light_top = new THREE.DirectionalLight( /*0x777777*/ ); + light_top.name = 'light_top' light_top.position.set(8, 100, 8) lights.add(light_top); + + light_top.intensity = 0.66 - var light_west = new THREE.DirectionalLight( 0x222222 ); + var light_north = new THREE.DirectionalLight( /*0x444444*/ ); + light_north.name = 'light_north' + light_north.position.set(8, 8, -100) + lights.add(light_north); + + var light_south = new THREE.DirectionalLight( /*0x444444*/ ); + light_south.name = 'light_south' + light_south.position.set(8, 8, 100) + lights.add(light_south); + + light_north.intensity = light_south.intensity = 0.44 + + var light_west = new THREE.DirectionalLight( /*0x222222*/ ); + light_west.name = 'light_west' light_west.position.set(-100, 8, 8) lights.add(light_west); - var light_east = new THREE.DirectionalLight( 0x222222 ); + var light_east = new THREE.DirectionalLight( /*0x222222*/ ); + light_east.name = 'light_east' light_east.position.set(100, 8, 8) lights.add(light_east); - var light_north = new THREE.DirectionalLight( 0x444444 ); - light_north.position.set(8, 8, -100) - lights.add(light_north); - - var light_south = new THREE.DirectionalLight( 0x444444 ); - light_south.position.set(8, 8, 100) - lights.add(light_south); + light_west.intensity = light_east.intensity = 0.22 setShading() @@ -951,14 +968,11 @@ function animate() { function setShading() { scene.remove(lights) display_scene.remove(lights) - Sun.intensity = 1 + Sun.intensity = settings.brightness.value/100; if (settings.shading.value === true) { - Sun.intensity = 0.65 - if (display_mode) { - display_scene.add(lights) - } else { - scene.add(lights) - } + (display_mode ? display_scene : scene).add(lights) + } else { + Sun.intensity *= (1/0.6) } } //Helpers @@ -1089,13 +1103,6 @@ function centerTransformer(offset) { Transformer.position.z += 8; Transformer.rotation.set(0, 0, 0) Transformer.update() - - //if (!rotate_tool) { - // var quat = new THREE.Quaternion() - // g_mesh.getWorldQuaternion(quat) - // Transformer.rotation.setFromQuaternion(quat, 'ZYX') - //} else { - //} return; } @@ -1104,57 +1111,18 @@ function centerTransformer(offset) { Canvas.updateAllBones() } if (!rotate_tool) { - var first_obj - var center = [0, 0, 0] - var i = 0; - selected.forEach(function(obj) { - var m = obj.mesh - if (obj.visibility && m) { - var pos = new THREE.Vector3( - obj.from[0] + obj.size(0)/2, - obj.from[1] + obj.size(1)/2, - obj.from[2] + obj.size(2)/2 - ) - if (!Blockbench.entity_mode) { - - pos.x -= obj.origin[0] - pos.y -= obj.origin[1] - pos.z -= obj.origin[2] - var r = m.getWorldQuaternion(new THREE.Quaternion()) - pos.applyQuaternion(r) - pos.x += obj.origin[0] - pos.y += obj.origin[1] - pos.z += obj.origin[2] - } else { - var r = m.getWorldQuaternion(new THREE.Quaternion()) - pos.applyQuaternion(r) - pos.add(m.getWorldPosition(new THREE.Vector3())) - pos.x += 8 - pos.y += 8 - pos.z += 8 - } - - center[0] += pos.x - center[1] += pos.y - center[2] += pos.z - - if (!first_obj) { - first_obj = obj - } + var first_obj; + var center = getSelectionCenter() + for (var i = 0; i < selected.length && !first_obj; i++) { + if (selected[i].visibility) { + first_obj = selected[i]; } - }) - if (!first_obj) { - return; - } - i = 0; - while (i < 3) { - center[i] = center[i] / selected.length - i++; } } else { var first_obj = selected[0] var center = first_obj.origin } + if (!first_obj || !first_obj.visibility) return; var vec = new THREE.Vector3(center[0], center[1], center[2]) //Position + Rotation @@ -1357,7 +1325,7 @@ class CanvasController { if (texture) { used = false; for (var face in obj.faces) { - if (obj.faces[face] && obj.faces[face].texture === '#'+texture.id) { + if (obj.faces[face].texture === texture.uuid) { used = true; } } @@ -1420,9 +1388,7 @@ class CanvasController { } } arr.forEach(function(obj) { - if (obj.visibility == true) { - Canvas.adaptObjectPosition(obj) - } + Canvas.adaptObjectPosition(obj) }) if (leave_selection !== true) { updateSelection() @@ -1608,22 +1574,22 @@ class CanvasController { } iterate(el, elmesh) } - adaptObjectFaces(obj, mesh) { - if (!mesh) mesh = obj.mesh + adaptObjectFaces(cube, mesh) { + if (!mesh) mesh = cube.mesh if (!mesh) return; if (!Prop.wireframe) { var materials = [] this.face_order.forEach(function(face) { - if (obj.faces[face].texture === null) { + if (cube.faces[face].texture === null) { materials.push(Canvas.transparentMaterial) } else { - var tex = getTextureById(obj.faces[face].texture) - if (typeof tex === 'object') { + var tex = cube.faces[face].getTexture() + if (tex && tex.uuid) { materials.push(Canvas.materials[tex.uuid]) } else { - materials.push(emptyMaterials[obj.color]) + materials.push(emptyMaterials[cube.color]) } } }) @@ -1717,7 +1683,7 @@ class CanvasController { stretch = 1 frame = 0 if (obj[face].texture && obj[face].texture !== null) { - var tex = getTextureById(obj[face].texture) + var tex = obj[face].getTexture() if (typeof tex === 'object' && tex.constructor.name === 'Texture' && tex.frameCount) { stretch = tex.frameCount if (animation === true && tex.currentFrame) { @@ -1830,6 +1796,9 @@ BARS.defineActions(function() { click: function () { Prop.wireframe = !Prop.wireframe Canvas.updateAll() + if (Modes.id === 'animate') { + Animator.preview() + } } }) @@ -1888,6 +1857,7 @@ BARS.defineActions(function() { id: 'toggle_quad_view', icon: 'widgets', category: 'view', + condition: () => (Modes.id === 'edit' || Modes.id === 'paint'), keybind: new Keybind({key: 9}), click: function () { main_preview.toggleFullscreen() @@ -1895,8 +1865,8 @@ BARS.defineActions(function() { }) new Action({ id: 'camera_reset', - name: 'direction.top', - description: 'direction.top', + name: 'menu.preview.perspective.reset', + description: 'menu.preview.perspective.reset', icon: 'videocam', category: 'view', keybind: new Keybind({key: 96}), diff --git a/js/settings.js b/js/settings.js index 57334187a..aa894ee05 100644 --- a/js/settings.js +++ b/js/settings.js @@ -16,6 +16,7 @@ function settingSetup() { //focal_length: {category: 'preview', value: 70, type: 'number'}, display_skin: {category: 'preview', value: false, type: 'click', condition: isApp, icon: 'icon-player', click: function() { changeDisplaySkin() }}, seethrough_outline: {category: 'preview', value: false}, + brightness: {category: 'preview', value: 50, type: 'number'}, shading: {category: 'preview', value: true}, transparency: {category: 'preview', value: true}, texture_fps: {category: 'preview', value: 2, type: 'number'}, @@ -242,7 +243,7 @@ function saveSettings(force_update) { } } Canvas.outlineMaterial.depthTest = !settings.seethrough_outline.value - if (hasSettingChanged('shading')) { + if (hasSettingChanged('shading') || hasSettingChanged('brightness')) { setShading() } if (hasSettingChanged('texture_fps')) { @@ -265,7 +266,10 @@ function saveProjectSettings() { if (uv_dialog.editors) { uv_dialog.editors.single.setGrid() } - entityMode.setResolution() + if (entityMode.old_res.x !== Project.texture_width || entityMode.old_res.y !== Project.texture_height) { + entityMode.setResolution() + Undo.finishEdit('changed resolution') + } } hideDialog() } @@ -279,3 +283,41 @@ function toggleSetting(setting) { } function toggleWireframe() { } + +onVueSetup(function() { + var structure = {} + for (var key in settings) { + var category = settings[key].category + if (!category) category = 'general' + + if (!structure[category]) { + structure[category] = { + name: tl('settings.category.'+category), + open: category === 'general', + items: {} + } + } + structure[category].items[key] = settings[key] + } + var settingslist = new Vue({ + el: 'ul#settingslist', + data: {structure}, + methods: { + saveSettings: function() { + localStorage.setItem('settings', JSON.stringify(settings)) + }, + toggleCategory: function(category) { + if (!category.open) { + for (var ct in structure) { + structure[ct].open = false + } + } + category.open = !category.open + } + } + }) + var project_vue = new Vue({ + el: '#project_settings', + data: {Project} + }) +}) \ No newline at end of file diff --git a/js/textures.js b/js/textures.js index 947340934..a0abb5b4a 100644 --- a/js/textures.js +++ b/js/textures.js @@ -478,7 +478,7 @@ class Texture { var sides = ['north', 'east', 'south', 'west', 'up', 'down'] elements.forEach(function(s) { sides.forEach(function(side) { - s.faces[side].texture = '#'+scope.id + s.faces[side].texture = scope.uuid }) }) Canvas.updateAllFaces() @@ -537,15 +537,16 @@ class Texture { if (selected.length === 0) return; var scope = this; Undo.initEdit({cubes: selected}) - if (all || Blockbench.entity_mode) { - var sides = ['north', 'east', 'south', 'west', 'up', 'down'] - } else { - var sides = [main_uv.face] - } + selected.forEach(function(obj) { - sides.forEach(function(side) { - obj.faces[side].texture = '#'+scope.id - }) + for (var face in obj.faces) { + if (all || Blockbench.entity_mode || face === main_uv.face) { + var f = obj.faces[face] + if (all !== 'blank' || (f.texture !== null && !f.getTexture())) { + f.texture = scope.uuid + } + } + } }) Canvas.updateSelectedFaces() main_uv.loadData() @@ -713,6 +714,12 @@ class Texture { condition: function() {return !Blockbench.entity_mode && selected.length > 0}, click: function(texture) {texture.apply()} }, + { + icon: 'texture', + name: 'menu.texture.blank', + condition: function() {return !Blockbench.entity_mode && selected.length > 0}, + click: function(texture) {texture.apply('blank')} + }, { icon: 'fa-cube', name: 'menu.texture.cube', @@ -876,7 +883,7 @@ function loadTextureDraggable() { if ($('canvas.preview:hover').length > 0) { var data = Canvas.getCurrentPreview().raycast() if (data.cube && data.face) { - var tex = getTextureById(ui.helper.attr('texid')) + var tex = textures.findInArray('uuid', ui.helper.attr('texid')); if (tex) { data.cube.applyTexture(tex, [data.face]) } @@ -928,7 +935,7 @@ function changeTexturesFolder() { } function getTextureById(id) { - if (id === undefined) return; + if (id === undefined || id === false) return; if (id == null) { return {material: transparentMaterial}; } @@ -937,10 +944,18 @@ function getTextureById(id) { } function getTexturesById(id) { if (id === undefined) return; - id = id.split('#').join(''); + id = id.replace('#', ''); return $.grep(textures, function(e) {return e.id == id}); } +onVueSetup(function() { + texturelist = new Vue({ + el: '#texture_list', + data: {textures} + }) + texturelist._data.elements = textures +}) + BARS.defineActions(function() { new Action({ id: 'import_texture', diff --git a/js/transform.js b/js/transform.js index 7dda61ea0..22a06df0b 100644 --- a/js/transform.js +++ b/js/transform.js @@ -1,71 +1,52 @@ //Actions function origin2geometry() { - Undo.initEdit({cubes: selected}) + if (Blockbench.entity_mode) { + Undo.initEdit({group: selected_group}) + if (!selected_group || selected_group.children.length === 0) return; var position = [0, 0, 0] selected_group.children.forEach(function(obj) { - position[0] += obj.from[0] + obj.size(0)/2 - position[1] += obj.from[1] + obj.size(1)/2 - position[2] += obj.from[2] + obj.size(2)/2 + if (obj.type === 'cube') { + position[0] += obj.from[0] + obj.size(0)/2 + position[1] += obj.from[1] + obj.size(1)/2 + position[2] += obj.from[2] + obj.size(2)/2 + } }) position.forEach(function(p, pi) { position[pi] = p / selected_group.children.length }) selected_group.origin = position - } else if (selected.length > 1) { - - var center = [0, 0, 0] - var i = 0; - selected.forEach(function(obj) { - i = 0; - while (i < 3) { - center[i] += obj.from[i] - center[i] += obj.to[i] - i++; - } - }) - i = 0; - while (i < 3) { - center[i] = center[i] / (selected.length * 2) - i++; - } - selected.forEach(function(obj) { - obj.origin = center.slice() - }) - } else { + Undo.initEdit({cubes: selected}) - var obj = selected[0] - var element_size = obj.size() - var element_center = new THREE.Vector3( - (element_size[0] / 2) + obj.from[0], - (element_size[1] / 2) + obj.from[1], - (element_size[2] / 2) + obj.from[2] - ) - - element_center.x -= obj.origin[0] - element_center.y -= obj.origin[1] - element_center.z -= obj.origin[2] + var center = getSelectionCenter() + + selected.forEach(cube => { + cube.transferOrigin(center) + }) + } + Canvas.updatePositions() + Undo.finishEdit('origin to geometry') +} +function getSelectionCenter() { + var center = [0, 0, 0] + var i = 0; + selected.forEach(cube => { + var m = cube.mesh + if (cube.visibility && m) { - if (obj.mesh) { - element_center.applyEuler(obj.mesh.rotation) + var pos = cube.getWorldCenter() + center[0] += pos.x + center[1] += pos.y + center[2] += pos.z } - obj.origin[0] += element_center.x - obj.origin[1] += element_center.y - obj.origin[2] += element_center.z - - obj.to[0] = obj.origin[0] + element_size[0] / 2 - obj.to[1] = obj.origin[1] + element_size[1] / 2 - obj.to[2] = obj.origin[2] + element_size[2] / 2 - - obj.from[0] = obj.origin[0] - element_size[0] / 2 - obj.from[1] = obj.origin[1] - element_size[1] / 2 - obj.from[2] = obj.origin[2] - element_size[2] / 2 + }) + for (var i = 0; i < 3; i++) { + center[i] = center[i] / selected.length } - Canvas.updatePositions() - Undo.finishEdit('origin2geometry') + return center; } function isMovementGlobal() { if (selected.length === 0 || (!settings.local_move.value && Toolbox.selected.id !== 'resize_tool')) { @@ -356,8 +337,10 @@ function scaleAll(save, size) { } if (clip && Blockbench.entity_mode === false) { $('#scaling_clipping_warning').text('Model clipping: Your model is too large for the canvas') + $('#scale_overflow_btn').css('display', 'inline-block') } else { $('#scaling_clipping_warning').text('') + $('#scale_overflow_btn').hide() } Canvas.updatePositions() if (save === true) { @@ -393,6 +376,26 @@ function cancelScaleAll() { Canvas.updatePositions() hideDialog() } +function scaleAllSelectOverflow() { + var overflow = []; + selected.forEach(function(obj) { + var clip = false + obj.from.forEach(function(ogn, i) { + + if (obj.from[i] > 32 || obj.from[i] < -16) clip = true + if (obj.to[i] > 32 || obj.to[i] < -16) clip = true + }) + if (clip) { + overflow.push(obj) + } + }) + cancelScaleAll() + selected.length = 0; + overflow.forEach(cube => { + selected.push(cube) + }) + updateSelection(); +} //Center function centerCubesAll(axis) { centerCubes(0, false) @@ -474,19 +477,24 @@ function rotateOnAxis(value, fixed, axis) { var axis_letter = getAxisLetter(axis) var origin = selected[0].origin selected.forEach(function(obj, i) { - if (!obj.rotation.equals([0,0,0])) { + if (!obj.rotation.allEqual(0)) { origin = obj.origin } }) + if (origin.allEqual(8)) { + origin = getSelectionCenter() + origin.forEach((n, ni) => { + origin[ni] = Math.round(n*2)/2 + }) + } selected.forEach(function(obj, i) { - if (obj.rotation.equals([0,0,0])) { + if (obj.rotation.allEqual(0)) { obj.origin = origin.slice() } var obj_val = value; if (!fixed) { obj_val += obj.rotation[axis] } - obj_val = Math.trimDeg(obj_val) if (settings.limited_rotation.value) { //Limit To 1 Axis @@ -813,6 +821,7 @@ BARS.defineActions(function() { }, 'group', true) } showDialog('scaling') + scaleAll(false, 1) } }) new Action({ @@ -964,6 +973,14 @@ BARS.defineActions(function() { id: 'toggle_shade', icon: 'wb_sunny', category: 'transform', + condition: () => !Blockbench.entity_mode, + click: function () {toggleCubeProperty('shade')} + }) + new Action({ + id: 'toggle_mirror_uv', + icon: 'icon-mirror_x', + category: 'transform', + condition: () => Blockbench.entity_mode, click: function () {toggleCubeProperty('shade')} }) new Action({ diff --git a/js/undo.js b/js/undo.js index 4d81c01a8..7728f7c6c 100644 --- a/js/undo.js +++ b/js/undo.js @@ -12,8 +12,10 @@ var Undo = { Undo.current_save = new Undo.save(aspects) }, finishEdit: function(action, aspects) { + if (!Undo.current_save) return; aspects = aspects || Undo.current_save.aspects //After + Blockbench.dispatchEvent('finish_edit', {aspects}) var entry = { before: Undo.current_save, post: new Undo.save(aspects), @@ -34,6 +36,7 @@ var Undo = { if (!aspects || !aspects.keep_saved) { Prop.project_saved = false; } + Blockbench.dispatchEvent('finished_edit', {aspects}) }, cancelEdit: function() { if (!Undo.current_save) return; @@ -50,7 +53,7 @@ var Undo = { var entry = Undo.history[Undo.index] Undo.loadSave(entry.before, entry.post) console.log('Undo: '+entry.action) - Blockbench.dispatchEvent('undo', {entry: entry}) + Blockbench.dispatchEvent('undo', {entry}) }, redo: function() { if (Undo.history.length <= 0) return; @@ -63,7 +66,7 @@ var Undo = { var entry = Undo.history[Undo.index-1] Undo.loadSave(entry.post, entry.before) console.log('Redo: '+entry.action) - Blockbench.dispatchEvent('redo', {}) + Blockbench.dispatchEvent('redo', {entry}) }, getItemByUUID: function(list, uuid) { if (!list || typeof list !== 'object' || !list.length) {return false;} @@ -75,7 +78,6 @@ var Undo = { i++; } return false; - }, save: function(aspects) { var scope = this; @@ -168,7 +170,7 @@ var Undo = { var obj = elements.findInArray('uuid', uuid) if (obj) { for (var face in obj.faces) { - obj.faces[face] = {uv: []} + obj.faces[face].reset() } obj.extend(data) Canvas.adaptObjectPosition(obj) diff --git a/js/util.js b/js/util.js index f06abdd8b..d102a1fc6 100644 --- a/js/util.js +++ b/js/util.js @@ -60,6 +60,17 @@ const Condition = function(condition, context) { return !!condition } } +class oneLiner { + constructor(data) { + if (data !== undefined) { + for (var key in data) { + if (data.hasOwnProperty(key)) { + this[key] = data[key] + } + } + } + } +} var cl = console.log var asyncLoop = function(o){ var i=-1; @@ -81,6 +92,20 @@ function guid() { return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } +function bbuid(l) { + l = l || 1 + let chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + var s = ''; + while (l > 0) { + var n = Math.floor(Math.random()*62) + if (n > 9) { + n = chars[n-10] + } + s += n + l--; + } + return s; +} Math.radToDeg = function(rad) { return rad / Math.PI * 180 } @@ -125,6 +150,7 @@ function limitNumber(number, min, max) { if (number < min || isNaN(number)) number = min; return number; } +Math.clamp = limitNumber; function getRectangle(a, b, c, d) { var rect = {}; if (!b && typeof a === 'object') { @@ -203,6 +229,7 @@ Array.prototype.remove = function (item) { { } Array.prototype.empty = function() { this.length = 0; + return this; } Array.prototype.findInArray = function(key, value) { for (var i = 0; i < this.length; i++) { diff --git a/js/uv.js b/js/uv.js index 102f2f6ec..dfef1b595 100644 --- a/js/uv.js +++ b/js/uv.js @@ -115,7 +115,10 @@ class UVEditor { Undo.initEdit({cubes: selected}) } var onAfter = function() { - Undo.finishEdit('dones') + Undo.finishEdit('edit UV') + if (Blockbench.entity_mode) { + scope.displayAllMappingOverlays() + } } var getInterval = function(event) { return Blockbench.entity_mode @@ -226,16 +229,6 @@ class UVEditor { $(this).find('.uv_mapping_overlay').remove() }) - this.jquery.main.on('mousewheel', function() { - if (Blockbench.entity_mode) { - scope.displayMappingOverlay() - scope.jquery.main.on('mousemove', function() { - $(scope.jquery.size).find('.uv_mapping_overlay').remove() - scope.jquery.main.off('mousemove') - }) - } - }) - if (toolbar) { this.jquery.bar = $(Toolbars.main_uv.node) this.jquery.main.append(this.jquery.bar) @@ -289,6 +282,9 @@ class UVEditor { Undo.finishEdit('uv_change') scope.disableAutoUV() scope.updateDragHandle(ui.position) + if (Blockbench.entity_mode) { + scope.displayAllMappingOverlays() + } } }) @@ -309,13 +305,25 @@ class UVEditor { }) this.jquery.frame.mousedown(function(event) { - if (Toolbox.selected.id === 'brush_tool') { + if (Toolbox.selected.paintTool) { scope.startBrush(event) } }) this.setSize(this.size) return this; } + message(msg, vars) { + msg = tl(msg, vars) + var box = $('
    ' + msg + '
    ') + this.jquery.frame.append(box) + setTimeout(function() { + box.fadeOut(200) + setTimeout(function() { + box.remove() + }, 300) + }, 1000) + } + //Brush getBrushCoordinates(event, tex) { var scope = this; var multiplier = (Blockbench.entity_mode && tex) ? tex.res/Project.texture_width : 1 @@ -332,11 +340,10 @@ class UVEditor { var texture = scope.getTexture() if (texture) { Painter.current.x = Painter.current.y = 0 - var x = scope.getBrushCoordinates(event, texture).x - var y = scope.getBrushCoordinates(event, texture).y - Painter.startBrush(texture, x, y, undefined, event) + var coords = scope.getBrushCoordinates(event, texture) + Painter.startBrush(texture, coords.x, coords.y, undefined, event) } - if (event.altKey === false && texture && texture.mode !== 'link') { + if (Toolbox.selected.id !== 'color_picker' && texture) { scope.jquery.frame.get(0).addEventListener('mousemove', scope.moveBrush, false ); document.addEventListener('mouseup', scope.stopBrush, false ); } @@ -386,17 +393,6 @@ class UVEditor { document.removeEventListener( 'mouseup', scope.stopBrush, false ); Painter.stopBrush() } - message(msg, vars) { - msg = tl(msg, vars) - var box = $('
    ' + msg + '
    ') - this.jquery.frame.append(box) - setTimeout(function() { - box.fadeOut(200) - setTimeout(function() { - box.remove() - }, 300) - }, 1000) - } //Get getPixelSize() { if (Blockbench.entity_mode) { @@ -426,7 +422,7 @@ class UVEditor { } } getTexture() { - return getTextureById(selected[0].faces[this.face].texture) + return selected[0].faces[this.face].getTexture() } forCubes(cb) { var i = 0; @@ -437,6 +433,7 @@ class UVEditor { } //Set setSize(size, cancel_load) { + var old_size = this.size; this.size = size this.jquery.frame.width(size) if (uv_dialog.editors !== undefined && this === uv_dialog.editors.single) { @@ -447,6 +444,9 @@ class UVEditor { this.height = size / (Project.texture_width/Project.texture_height) this.jquery.frame.height(this.height) $('.panel#textures').css('top', 133+(size / (Project.texture_width/Project.texture_height))+'px') + if (old_size !== size) { + this.displayAllMappingOverlays(true) + } } else { this.height = size this.jquery.frame.height(size) @@ -489,12 +489,14 @@ class UVEditor { } if (load !== false) this.loadData() } - setFace(face) { + setFace(face, update) { this.face = face - this.loadData() if (this.id === 'main_uv') { $('input#'+face+'_radio').prop("checked", true) } + if (update !== false) { + this.loadData() + } return this; } setFrameColor(black) { @@ -542,12 +544,14 @@ class UVEditor { //Set Rotation BarItems.uv_rotation.set(face.rotation||0) - this.displayTexture(face.texture) + this.displayTexture(face) this.displayFrame()//and transform info this.displayTools() this.displaySliders() this.updateDragHandle() - + if (Blockbench.entity_mode) { + this.displayAllMappingOverlays() + } if (this.id !== 'main_uv') { this.displayTools() } @@ -570,12 +574,14 @@ class UVEditor { }) } else { - + var trim = v => Math.round(v*1000+0.3)/1000; var pixelSize = this.size/16 - var left = this.jquery.size.position().left / pixelSize - var top = this.jquery.size.position().top / pixelSize * (Project.texture_width/Project.texture_height) - var left2 = (this.jquery.size.width()) / pixelSize + left - var top2 = (this.jquery.size.height()) / pixelSize + top + + var left = trim( this.jquery.size.position().left / pixelSize); + var top = trim( this.jquery.size.position().top / pixelSize * (Project.texture_width/Project.texture_height)); + var left2= Math.clamp(trim( (this.jquery.size.width()) / pixelSize + left), 0, 16); + var top2 = Math.clamp(trim( (this.jquery.size.height()) / pixelSize + top), 0, 16); + var uvTag = this.getUVTag() if (uvTag[0] > uvTag[2]) { @@ -600,25 +606,21 @@ class UVEditor { main_uv.loadData() } } - applyTexture(id) { + applyTexture(uuid) { var scope = this; Undo.initEdit({cubes: selected, uv_only: true}) this.forCubes(obj => { - obj.faces[scope.face].texture = '#'+id + obj.faces[scope.face].texture = uuid }) this.loadData() Canvas.updateSelectedFaces() Undo.finishEdit('apply_texture') } - displayTexture(id) { - if (!id || id === null) { + displayTexture(face) { + var tex = face.getTexture() + if (!tex || typeof tex !== 'object' || tex.error) { this.displayEmptyTexture() } else { - var tex = getTextureById(id+'') - if (tex === undefined || tex.error) { - this.displayEmptyTexture() - return; - } this.setFrameColor(tex.dark_box) var css = 'url("'+tex.source.split('\\').join('\\\\').replace(/ /g, '%20')+'")' this.jquery.frame.css('background-image', css) @@ -717,24 +719,40 @@ class UVEditor { this.updateDragHandle() this.displayTransformInfo() } + //Overlay displayMappingOverlay() { if (!Blockbench.entity_mode) return this; var scope = this; + var sides = this.getMappingOverlay() + + $(scope.jquery.size).find('.uv_mapping_overlay').remove() + scope.jquery.size.append(sides) + + return this; + } + getMappingOverlay(cube, absolute) { + var scope = this; + var sides = $('
    ') var pixels = scope.getPixelSize() + if (!cube) cube = selected[0] function addElement(x, y, width, height, n, color) { + if (absolute) { + x += cube.uv_offset[0]; + y += cube.uv_offset[1]; + } x *= pixels; y *= pixels; width = limitNumber(width *pixels + x, 0, scope.size) - x; height = limitNumber(height*pixels + y, 0, scope.height)- y; - scope.jquery.size.append('
    ') + sides.append($(`
    `)) } - var size = selected[0].size(undefined, true) + var size = cube.size(undefined, true) - $(scope.jquery.size).find('.uv_mapping_overlay').remove() + sides.attr('size_hash', `${cube.uv_offset[0]}_${cube.uv_offset[1]}_${size[0]}_${size[1]}_${size[2]}`) addElement(size[2], 0, size[0], size[2], '#b4d4e1', '#ecf8fd') addElement(size[2]+size[0], 0, size[0], size[2], '#536174', '#6e788c') @@ -742,7 +760,32 @@ class UVEditor { addElement(size[2], size[2], size[0], size[1], '#5bbcf4', '#7BD4FF') addElement(size[2]+size[0], size[2], size[2], size[1], '#f48686', '#FFA7A4') addElement(2*size[2]+size[0], size[2], size[0], size[1],'#f8dd72', '#FFF899') + + return sides; } + displayAllMappingOverlays(force_reload) { + var scope = this; + var cycle = bbuid(4) + if (this.showing_overlays) { + elements.forEach(cube => { + var size = cube.size(undefined, true) + var hash = `${cube.uv_offset[0]}_${cube.uv_offset[1]}_${size[0]}_${size[1]}_${size[2]}` + var c = scope.jquery.frame.find(`.mapping_overlay_cube:not(.${cycle})[size_hash="${hash}"]`).first() + if (force_reload || !c.length) { + var sides = scope.getMappingOverlay(cube, true) + sides.addClass(cycle) + scope.jquery.frame.append(sides) + } else { + c.addClass(cycle) + } + }) + $(`.mapping_overlay_cube:not(.${cycle})`).remove() + $('.mapping_overlay_cube').removeClass(cycle) + } else { + $(scope.jquery.frame).find('.mapping_overlay_cube').remove() + } + } + //UI displaySliders() { this.sliders.pos_x.update() this.sliders.pos_y.update() @@ -753,7 +796,7 @@ class UVEditor { //Cullface var face = selected[0].faces[this.face] BarItems.cullface.set(face.cullface||'off') - BarItems.face_tint.setIcon(face.tintindex !== undefined ? 'check_box' : 'check_box_outline_blank') + BarItems.face_tint.setIcon(face.tint ? 'check_box' : 'check_box_outline_blank') } updateDragHandle() { var pos = this.jquery.size.position() @@ -914,10 +957,10 @@ class UVEditor { break; case 'east': uv = [ - 16 - obj.from[2], - 16 - obj.from[1], 16 - obj.to[2], 16 - obj.to[1], + 16 - obj.from[2], + 16 - obj.from[1], ]; break; case 'up': @@ -1024,15 +1067,11 @@ class UVEditor { } switchTint(event) { var scope = this; - var val = selected[0].faces[scope.face].tintindex === undefined + var val = !selected[0].faces[scope.face].tint if (event === true || event === false) val = event this.forCubes(obj => { - if (val) { - obj.faces[scope.face].tintindex = 0 - } else { - delete obj.faces[scope.face].tintindex - } + obj.faces[scope.face].tint = val }) if (val) { this.message('uv_editor.tint_on') @@ -1043,13 +1082,9 @@ class UVEditor { } rotate() { var scope = this; - var value = BarItems.uv_rotation.get() + var value = parseInt(BarItems.uv_rotation.get()) this.forCubes(obj => { - if (value == 0) { - delete obj.faces[scope.face].rotation - } else { - obj.faces[scope.face].rotation = parseInt(value) - } + obj.faces[scope.face].rotation = value Canvas.updateUV(obj) }) this.displayTransformInfo() @@ -1057,12 +1092,9 @@ class UVEditor { } setRotation(value) { var scope = this; + value = parseInt(value) this.forCubes(obj => { - if (value == 0) { - delete obj.faces[scope.face].rotation - } else { - obj.faces[scope.face].rotation = parseInt(value) - } + obj.faces[scope.face].rotation = value Canvas.updateUV(obj) }) this.loadData() @@ -1105,15 +1137,7 @@ class UVEditor { return; } var tag = selected[0].faces[face] - var new_tag = { - uv: tag.uv.slice(), - face: face - } - if (tag.texture !== undefined) new_tag.texture = tag.texture - if (tag.cullface) new_tag.cullface = tag.cullface - if (tag.rotation) new_tag.rotation = tag.rotation - if (tag.enabled !== undefined) new_tag.enabled = tag.enabled - if (tag.tintindex !== undefined) new_tag.tintindex = tag.tintindex + var new_tag = new Face().extend(tag) uv_dialog.clipboard.push(new_tag) } if (event.shiftKey) { @@ -1141,15 +1165,7 @@ class UVEditor { function applyFace(tag, face) { if (!face) face = tag.face selected.forEach(function(obj) { - var target = obj.faces[face] - target.uv = tag.uv.slice() - - if (tag.texture !== undefined || target.texture !== undefined) target.texture = tag.texture - if (tag.cullface || target.cullface) target.cullface = tag.cullface - if (tag.rotation || target.rotation) target.rotation = tag.rotation - if (tag.enabled !== undefined || target.enabled !== undefined) target.enabled = tag.enabled - if (tag.tintindex !== undefined || target.texture !== undefined) target.tintindex = tag.tintindex - + obj.faces[face].extend(tag) Canvas.updateUV(obj) }) } @@ -1194,12 +1210,7 @@ class UVEditor { var scope = this; this.forCubes(obj => { scope.getFaces(event).forEach(function(side) { - obj.faces[side].uv = [0, 0, 1, 1] - delete obj.faces[side].texture; - delete obj.faces[side].rotation; - delete obj.faces[side].tintindex; - delete obj.faces[side].enabled; - delete obj.faces[side].cullface; + obj.faces[side].reset() }) Canvas.adaptObjectFaces(obj) }) @@ -1213,6 +1224,9 @@ class UVEditor { } } UVEditor.prototype.menu = new Menu([ + 'copy', + 'paste', + /* {icon: 'content_copy', name: 'menu.uv.copy', click: function(editor) { editor.copy(event) }}, @@ -1220,28 +1234,16 @@ class UVEditor { Undo.initEdit({cubes: selected, uv_only: true}) editor.paste(event) Undo.finishEdit('uv_paste') - }}, + }},*/ {icon: 'photo_size_select_large', name: 'menu.uv.mapping', children: function(editor) { return [ {icon: editor.reference_face.enabled!==false ? 'check_box' : 'check_box_outline_blank', name: 'menu.uv.mapping.export', click: function(editor) { Undo.initEdit({cubes: selected, uv_only: true}) editor.toggleUV(event) Undo.finishEdit('uv_toggle') }}, - {icon: 'zoom_out_map', name: 'menu.uv.mapping.maximize', click: function(editor) { - Undo.initEdit({cubes: selected, uv_only: true}) - editor.maximize(event) - Undo.finishEdit('uv_maximize') - }}, - {icon: 'brightness_auto', name: 'menu.uv.mapping.auto', click: function(editor) { - Undo.initEdit({cubes: selected, uv_only: true}) - editor.setAutoSize(event) - Undo.finishEdit('uv_auto') - }}, - {icon: 'brightness_auto', name: 'menu.uv.mapping.rel_auto', click: function(editor) { - Undo.initEdit({cubes: selected, uv_only: true}) - editor.setRelativeAutoSize(event) - Undo.finishEdit('uv_auto') - }}, + 'uv_maximize', + 'uv_auto', + 'uv_rel_auto', {icon: 'rotate_90_degrees_ccw', name: 'menu.uv.mapping.rotation', children: function() { var off = 'radio_button_unchecked' var on = 'radio_button_checked' @@ -1275,7 +1277,8 @@ class UVEditor { Undo.initEdit({cubes: selected, uv_only: true}) editor.mirrorX(event) Undo.finishEdit('uv_mirror') - }}, + } + }, { icon: (editor.reference_face.uv[1] > editor.reference_face.uv[3] ? 'check_box' : 'check_box_outline_blank'), name: 'menu.uv.mapping.mirror_y', @@ -1283,13 +1286,14 @@ class UVEditor { Undo.initEdit({cubes: selected, uv_only: true}) editor.mirrorY(event) Undo.finishEdit('uv_mirror') - }}, + } + }, ]}}, { - icon: (editor) => (editor.reference_face.tintindex === 0 ? 'check_box' : 'check_box_outline_blank'), + icon: (editor) => (editor.reference_face.tint ? 'check_box' : 'check_box_outline_blank'), name: 'menu.uv.tint', click: function(editor) { Undo.initEdit({cubes: selected, uv_only: true}) - editor.switchTint(selected[0].faces[editor.face].tintindex !== 0) + editor.switchTint(selected[0].faces[editor.face].tint) Undo.finishEdit('face_tint') } }, @@ -1313,7 +1317,7 @@ class UVEditor { arr.push({ name: t.name, icon: (t.mode === 'link' ? t.img : t.source), - click: function(editor) {editor.applyTexture(t.id)} + click: function(editor) {editor.applyTexture(t.uuid)} }) }) return arr; @@ -1552,16 +1556,7 @@ const uv_dialog = { function addToClipboard(face) { var tag = selected[0].faces[face] - var new_tag = { - uv: tag.uv.slice(), - face: face - } - if (tag.texture) new_tag.texture = tag.texture - if (tag.cullface) new_tag.cullface = tag.cullface - if (tag.rotation) new_tag.rotation = tag.rotation - if (tag.enabled !== undefined) new_tag.enabled = tag.enabled - if (tag.tintindex !== undefined) new_tag.tintindex = tag.tintindex - uv_dialog.clipboard.push(new_tag) + uv_dialog.clipboard.push(new Face(tag)) } if (uv_dialog.hoveredSide) { addToClipboard(uv_dialog.hoveredSide) @@ -1583,15 +1578,7 @@ const uv_dialog = { function applyFace(tag, face) { if (!face) face = tag.face selected.forEach(function(obj) { - var target = obj.faces[face] - target.uv = tag.uv.slice() - - if (tag.texture || target.texture) target.texture = tag.texture - if (tag.cullface || target.cullface) target.cullface = tag.cullface - if (tag.rotation || target.rotation) target.rotation = tag.rotation - if (tag.enabled !== undefined || target.enabled !== undefined) target.enabled = tag.enabled - if (tag.tintindex !== undefined || target.texture !== undefined) target.tintindex = tag.tintindex - + obj.faces[face].extend(tag) Canvas.updateUV(obj) }) } @@ -1646,7 +1633,7 @@ BARS.defineActions(function() { onChange: function(slider) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('rotate') - Undo.finishEdit('uv') + Undo.finishEdit('uv rotate') } }) new BarSelect({ @@ -1677,7 +1664,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('maximize', event) - Undo.finishEdit('uv') + Undo.finishEdit('uv maximize') } }) new Action({ @@ -1688,7 +1675,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('setAutoSize', event) - Undo.finishEdit('uv') + Undo.finishEdit('auto uv') } }) new Action({ @@ -1699,7 +1686,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('setRelativeAutoSize', event) - Undo.finishEdit('uv') + Undo.finishEdit('auto uv') } }) new Action({ @@ -1710,7 +1697,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('mirrorX', event) - Undo.finishEdit('uv') + Undo.finishEdit('mirror uv') } }) new Action({ @@ -1721,7 +1708,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('mirrorY', event) - Undo.finishEdit('uv') + Undo.finishEdit('mirror uv') } }) new Action({ @@ -1732,7 +1719,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('clear', event) - Undo.finishEdit('uv') + Undo.finishEdit('remove face') } }) new Action({ @@ -1743,7 +1730,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('reset', event) - Undo.finishEdit('uv') + Undo.finishEdit('reset uv') } }) new Action({ @@ -1754,7 +1741,7 @@ BARS.defineActions(function() { click: function (e) { Undo.initEdit({cubes: selected, uv_only: true}) main_uv.applyAll(e) - Undo.finishEdit('uv') + Undo.finishEdit('uv apply all') } }) new BarSelect({ @@ -1773,7 +1760,7 @@ BARS.defineActions(function() { onChange: function(sel, event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('switchCullface') - Undo.finishEdit('uv') + Undo.finishEdit('cullface') } }) new Action({ @@ -1784,7 +1771,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('autoCullface', event) - Undo.finishEdit('uv') + Undo.finishEdit('auto cullface') } }) new Action({ @@ -1794,7 +1781,7 @@ BARS.defineActions(function() { click: function (event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('switchTint', event) - Undo.finishEdit('uv') + Undo.finishEdit('tint') } }) new Action({ @@ -1806,4 +1793,15 @@ BARS.defineActions(function() { showUVShiftDialog() } }) + new Action({ + id: 'toggle_uv_overlay', + condition: () => Blockbench.entity_mode, + icon: 'crop_landscape',//'crop_landscape' + category: 'uv', + click: function () { + main_uv.showing_overlays = !main_uv.showing_overlays + BarItems.toggle_uv_overlay.setIcon(main_uv.showing_overlays ? 'view_quilt' : 'crop_landscape') + main_uv.displayAllMappingOverlays() + } + }) }) diff --git a/js/web.js b/js/web.js index 5f1d19e62..3f9c66e7e 100644 --- a/js/web.js +++ b/js/web.js @@ -22,12 +22,10 @@ function tryLoadPOSTModel() { if ($('#post_textures').text() !== '') { var data = JSON.parse( $('#post_textures').text() ) for (var key in data) { - if (data.hasOwnProperty(key)) { - var tex = getTextureById(key+''); - if (tex) { - tex.img.src = '' - tex.source = 'data:image/png;base64,'+data[key] - } + var tex = textures.findInArray('id', key+''); + if (tex) { + tex.img.src = '' + tex.source = 'data:image/png;base64,'+data[key] } } textures.forEach(function(tex) { diff --git a/lang/de.json b/lang/de.json index 321daef96..0dd46898d 100644 --- a/lang/de.json +++ b/lang/de.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Vierfachansicht", "menu.preview.fullview": "Vollansicht", "menu.preview.stop_drag": "Hintergrundpositionierung beenden", - "menu.uv.copy": "Kopieren", - "menu.uv.paste": "Einfügen", "menu.uv.mapping": "UV Mapping", "menu.uv.mapping.export": "Exportieren", - "menu.uv.mapping.maximize": "Maximieren", - "menu.uv.mapping.auto": "Auto UV", - "menu.uv.mapping.rel_auto": "Rel. Auto UV", "menu.uv.mapping.rotation": "Drehung", "menu.uv.mapping.mirror_x": "Spiegeln X", "menu.uv.mapping.mirror_y": "Spiegeln Y", @@ -825,5 +820,21 @@ "action.open_backup_folder": "Backup-Ordner öffnen", "action.open_backup_folder.desc": "Öffnet den Backup-Ordner von Blockbench", "switches.mirror": "UV Spiegeln", - "language_name": "Deutsch" + "language_name": "Deutsch", + "message.plugin_reload": "%0 lokale Plugins wurden neugeladen", + "settings.brightness": "Helligkeit", + "settings.brightness.desc": "Helligkeit der Vorschau. Standardwert ist 50", + "menu.preview.perspective.reset": "Kamera zurücksetzen", + "action.fill_mode": "Füllmodus", + "action.fill_mode.desc": "Modus des Farbeimers", + "action.fill_mode.face": "Fläche", + "action.fill_mode.color": "Farbe", + "action.fill_mode.cube": "Element", + "action.toggle_mirror_uv": "UV spiegeln", + "action.toggle_mirror_uv.desc": "UV Mapping der ausgewählten Elemente auf der X Achse spiegeln.", + "action.toggle_uv_overlay": "UV Maske einblenden", + "action.toggle_uv_overlay.desc": "Blendet UV Masken für alle Elemente über der Textur ein", + "menu.texture.blank": "Auf leere Flächen anwenden", + "dialog.scale.select_overflow": "Überstehendes auswählen", + "dialog.create_texture.compress": "Template verdichten" } \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index d83d4695a..955006d1e 100644 --- a/lang/en.json +++ b/lang/en.json @@ -142,6 +142,7 @@ "message.invalid_plugin": "Invalid Plugin File, See Console", "message.load_plugin_app": "Do you want to allow this plugin to make changes to your PC? Only load plugins from people you trust.", "message.load_plugin_web": "Do you want to load this plugin? Only load plugins from people you trust.", + "message.plugin_reload": "Reloaded %0 local plugins", "message.preset_no_info": "Preset does not contain information for this slot", "message.restart_to_update": "Restart Blockbench to apply changes", "message.save_file": "Saved as %0", @@ -201,6 +202,7 @@ "dialog.scale.scale": "Scale", "dialog.scale.clipping": "Model clipping: Your model is too large for the canvas", "dialog.scale.confirm": "Scale", + "dialog.scale.select_overflow": "Select Overflow", "dialog.plugins.title": "Plugins", "dialog.plugins.installed": "Installed", @@ -228,6 +230,7 @@ "dialog.create_texture.name": "Name", "dialog.create_texture.folder": "Folder", "dialog.create_texture.template": "Template", + "dialog.create_texture.compress": "Compress Template", "dialog.create_texture.resolution": "Resolution", "dialog.create_gif.title": "Record GIF", @@ -325,6 +328,8 @@ "settings.display_skin.desc": "Skin used for the display reference player model", "settings.seethrough_outline": "X-Ray Outlines", "settings.seethrough_outline.desc": "Show outlines through objects", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", "settings.shading": "Shading", "settings.shading.desc": "Enable shading", "settings.transparency": "Transparency", @@ -441,6 +446,11 @@ "action.brush_mode.desc": "Mode of the brush", "action.brush_mode.brush": "Round", "action.brush_mode.noise": "Noise", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", "action.brush_color": "Color", "action.brush_color.desc": "Color of the brush", "action.slider_brush_size": "Size", @@ -614,6 +624,8 @@ "action.toggle_autouv.desc": "Toggle the auto UV setting of the selected cubes.", "action.toggle_shade": "Toggle Shading", "action.toggle_shade.desc": "Toggle the shading of the selected cubes.", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", "action.rename": "Rename", "action.rename.desc": "Change the name of the selected cubes.", "action.update_autouv": "Update Auto UV", @@ -710,6 +722,8 @@ "action.face_tint.desc": "Enables the tint option for the current face", "action.uv_shift": "Shift UV", "action.uv_shift.desc": "Shift all UV regions by a fixed amount or mathematical expression", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", "action.add_animation": "Add Animation", "action.add_animation.desc": "Create a blank animation", @@ -768,6 +782,7 @@ "menu.group.resolve": "Resolve", "menu.texture.face": "Apply to Face", + "menu.texture.blank": "Apply to Untextured Faces", "menu.texture.cube": "Apply to Cubes", "menu.texture.file": "File", "menu.texture.refresh": "Refresh", @@ -788,17 +803,13 @@ "menu.preview.screenshot": "Screenshot", "menu.preview.perspective": "Perspective", "menu.preview.perspective.normal": "Normal", + "menu.preview.perspective.reset": "Reset Camera", "menu.preview.quadview": "Quad View", "menu.preview.fullview": "Full View", "menu.preview.stop_drag": "Stop Background Positioning", - "menu.uv.copy": "Copy", - "menu.uv.paste": "Paste", "menu.uv.mapping": "UV Mapping", "menu.uv.mapping.export": "Export", - "menu.uv.mapping.maximize": "Maximize", - "menu.uv.mapping.auto": "Auto UV", - "menu.uv.mapping.rel_auto": "Rel. Auto UV", "menu.uv.mapping.rotation": "Rotation", "menu.uv.mapping.mirror_x": "Mirror X", "menu.uv.mapping.mirror_y": "Mirror Y", diff --git a/lang/es.json b/lang/es.json index 208594934..89a556fc8 100644 --- a/lang/es.json +++ b/lang/es.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Vista Cuádruple", "menu.preview.fullview": "Vista Completa", "menu.preview.stop_drag": "Parar la Colocación del Fondo", - "menu.uv.copy": "Copiar", - "menu.uv.paste": "Pegar", "menu.uv.mapping": "Mapeado del UV", "menu.uv.mapping.export": "Exportar", - "menu.uv.mapping.maximize": "Maximizar", - "menu.uv.mapping.auto": "Auto UV", - "menu.uv.mapping.rel_auto": "Rel. Auto UV", "menu.uv.mapping.rotation": "Rotación", "menu.uv.mapping.mirror_x": "Invertir X", "menu.uv.mapping.mirror_y": "Invertir Y", @@ -824,6 +819,22 @@ "action.color_picker.desc": "Herramienta para seleccionar el color de píxeles en tu textura", "action.open_backup_folder": "Abrir Carpeta de Backups", "action.open_backup_folder.desc": "Abre la carpeta de backups de Blockbench", - "switches.mirror": "Mirror UV", - "language_name": "English" + "switches.mirror": "Invertir UV", + "language_name": "Inglés", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/fr.json b/lang/fr.json index 19e47b116..27751c4ec 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Quadruple vue", "menu.preview.fullview": "Vue complète", "menu.preview.stop_drag": "Arrêter le positionnement en arrière-plan", - "menu.uv.copy": "Copier", - "menu.uv.paste": "Coller", "menu.uv.mapping": "Cartographie UV", "menu.uv.mapping.export": "Exporter", - "menu.uv.mapping.maximize": "Maximiser", - "menu.uv.mapping.auto": "UV automatique", - "menu.uv.mapping.rel_auto": "Rel. UV automatique", "menu.uv.mapping.rotation": "Rotation", "menu.uv.mapping.mirror_x": "Miroir X", "menu.uv.mapping.mirror_y": "Miroir Y", @@ -825,5 +820,21 @@ "action.open_backup_folder": "Ouvrir le dossier de sauvegarde", "action.open_backup_folder.desc": "Ouvre le dossier de sauvegarde de Blockbench", "switches.mirror": "Mirror UV", - "language_name": "English" + "language_name": "English", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/ja.json b/lang/ja.json index 35957ecac..907ca0cbb 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -5,7 +5,7 @@ "dialog.close": "閉じる", "dialog.import": "インポート", "dialog.save": "保存", - "dialog.discard": "削除", + "dialog.discard": "保存しない", "dialog.dontshowagain": "二度と表示しないようにします", "data.cube": "キューブ", "data.cubes": "キューブ", @@ -561,13 +561,8 @@ "menu.preview.quadview": "クワッドビュー", "menu.preview.fullview": "全景", "menu.preview.stop_drag": "背景のポジショニングを停止する", - "menu.uv.copy": "コピー", - "menu.uv.paste": "ペースト", "menu.uv.mapping": "UVマッピング", "menu.uv.mapping.export": "エクスポート", - "menu.uv.mapping.maximize": "最大化", - "menu.uv.mapping.auto": "自動UV", - "menu.uv.mapping.rel_auto": "小.自動UV", "menu.uv.mapping.rotation": "回転", "menu.uv.mapping.mirror_x": "ミラーX", "menu.uv.mapping.mirror_y": "ミラーY", @@ -743,15 +738,15 @@ "layout.color.wireframe": "ワイヤーフレーム", "layout.color.wireframe.desc": "Wireframe view lines", "action.add_animation": "アニメーションを加える", - "action.add_animation.desc": "action.add_animation.desc", + "action.add_animation.desc": "Create a blank animation", "action.load_animation_file": "アニメーションをインポートする", - "action.load_animation_file.desc": "action.load_animation_file.desc", + "action.load_animation_file.desc": "Import an animation file", "action.play_animation": "アニメーションを再生する", - "action.play_animation.desc": "action.play_animation.desc", + "action.play_animation.desc": "Preview the selected animation", "action.export_animation_file": "アニメーションをエクスポートする", - "action.export_animation_file.desc": "action.export_animation_file.desc", + "action.export_animation_file.desc": "Export a json file with the current animations", "action.slider_keyframe_time": "タイムコード", - "action.slider_keyframe_time.desc": "action.slider_keyframe_time.desc", + "action.slider_keyframe_time.desc": "Change the timecode of the selected keyframes", "timeline.rotation": "ローテーション", "timeline.position": "ポジション", "timeline.scale": "スケール", @@ -807,23 +802,39 @@ "settings.seethrough_outline": "X-Rey アウトライン", "settings.seethrough_outline.desc": "オブジェクトを通してアウトラインを表示する", "mode.edit": "編集", - "mode.paint": "ペイント", - "mode.display": "ディスプレイ", + "mode.paint": "Paint", + "mode.display": "Display", "mode.animate": "生き物", - "status_bar.recording_gif": "Recording GIF", - "status_bar.processing_gif": "Processing GIF", - "settings.backup_retain": "Backup Retain Duration", - "settings.backup_retain.desc": "Set how long Blockbench retains old backups in days", + "status_bar.recording_gif": "GIFの録音", + "status_bar.processing_gif": "GIFの加工処理", + "settings.backup_retain": "バックアップ保持期間", + "settings.backup_retain.desc": "Blockbenchがバックアップデーターを保持する期間を設定します", "action.rotate_tool": "回転", - "action.rotate_tool.desc": "Tool to select and rotate elements", - "action.fill_tool": "Paint Bucket", - "action.fill_tool.desc": "Paint bucket to fill entire faces with one color", - "action.eraser": "Eraser", - "action.eraser.desc": "Eraser tool to replace colors on a texture with transparency", - "action.color_picker": "Color Picker", - "action.color_picker.desc": "Tool to pick the color of pixels on your texture", - "action.open_backup_folder": "Open Backup Folder", - "action.open_backup_folder.desc": "Opens the Blockbench backup folder", - "switches.mirror": "Mirror UV", - "language_name": "English" + "action.rotate_tool.desc": "選択中の要素を回転させることができます", + "action.fill_tool": "バケツ", + "action.fill_tool.desc": "選択欄を塗りつぶすことができます", + "action.eraser": "消しゴム", + "action.eraser.desc": "テクスチャの色を消去できます", + "action.color_picker": "カラーピッカー", + "action.color_picker.desc": "テクスチャの色を選択できます", + "action.open_backup_folder": "バックアップフォルダーを開く", + "action.open_backup_folder.desc": "Blockbenchのバックアップフォルダーを開きます", + "switches.mirror": "ミラーUV", + "language_name": "English", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/nl.json b/lang/nl.json index 69d742588..cde4a308f 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Vierdelige Weergave", "menu.preview.fullview": "Volledige Weergave", "menu.preview.stop_drag": "Stop Achtergrong Positionering", - "menu.uv.copy": "Kopiëren", - "menu.uv.paste": "Plakken", "menu.uv.mapping": "UV Mapping", "menu.uv.mapping.export": "Exporteren", - "menu.uv.mapping.maximize": "Maximaliseer", - "menu.uv.mapping.auto": "Auto UV", - "menu.uv.mapping.rel_auto": "Rel. Auto UV", "menu.uv.mapping.rotation": "Rotatie", "menu.uv.mapping.mirror_x": "Spiegel X", "menu.uv.mapping.mirror_y": "Spiegel Y", @@ -825,5 +820,21 @@ "action.open_backup_folder": "Open Backup Folder", "action.open_backup_folder.desc": "Opens the Blockbench backup folder", "switches.mirror": "Mirror UV", - "language_name": "English" + "language_name": "English", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/pl.json b/lang/pl.json index d21196878..aec983e23 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -11,7 +11,7 @@ "data.cubes": "Kostki", "data.group": "Grupa", "data.texture": "Tekstura", - "data.plugin": "Plugin", + "data.plugin": "Wtyczka", "data.preview": "Podgląd", "data.toolbar": "Pasek narzędzi", "data.image": "Obraz", @@ -561,13 +561,8 @@ "menu.preview.quadview": "Widok Quad", "menu.preview.fullview": "Pełny widok", "menu.preview.stop_drag": "Skończ pozycjonowanie tła", - "menu.uv.copy": "Kopiuj", - "menu.uv.paste": "Wklej", "menu.uv.mapping": "Mapowanie UV", "menu.uv.mapping.export": "Eksport", - "menu.uv.mapping.maximize": "Maksymalizuj", - "menu.uv.mapping.auto": "Automatyczne UV", - "menu.uv.mapping.rel_auto": "Względne automatyczne UV", "menu.uv.mapping.rotation": "Rotacja", "menu.uv.mapping.mirror_x": "Odbicie X", "menu.uv.mapping.mirror_y": "Odbicie Y", @@ -784,46 +779,62 @@ "action.slider_animation_length": "Długość animacji", "action.slider_animation_length.desc": "Zmień długość zaznaczonej animacji", "menu.group.material": "Ustal materiał", - "action.camera_reset": "Reset Camera", - "action.camera_reset.desc": "Reset the current preview to the default camera angle", - "panel.variable_placeholders": "Variable Placeholders", - "panel.variable_placeholders.info": "List the variables you want to preview via name=value", - "status_bar.vertex_distance": "Distance: %0", - "dialog.create_gif.title": "Record GIF", - "dialog.create_gif.length": "Length (Seconds)", + "action.camera_reset": "Zresetój Kamerę", + "action.camera_reset.desc": "\n Zresetuj bieżący podgląd do domyślnego kąta kamery", + "panel.variable_placeholders": "\n Zmienne symbole zastępcze", + "panel.variable_placeholders.info": " Wymień zmienne, które chcesz podglądnąć za pomocą nazwy = wartość", + "status_bar.vertex_distance": "Odległość: 0%", + "dialog.create_gif.title": "Zapisz GIF", + "dialog.create_gif.length": "Długość (sekundy)", "dialog.create_gif.fps": "FPS", - "dialog.create_gif.compression": "Compression Amount", - "dialog.create_gif.play": "Start Animation", - "category.animation": "Animation", - "action.record_model_gif": "Record GIF", - "action.record_model_gif.desc": "Record an animated GIF of the model from the current angle", - "display.mirror": "Mirror", + "dialog.create_gif.compression": "Ilość kompresji", + "dialog.create_gif.play": "Rozpocznij animację", + "category.animation": "Animacja", + "action.record_model_gif": "Zapisz GIF", + "action.record_model_gif.desc": "\n Nagraj animowany GIF modelu z bieżącego kąta", + "display.mirror": "Lustro", "data.separator": "Separator", - "message.set_background_position.title": "Background Position", - "menu.preview.background.set_position": "Set Position", - "dialog.toolbar_edit.hidden": "Hidden", - "action.export_class_entity": "Export Java Entity", - "action.export_class_entity.desc": "Export the entity model as a Java class", - "settings.seethrough_outline": "X-Ray Outlines", - "settings.seethrough_outline.desc": "Show outlines through objects", - "mode.edit": "Edit", - "mode.paint": "Paint", - "mode.display": "Display", - "mode.animate": "Animate", - "status_bar.recording_gif": "Recording GIF", - "status_bar.processing_gif": "Processing GIF", - "settings.backup_retain": "Backup Retain Duration", - "settings.backup_retain.desc": "Set how long Blockbench retains old backups in days", - "action.rotate_tool": "Rotate", - "action.rotate_tool.desc": "Tool to select and rotate elements", - "action.fill_tool": "Paint Bucket", - "action.fill_tool.desc": "Paint bucket to fill entire faces with one color", - "action.eraser": "Eraser", - "action.eraser.desc": "Eraser tool to replace colors on a texture with transparency", - "action.color_picker": "Color Picker", - "action.color_picker.desc": "Tool to pick the color of pixels on your texture", - "action.open_backup_folder": "Open Backup Folder", - "action.open_backup_folder.desc": "Opens the Blockbench backup folder", - "switches.mirror": "Mirror UV", - "language_name": "English" + "message.set_background_position.title": "Pozycja tła", + "menu.preview.background.set_position": "Ustaw pozycję", + "dialog.toolbar_edit.hidden": "\n Ukryty", + "action.export_class_entity": "Eksportuj byt Java", + "action.export_class_entity.desc": "Wyeksportuj model bytu jako klasę Java", + "settings.seethrough_outline": "Kontury X-Ray", + "settings.seethrough_outline.desc": "Pokaż kontury przez obiekty", + "mode.edit": "Edytować", + "mode.paint": "Farba", + "mode.display": "Ekspozycja", + "mode.animate": "Animować", + "status_bar.recording_gif": "Nagrywanie GIF", + "status_bar.processing_gif": "Przetwarzanie GIF", + "settings.backup_retain": "Czas podtrzymania kopii zapasowej", + "settings.backup_retain.desc": "Ustaw jak długo Blockbench zachowuje stare kopie zapasowe w dniach", + "action.rotate_tool": "Narzędzie oobracania", + "action.rotate_tool.desc": "Narzędzie do wybierania i obracania elementów", + "action.fill_tool": "Wiaderko farby", + "action.fill_tool.desc": "Wiaderko farby zalewa maluje obszar na jeden kolor", + "action.eraser": "Gumka", + "action.eraser.desc": "Gumka zastępuje teksturę przeżroczystością", + "action.color_picker": "Prubnik koloru", + "action.color_picker.desc": "Narzędzie do wyboru koloru pikseli na teksturze", + "action.open_backup_folder": "Otwórz folder kopii zapasowej", + "action.open_backup_folder.desc": "Otwórz folder kopii zapasowej BlockBench", + "switches.mirror": "Lustro UV", + "language_name": "Polski", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/ru.json b/lang/ru.json index c1257ad30..592368084 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Четыре вида", "menu.preview.fullview": "Полный вид", "menu.preview.stop_drag": "Закончить изменение позиции", - "menu.uv.copy": "Копировать", - "menu.uv.paste": "Вставить", "menu.uv.mapping": "UV-преобразование", "menu.uv.mapping.export": "Экспортировать", - "menu.uv.mapping.maximize": "Максимизировать", - "menu.uv.mapping.auto": "Авто UV", - "menu.uv.mapping.rel_auto": "Относительный авто UV", "menu.uv.mapping.rotation": "Поворот", "menu.uv.mapping.mirror_x": "Отразить ось X", "menu.uv.mapping.mirror_y": "Отразить ось Y", @@ -825,5 +820,21 @@ "action.open_backup_folder": "Открыть папку автосохранений", "action.open_backup_folder.desc": "Opens the Blockbench backup folder", "switches.mirror": "Mirror UV", - "language_name": "English" + "language_name": "English", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/sv.json b/lang/sv.json index 9ab96aaf7..f2c25387d 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -561,13 +561,8 @@ "menu.preview.quadview": "Fyrvy", "menu.preview.fullview": "Fullvy", "menu.preview.stop_drag": "Stoppa bakgrundspositionering", - "menu.uv.copy": "Kopiera", - "menu.uv.paste": "Klistra in", "menu.uv.mapping": "UV kartläggning", "menu.uv.mapping.export": "Exportera", - "menu.uv.mapping.maximize": "Maximera", - "menu.uv.mapping.auto": "Auto UV", - "menu.uv.mapping.rel_auto": "Rel. Auto UV", "menu.uv.mapping.rotation": "Rotation", "menu.uv.mapping.mirror_x": "Spegla X", "menu.uv.mapping.mirror_y": "Spegla Y", @@ -825,5 +820,21 @@ "action.open_backup_folder": "Open Backup Folder", "action.open_backup_folder.desc": "Opens the Blockbench backup folder", "switches.mirror": "Mirror UV", - "language_name": "English" + "language_name": "English", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/lang/zh.json b/lang/zh.json index 9e90fd744..a1c8ae738 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -288,7 +288,7 @@ "category.blockbench": "Blockbench", "category.edit": "编辑", "category.transform": "转换", - "category.filter": "滤器", + "category.filter": "过滤", "category.view": "视图", "category.display": "显示设置", "category.textures": "贴图", @@ -514,7 +514,7 @@ "action.reload.desc": "重载 Blockbench,这将删除所有未保存的进度", "menu.file": "文件", "menu.edit": "编辑", - "menu.transform": "尺寸", + "menu.transform": "模型控制", "menu.filter": "插件目录", "menu.display": "物品显示", "menu.view": "视图", @@ -561,13 +561,8 @@ "menu.preview.quadview": "四视图", "menu.preview.fullview": "大视图", "menu.preview.stop_drag": "停止背景定位", - "menu.uv.copy": "复制", - "menu.uv.paste": "粘贴", "menu.uv.mapping": "UV贴图射映", "menu.uv.mapping.export": "导出", - "menu.uv.mapping.maximize": "最大化", - "menu.uv.mapping.auto": "自动UV", - "menu.uv.mapping.rel_auto": "自动相对 UV", "menu.uv.mapping.rotation": "旋转", "menu.uv.mapping.mirror_x": "镜像 X 轴", "menu.uv.mapping.mirror_y": "镜像 Y 轴", @@ -624,8 +619,8 @@ "display.reference.player": "玩家", "display.reference.zombie": "僵尸", "display.reference.armor_stand": "盔甲架", - "display.reference.baby_zombie": "幼年僵尸", - "display.reference.armor_stand_small": "小盔甲架", + "display.reference.baby_zombie": "小僵尸", + "display.reference.armor_stand_small": "小形盔甲架", "display.reference.monitor": "普通", "display.reference.bow": "弓", "display.reference.block": "方块", @@ -824,6 +819,22 @@ "action.color_picker.desc": "用于选择材质纹理上像素颜色的工具", "action.open_backup_folder": "打开备份文件夹", "action.open_backup_folder.desc": "打开Blockbench备份文件夹", - "switches.mirror": "Mirror UV", - "language_name": "中文" + "switches.mirror": "镜像 UV", + "language_name": "中文", + "message.plugin_reload": "Reloaded %0 local plugins", + "settings.brightness": "Brightness", + "settings.brightness.desc": "Brightness of the preview. Default is 50", + "menu.preview.perspective.reset": "Reset Camera", + "action.fill_mode": "Fill Mode", + "action.fill_mode.desc": "Mode of the fill tool", + "action.fill_mode.face": "Face", + "action.fill_mode.color": "Color", + "action.fill_mode.cube": "Cube", + "action.toggle_mirror_uv": "Mirror UV", + "action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.", + "action.toggle_uv_overlay": "Toggle UV Overlay", + "action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.", + "menu.texture.blank": "Apply to Untextured Faces", + "dialog.scale.select_overflow": "Select Overflow", + "dialog.create_texture.compress": "Compress Template" } \ No newline at end of file diff --git a/main.js b/main.js index e752fa65d..bb9792085 100644 --- a/main.js +++ b/main.js @@ -1,11 +1,14 @@ -const {app, BrowserWindow} = require('electron') +const {app, BrowserWindow, Menu} = require('electron') const path = require('path') const url = require('url') -let win +let orig_win; -function createWindow () { - win = new BrowserWindow({ +function createWindow() { + if (!app.requestSingleInstanceLock()) { + return; + } + let win = new BrowserWindow({ icon:'icon.ico', show: false, backgroundColor: '#21252b', @@ -16,11 +19,43 @@ function createWindow () { nodeIntegration: true } }) + if (!orig_win) orig_win = win; var index_path = path.join(__dirname, 'index.html') - /*if (__dirname.includes('xampp\\htdocs\\')) { - index_path = path.join(__dirname, 'index.php') - }*/ - win.setMenu(null); + if (process.platform === 'darwin') { + var template = [{ + label: 'File', + submenu: [{ + label: 'Quit', + accelerator: 'CmdOrCtrl+Q', + click: function() { + app.quit(); + } + }] + }, { + label: 'Edit', + submenu: [{ + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + selector: 'cut:' + }, { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + selector: 'copy:' + }, { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + selector: 'paste:' + }, { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + selector: 'selectAll:' + }] + }] + var osxMenu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(osxMenu) + } else { + win.setMenu(null); + } win.maximize() win.show() win.loadURL(url.format({ @@ -34,6 +69,11 @@ function createWindow () { //win.webContents.openDevTools() } +app.on('second-instance', function (event, argv, cwd) { + process.argv = argv + createWindow() +}) + app.commandLine.appendSwitch('ignore-gpu-blacklist') app.on('ready', createWindow) diff --git a/package.json b/package.json index eea1374e5..02dcbbf15 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Blockbench", "description": "Minecraft Block Model Editor", - "version": "2.3.2", + "version": "2.4.0", "license": "MIT", "author": { "name": "JannisX11",