diff --git a/assets/missing.png b/assets/missing.png index 642f1e01..b8125955 100644 Binary files a/assets/missing.png and b/assets/missing.png differ diff --git a/css/dialogs.css b/css/dialogs.css index 96e8aa01..0fc6efca 100644 --- a/css/dialogs.css +++ b/css/dialogs.css @@ -1559,8 +1559,9 @@ overflow: hidden; max-width: unset; } - #plugin_browser_details { + .plugin_browser_tabbed_page { padding: 8px 24px; + overflow-y: auto; } #plugin_browser_details tr:nth-child(even) { background-color: var(--color-back); @@ -1569,10 +1570,6 @@ padding: 5px 2px; line-height: 20px; } - #plugin_browser_changelog { - padding: 4px 20px; - overflow-y: auto; - } #plugin_browser_changelog > li { padding: 7px 8px; } @@ -1608,12 +1605,6 @@ font-size: 20px; margin-right: 2px; } - #plugin_browser_page .settings_list { - padding: 4px 20px; - } - #plugin_browser_page .features_list { - padding: 4px 20px; - } li.plugin_feature_entry { display: flex; gap: 8px; @@ -2336,14 +2327,24 @@ align-items: center; cursor: pointer; padding: 2px; + position: relative; } .flipbook_frame:hover { - background-color: var(--color-hover); color: var(--color-light); + background-color: var(--color-back); + } + .flipbook_frame.viewing::before { + content: ""; + position: absolute; + left: 0; + top: 10px; + bottom: 10px; + border-top: 40px solid transparent; + border-bottom: 40px solid transparent; + border-left: 8px solid var(--color-accent); } .flipbook_frame.viewing { - background-color: var(--color-accent); - color: var(--color-accent_text); + background-color: var(--color-back); } .flipbook_frame.selected { background-color: var(--color-accent); diff --git a/css/setup.css b/css/setup.css index f92f0e24..79f0c393 100644 --- a/css/setup.css +++ b/css/setup.css @@ -738,15 +738,12 @@ display: inline-block; width: 20px; height: 24px; - margin-top: -28px; + margin-top: -29px; user-select: none; margin-left: -42px; overflow: hidden; text-align: center; } - div.nslide_arrow.na_right { - margin-left: 16px; - } div.nslide.editing { cursor: text; } diff --git a/js/animations/timeline.js b/js/animations/timeline.js index d5805e4e..fa9bebb9 100644 --- a/js/animations/timeline.js +++ b/js/animations/timeline.js @@ -85,6 +85,7 @@ const Timeline = { return }; + if (Timeline.selector.interval) clearInterval(Timeline.selector.interval); Timeline.selector.interval = setInterval(Timeline.selector.move, 1000/60); document.addEventListener('mouseup', Timeline.selector.end, false); @@ -195,8 +196,8 @@ const Timeline = { end(e) { e.stopPropagation(); document.removeEventListener('mousemove', Timeline.selector.move); - clearInterval(Timeline.selector.interval); document.removeEventListener('mouseup', Timeline.selector.end); + clearInterval(Timeline.selector.interval); if (!Timeline.selector.selecting) { if (settings.canvas_unselect.value) { diff --git a/js/api.js b/js/api.js index 07f8ce9d..e08f7fa5 100644 --- a/js/api.js +++ b/js/api.js @@ -209,14 +209,35 @@ const Blockbench = { showMessageBox(options = 0, cb) { return new MessageBox(options, cb).show(); }, - async textPrompt(title, value, callback, placeholder = null) { + /** + * + * @param {*} title + * @param {*} value + * @param {*} callback + * @param {object} options Options + * @param {string} options.info Info text + * @param {string} options.description Description for the text input + * @returns {Promise} Input value + */ + async textPrompt(title, value, callback, options = 0) { + if (typeof options == 'string') { + options = {placeholder: options}; + console.warn('textPrompt: 4th argument is expected to be a string'); + } let answer = await new Promise((resolve) => { + let form = { + text: {full_width: true, placeholder: options.placeholder, value, description: options.description} + }; + if (options.info) { + form.description = { + type: 'info', + text: tl(options.info) + } + } new Dialog({ id: 'text_input', title: title || 'dialog.input.title', - form: { - text: {full_width: true, placeholder, value} - }, + form, onConfirm({text}) { if (callback) callback(text); resolve(text); diff --git a/js/display_mode.js b/js/display_mode.js index 2f53a065..a4f4a5fd 100644 --- a/js/display_mode.js +++ b/js/display_mode.js @@ -298,6 +298,11 @@ class refModel { setDisplayArea(0, 0, -6, 90, 180, 0, 1, 1, 1); } break; + case 'block': + this.updateBasePosition = function() { + setDisplayArea(8, 4, 8, 0, 0, 0, 1, 1, 1) + } + break; case 'zombie': this.updateBasePosition = function() { if (display_slot === 'thirdperson_righthand') { diff --git a/js/interface/actions.js b/js/interface/actions.js index ad59a328..509f0985 100644 --- a/js/interface/actions.js +++ b/js/interface/actions.js @@ -853,15 +853,15 @@ class NumSlider extends Widget { '
navigate_next
' ) - var n = limitNumber(scope.node.clientWidth/2-24, 6, 1000) + var n = limitNumber(scope.node.clientWidth/2-22, 6, 1000) scope.jq_outer.find('.nslide_arrow.na_left').click(function(e) { scope.arrow(-1, e) - }).css('margin-left', (-n-24)+'px') + }).css('margin-left', (-n-22)+'px') scope.jq_outer.find('.nslide_arrow.na_right').click(function(e) { scope.arrow(1, e) - }).css('margin-left', n+'px') + }).css('margin-left', (n)+'px') }) .on('mouseleave', function() { scope.jq_outer.find('.nslide_arrow').remove() diff --git a/js/interface/interface.js b/js/interface/interface.js index 384a092c..b4b8c88c 100644 --- a/js/interface/interface.js +++ b/js/interface/interface.js @@ -1053,10 +1053,13 @@ BARS.defineActions(function() { panel.resetCustomLayout(); } + Prop.show_left_bar = true; + Prop.show_right_bar = true; + Blockbench.dispatchEvent('reset_layout', {}); - updateInterface(); updateSidebarOrder(); + resizeWindow(); } }) }) diff --git a/js/interface/menu_bar.js b/js/interface/menu_bar.js index 67fb48b7..7d46c73e 100644 --- a/js/interface/menu_bar.js +++ b/js/interface/menu_bar.js @@ -389,11 +389,12 @@ const MenuBar = { new BarMenu('timeline', Timeline.menu.structure, { name: 'panel.timeline', + icon: 'timeline', condition: {modes: ['animate'], method: () => !AnimationController.selected}, onOpen() { setActivePanel('timeline'); } - }, {icon: 'timeline'}) + }) new BarMenu('display', [ new MenuSeparator('copypaste'), @@ -455,6 +456,7 @@ const MenuBar = { 'pixel_grid', 'painting_grid', new MenuSeparator('references'), + 'bedrock_animation_mode', 'preview_scene', 'edit_reference_images', new MenuSeparator('interface'), @@ -507,7 +509,6 @@ const MenuBar = { singleButton: true }).show(); }}, - 'reset_layout', {name: 'menu.help.developer.reset_storage', icon: 'fas.fa-hdd', click: () => { factoryResetAndReload(); }}, @@ -526,6 +527,7 @@ const MenuBar = { }}, 'reload', ]}, + 'reset_layout', 'about_window' ], {icon: 'help'}) MenuBar.update(); diff --git a/js/interface/panels.js b/js/interface/panels.js index 50eb207d..fe054fad 100644 --- a/js/interface/panels.js +++ b/js/interface/panels.js @@ -23,6 +23,7 @@ class Panel extends EventSystem { this.toolbars = []; if (!Interface.data.panels[this.id]) Interface.data.panels[this.id] = {}; + if (!Interface.getModeData().panels[this.id]) Interface.getModeData().panels[this.id] = {}; this.position_data = Interface.getModeData().panels[this.id]; let defaultp = this.default_position = data.default_position || 0; if (defaultp && defaultp.slot) this.previous_slot = defaultp.slot; diff --git a/js/interface/settings.js b/js/interface/settings.js index f7708e7d..02c413b5 100644 --- a/js/interface/settings.js +++ b/js/interface/settings.js @@ -723,6 +723,7 @@ const Settings = { } Settings.dialog.show(); if (options.search_term) Settings.dialog.content_vue.search_term = options.search_term; + if (options.profile) Settings.dialog.content_vue.profile = options.profile; Settings.dialog.content_vue.$forceUpdate(); }, old: {} diff --git a/js/io/formats/obj.js b/js/io/formats/obj.js index c7614f65..a4dc1287 100644 --- a/js/io/formats/obj.js +++ b/js/io/formats/obj.js @@ -71,7 +71,7 @@ var codec = new Codec('obj', { output.push(`o ${element.name||'cube'}`) element.getGlobalVertexPositions().forEach((coords) => { - vertex.set(...coords.V3_subtract(8, 8, 8)).divideScalar(export_scale); + vertex.set(...coords).divideScalar(export_scale); output.push('v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z); nbVertex++; }) diff --git a/js/io/formats/skin.js b/js/io/formats/skin.js index 9e9fa2cb..46d9ce38 100644 --- a/js/io/formats/skin.js +++ b/js/io/formats/skin.js @@ -6619,7 +6619,82 @@ skin_presets.villager = { }; skin_presets.villager_v2 = { display_name: 'Villager (New)', - model: `{ + model_java: `{ + "name": "villager_v2", + "texturewidth": 64, + "textureheight": 64, + "bones": [ + { + "name": "body", + "pivot": [0, 0, 0], + "cubes": [ + {"name": "body", "origin": [-4, 12, -3], "size": [8, 12, 6], "uv": [16, 20]}, + {"name": "body", "origin": [-4, 4, -3], "size": [8, 20, 6], "uv": [0, 38], "inflate": 0.5} + ] + }, + { + "name": "head", + "parent": "body", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "head", "origin": [-4, 24, -4], "size": [8, 10, 8], "uv": [0, 0]} + ] + }, + { + "name": "helmet", + "parent": "head", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "helmet", "origin": [-4, 24, -4], "size": [8, 10, 8], "uv": [32, 0], "inflate": 0.5} + ] + }, + { + "name": "brim", + "parent": "head", + "pivot": [0, 24, 0], + "rotation": [-90, 0, 0], + "cubes": [ + {"name": "brim", "origin": [-8, 16, -6], "size": [16, 16, 1], "uv": [30, 47], "inflate": 0.1} + ] + }, + { + "name": "nose", + "parent": "head", + "pivot": [0, 26, 0], + "cubes": [ + {"name": "nose", "origin": [-1, 23, -6], "size": [2, 4, 2], "uv": [24, 0]} + ] + }, + { + "name": "arms", + "parent": "body", + "pivot": [0, 22, 0], + "rotation": [-45, 0, 0], + "cubes": [ + {"name": "arms", "origin": [-4, 16, -2], "size": [8, 4, 4], "uv": [40, 38]}, + {"name": "arms", "origin": [-8, 16, -2], "size": [4, 8, 4], "uv": [44, 22]}, + {"name": "arms", "origin": [4, 16, -2], "size": [4, 8, 4], "uv": [44, 22], "mirror": true} + ] + }, + { + "name": "RightLeg", + "parent": "body", + "pivot": [-2, 12, 0], + "cubes": [ + {"name": "leg0", "origin": [-4, 0, -2], "size": [4, 12, 4], "uv": [0, 22]} + ] + }, + { + "name": "LeftLeg", + "parent": "body", + "pivot": [2, 12, 0], + "cubes": [ + {"name": "leg1", "origin": [0, 0, -2], "size": [4, 12, 4], "uv": [0, 22], "mirror": true} + ] + } + ] + }`, + model_bedrock: `{ "name": "villager_v2", "texturewidth": 64, "textureheight": 64, @@ -7093,7 +7168,68 @@ skin_presets.wolf = { skin_presets.zombie = { display_name: 'Zombie', pose: true, - model: `{ + model_java: `{ + "name": "zombie", + "texturewidth": 64, + "textureheight": 64, + "eyes": [ + [9, 12, 2, 1], + [13, 12, 2, 1] + ], + "bones": [ + { + "name": "Body", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "Body", "origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]} + ] + }, + { + "name": "Head", + "pivot": [0, 24, 0], + "pose": [3, -10, 0], + "cubes": [ + {"name": "Head", "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}, + {"name": "Hat Layer", "visibility": false, "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5} + ] + }, + { + "name": "RightArm", + "pivot": [-5, 22, 0], + "pose": [-80, -5, 0], + "cubes": [ + {"name": "RightArm", "origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]} + ] + }, + { + "name": "LeftArm", + "pivot": [5, 22, 0], + "pose": [-75, 5, 0], + "mirror": true, + "cubes": [ + {"name": "LeftArm", "origin": [4, 12, -2], "size": [4, 12, 4], "uv": [40, 16]} + ] + }, + { + "name": "RightLeg", + "pivot": [-1.9, 12, 0], + "pose": [-25, 0, 5], + "cubes": [ + {"name": "RightLeg", "origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 16]} + ] + }, + { + "name": "LeftLeg", + "pivot": [1.9, 12, 0], + "pose": [20, 0, 0], + "mirror": true, + "cubes": [ + {"name": "LeftLeg", "origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [0, 16]} + ] + } + ] + }`, + model_bedrock: `{ "name": "zombie", "texturewidth": 64, "textureheight": 32, @@ -7217,7 +7353,81 @@ skin_presets.zombie_villager_1 = { }; skin_presets.zombie_villager_2 = { display_name: 'Zombie Villager (New)', - model: `{ + model_java: `{ + "name": "zombie_villager_2", + "texturewidth": 64, + "textureheight": 64, + "bones": [ + { + "name": "waist", + "pivot": [0, 12, 0] + }, + { + "name": "Body", + "parent": "waist", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "Body", "origin": [-4, 12, -3], "size": [8, 12, 6], "uv": [16, 20]}, + {"name": "Body", "origin": [-4, 4, -3], "size": [8, 20, 6], "uv": [0, 38], "inflate": 0.5} + ] + }, + { + "name": "Head", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "Head", "origin": [-4, 24, -4], "size": [8, 10, 8], "uv": [0, 0], "inflate": 0.25}, + {"name": "Head", "origin": [-1, 23, -6], "size": [2, 4, 2], "uv": [24, 0], "inflate": 0.25} + ] + }, + { + "name": "helmet", + "parent": "Head", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "Head Layer", "origin": [-4, 24, -4], "size": [8, 10, 8], "uv": [32, 0], "inflate": 0.5} + ] + }, + { + "name": "brim", + "parent": "Head", + "pivot": [0, 24, 0], + "cubes": [ + {"name": "brim", "origin": [-8, 16, -6], "size": [16, 16, 1], "uv": [30, 47], "inflate": 0.1} + ] + }, + { + "name": "RightArm", + "pivot": [-5, 22, 0], + "cubes": [ + {"name": "RightArm", "origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [44, 22]} + ] + }, + { + "name": "LeftArm", + "pivot": [5, 22, 0], + "mirror": true, + "cubes": [ + {"name": "LeftArm", "origin": [4, 12, -2], "size": [4, 12, 4], "uv": [44, 22]} + ] + }, + { + "name": "RightLeg", + "pivot": [-2, 12, 0], + "cubes": [ + {"name": "RightLeg", "origin": [-4, 0, -2], "size": [4, 12, 4], "uv": [0, 22]} + ] + }, + { + "name": "LeftLeg", + "pivot": [2, 12, 0], + "mirror": true, + "cubes": [ + {"name": "LeftLeg", "origin": [0, 0, -2], "size": [4, 12, 4], "uv": [0, 22]} + ] + } + ] + }`, + model_bedrock: `{ "name": "zombie_villager_2", "texturewidth": 64, "textureheight": 64, diff --git a/js/io/io.js b/js/io/io.js index 586bb0e9..b22a8a28 100644 --- a/js/io/io.js +++ b/js/io/io.js @@ -676,7 +676,7 @@ BARS.defineActions(function() { Blockbench.showQuickMessage('message.invalid_link') }) } - }, 'https://blckbn.ch/123abc') + }, {placeholder: 'https://blckbn.ch/123abc'}); } }) new Action('extrude_texture', { diff --git a/js/modeling/mesh_editing.js b/js/modeling/mesh_editing.js index fd169796..6180e5c2 100644 --- a/js/modeling/mesh_editing.js +++ b/js/modeling/mesh_editing.js @@ -2190,6 +2190,9 @@ BARS.defineActions(function() { let new_vertices; let new_face_keys = []; let selected_face_keys = mesh.getSelectedFaces(); + if (original_vertices.length && (BarItems.selection_mode.value == 'vertex' || BarItems.selection_mode.value == 'edge')) { + selected_face_keys.empty(); + } let selected_faces = selected_face_keys.map(fkey => mesh.faces[fkey]); let combined_direction; @@ -2987,6 +2990,11 @@ BARS.defineActions(function() { } }) mesh.addFaces(new_face); + + // Multiple loop cuts + if (cut_no+1 < cuts) { + splitFace(face, [center_vertex, side_vertices[0]], double_side, cut_no+1); + } } } diff --git a/js/modeling/mirror_modeling.js b/js/modeling/mirror_modeling.js index c39be948..c2b81293 100644 --- a/js/modeling/mirror_modeling.js +++ b/js/modeling/mirror_modeling.js @@ -38,7 +38,7 @@ const MirrorModeling = { function updateParent(child, child_b) { let parent = child.parent; let parent_b = child_b.parent; - if (parent instanceof Group == false || parent == parent_b) return; + if (parent instanceof Group == false || parent_b instanceof Group == false || parent == parent_b) return; MirrorModeling.updateGroupCounterpart(parent_b, parent); @@ -66,7 +66,7 @@ const MirrorModeling = { let parent_list = mirror_group_parent instanceof Group ? mirror_group_parent.children : Outliner.root; let match = parent_list.find(node => { if (node instanceof Group == false) return false; - if (node.name == mirror_group.name && node.rotation.equals(mirror_group.rotation) && node.origin.equals(mirror_group.origin)) { + if ((node.name == mirror_group.name || Condition(mirror_group.needsUniqueName)) && node.rotation.equals(mirror_group.rotation) && node.origin.equals(mirror_group.origin)) { return true; } }) diff --git a/js/outliner/cube.js b/js/outliner/cube.js index cc7719f3..d5725da7 100644 --- a/js/outliner/cube.js +++ b/js/outliner/cube.js @@ -618,9 +618,8 @@ class Cube extends OutlinerElement { return vertices.map(coords => { vec.set(...coords.V3_subtract(this.origin)); vec.applyMatrix4( this.mesh.matrixWorld ); - let arr = vec.toArray(); - arr.V3_add(8, 0, 8); - return arr; + vec.sub(scene.position) + return vec.toArray(); }) } setUVMode(box_uv) { diff --git a/js/outliner/mesh.js b/js/outliner/mesh.js index a26033ca..f8e2657b 100644 --- a/js/outliner/mesh.js +++ b/js/outliner/mesh.js @@ -13,7 +13,12 @@ class MeshFace extends Face { } extend(data) { super.extend(data); - this.vertices.forEach(key => { + this.vertices.forEachReverse((key, i) => { + if (typeof key != 'string' || !key.length) { + this.vertices.splice(i, 1); + delete this.uv[key]; + return; + } if (!this.uv[key]) this.uv[key] = [0, 0]; if (data.uv && data.uv[key] instanceof Array) { this.uv[key].replace(data.uv[key]); diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js index 418ede0d..5c408457 100644 --- a/js/outliner/outliner.js +++ b/js/outliner/outliner.js @@ -952,11 +952,15 @@ function renameOutliner(element) { if (name) { Undo.initEdit({elements: selected}) selected.forEach(function(obj, i) { - obj.name = name.replace(/%/g, obj.index).replace(/\$/g, i) + obj.name = name.replace(/%+/g, val => { + return (obj.getParentArray().indexOf(obj)+1).toDigitString(val.length) + }).replace(/\$+/g, val => { + return (i+1).toDigitString(val.length) + }); }) Undo.finishEdit('Rename') } - }) + }, {description: tl('message.rename_elements.numbering')}) } } } diff --git a/js/plugin_loader.js b/js/plugin_loader.js index 456421fb..54a2926b 100644 --- a/js/plugin_loader.js +++ b/js/plugin_loader.js @@ -1096,6 +1096,9 @@ BARS.defineActions(function() { Settings.saveLocalStorages(); }, 20); }, + openSettingInSettings(key, profile) { + Settings.openDialog({search_term: key, profile}); + }, settingContextMenu(setting, event) { new Menu([ { @@ -1399,7 +1402,7 @@ BARS.defineActions(function() {
- +
@@ -1452,7 +1455,7 @@ BARS.defineActions(function() {
Author
-