From d766cd1678e99dd5a782d27eefe847952c7c96ab Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Thu, 30 Jan 2025 01:06:48 +0100 Subject: [PATCH 01/21] Fix issue where undoing a selection of a deleted element would create invalid state Make tool config API match type --- js/interface/dialog.js | 3 +++ js/undo.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/js/interface/dialog.js b/js/interface/dialog.js index ee3aa1fb..83fa19f2 100644 --- a/js/interface/dialog.js +++ b/js/interface/dialog.js @@ -687,6 +687,7 @@ window.ToolConfig = class ToolConfig extends Dialog { } save() { localStorage.setItem(`tool_config.${this.id}`, JSON.stringify(this.options)); + return this; } changeOptions(options) { for (let key in options) { @@ -696,6 +697,7 @@ window.ToolConfig = class ToolConfig extends Dialog { this.form.setValues(options); } this.save(); + return this; } close(button, event) { this.save(); @@ -712,6 +714,7 @@ window.ToolConfig = class ToolConfig extends Dialog { this.object.style.top = (anchor_position.top+anchor.offsetHeight) + 'px'; this.object.style.left = Math.clamp(anchor_position.left - 30, 0, window.innerWidth-this.object.clientWidth - (this.title ? 0 : 30)) + 'px'; } + return this; } build() { if (this.object) this.object.remove(); diff --git a/js/undo.js b/js/undo.js index 74fdd36c..ebf65ba9 100644 --- a/js/undo.js +++ b/js/undo.js @@ -854,7 +854,7 @@ UndoSystem.selectionSave = class { unselectAllElements(); if (this.elements) { - Outliner.selected.replace(this.elements.map(uuid => OutlinerNode.uuids[uuid])); + Outliner.selected.replace(this.elements.map(uuid => OutlinerNode.uuids[uuid]).filter(element => element instanceof OutlinerElement)); } if (this.groups) { for (let uuid of this.groups) { From a26bf1c17fe1f8abb2d6aedd3954e67b2db127de Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Thu, 30 Jan 2025 01:07:07 +0100 Subject: [PATCH 02/21] Add creaking skin preset --- js/io/formats/skin.js | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/js/io/formats/skin.js b/js/io/formats/skin.js index 1bf3edba..926491eb 100644 --- a/js/io/formats/skin.js +++ b/js/io/formats/skin.js @@ -2589,6 +2589,88 @@ skin_presets.cow = { ] }` }; +skin_presets.creaking = { + display_name: 'Creaking', + model: `{ + "name": "creaking", + "texturewidth": 64, + "textureheight": 64, + "eyes": [ + [6, 8, 3, 1], + [9, 10, 3, 1], + [7, 13, 3, 1] + ], + "bones": [ + { + "name": "root", + "pivot": [0, 0, 0] + }, + { + "name": "upperBody", + "parent": "root", + "pivot": [-1, 19, 0] + }, + { + "name": "head", + "parent": "upperBody", + "pivot": [-4, 30, 0], + "cubes": [ + {"origin": [-7, 30, -3], "size": [6, 10, 6], "uv": [0, 0]}, + {"origin": [-7, 40, -3], "size": [6, 3, 6], "uv": [28, 31]}, + {"origin": [-1, 29, 0], "size": [9, 14, 0], "uv": [12, 40]}, + {"origin": [-16, 30, 0], "size": [9, 14, 0], "uv": [34, 12]} + ] + }, + { + "name": "body", + "parent": "upperBody", + "pivot": [-1, 26, 1], + "cubes": [ + {"origin": [-1, 16, -2], "size": [6, 13, 5], "uv": [0, 16]}, + {"origin": [-7, 23, -2], "size": [6, 7, 5], "uv": [24, 0]} + ] + }, + { + "name": "rightArm", + "parent": "upperBody", + "pivot": [-8, 28.5, 1.5], + "cubes": [ + {"origin": [-10, 9, 0], "size": [3, 21, 3], "uv": [22, 13]}, + {"origin": [-10, 5, 0], "size": [3, 4, 3], "uv": [46, 0]} + ] + }, + { + "name": "leftArm", + "parent": "upperBody", + "pivot": [5, 28, 0.5], + "cubes": [ + {"origin": [5, 13, -1], "size": [3, 16, 3], "uv": [30, 40]}, + {"origin": [5, 29, -1], "size": [3, 4, 3], "uv": [52, 12]}, + {"origin": [5, 9, -1], "size": [3, 4, 3], "uv": [52, 19]} + ] + }, + { + "name": "leftLeg", + "parent": "root", + "pivot": [1.5, 16, 0.5], + "cubes": [ + {"origin": [0, 0, -1], "size": [3, 16, 3], "uv": [42, 40]}, + {"origin": [0, 0.3, -4], "size": [5, 0, 9], "uv": [45, 55]} + ] + }, + { + "name": "rightLeg", + "parent": "root", + "pivot": [-1, 17.5, 0.5], + "cubes": [ + {"origin": [-4, 0, -1], "size": [3, 19, 3], "uv": [0, 34]}, + {"origin": [-6, 0.3, -4], "size": [5, 0, 9], "uv": [45, 46]}, + {"origin": [-4, 19, -1], "size": [3, 3, 3], "uv": [12, 34]} + ] + } + ] + }` +}; skin_presets.creeper = { display_name: 'Creeper', model: `{ From 65bf903fefff8f3ad51a8b88258dd5c17b5db1d8 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Thu, 30 Jan 2025 20:11:19 +0100 Subject: [PATCH 03/21] Make minimum reference image size scale with zoom level --- js/preview/reference_images.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/js/preview/reference_images.js b/js/preview/reference_images.js index 1159fe1b..de6bd99c 100644 --- a/js/preview/reference_images.js +++ b/js/preview/reference_images.js @@ -349,14 +349,19 @@ class ReferenceImage { (e2.clientX - e1.clientX) * multiplier, (e2.clientY - e1.clientY) * multiplier, ]; - this.size[0] = Math.max(original_size[0] + offset[0] * sign_x, 48); + let zoom_level = this.getZoomLevel(); + let max_size = [ + 32 / zoom_level, + 24 / zoom_level + ]; + this.size[0] = Math.max(original_size[0] + offset[0] * sign_x, max_size[0]); this.position[0] = original_position[0] + offset[0] / 2, 0; if (!e2.ctrlOrCmd && !Pressing.overrides.ctrl) { offset[1] = sign_y * (this.size[0] / this.aspect_ratio - original_size[1]); } - this.size[1] = Math.max(original_size[1] + offset[1] * sign_y, 32); + this.size[1] = Math.max(original_size[1] + offset[1] * sign_y, max_size[1]); this.position[1] = original_position[1] + offset[1] / 2, 0; if (this.layer !== 'blueprint') { From b9b3a1afa384f422f6416862eb9e3bd8d4571219 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Thu, 30 Jan 2025 20:32:01 +0100 Subject: [PATCH 04/21] Fix material instances in face properties editor not applying to all cubes --- js/texturing/uv.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/js/texturing/uv.js b/js/texturing/uv.js index ff5d00e3..6615f8a1 100644 --- a/js/texturing/uv.js +++ b/js/texturing/uv.js @@ -4225,7 +4225,15 @@ Interface.definePanels(function() { startInputMaterialInstance(event) { Undo.initEdit({elements: Cube.selected, uv_only: true}) }, - endInputMaterialInstance(event) { + endInputMaterialInstance(event, fkey) { + let value = this.mappable_elements[0]?.faces[fkey]?.material_name; + if (typeof value == 'string') { + for (let element of this.mappable_elements) { + if (element.faces[fkey]) { + element.faces[fkey].material_name = value; + } + } + } Undo.finishEdit('Change material instances'); }, showInfoBox(title, text) { @@ -4329,7 +4337,7 @@ Interface.definePanels(function() { title="${tl('uv_editor.face_properties.material_instance')}" v-model="mappable_elements[0].faces[key].material_name" @focus="startInputMaterialInstance($event)" - @focusout="endInputMaterialInstance($event)" + @focusout="endInputMaterialInstance($event, key)" > From d207e00cae59de6e56818d5e5ff0d7a54eb30e4c Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Fri, 31 Jan 2025 21:41:28 +0100 Subject: [PATCH 05/21] Add V3_set support vor three vectors --- js/util/array_util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/util/array_util.js b/js/util/array_util.js index 80eb3200..2d1b81b0 100644 --- a/js/util/array_util.js +++ b/js/util/array_util.js @@ -111,6 +111,7 @@ Object.defineProperty(Array.prototype, "equals", {enumerable: false}); //Array Vector Array.prototype.V3_set = function(x, y, z) { if (x instanceof Array) return this.V3_set(...x); + if (x instanceof THREE.Vector3) return this.V3_set(x.x, x.y, x.z); if (y === undefined && z === undefined) z = y = x; this[0] = parseFloat(x)||0; this[1] = parseFloat(y)||0; From 7953f131125d41f4ef6e32a864b8edf9e2202120 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sat, 1 Feb 2025 14:52:20 +0100 Subject: [PATCH 06/21] Fix #2661 dragging multiple groups in outliner can causes recursive structure --- js/outliner/outliner.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js index 74e688c5..219cc467 100644 --- a/js/outliner/outliner.js +++ b/js/outliner/outliner.js @@ -854,7 +854,9 @@ function moveOutlinerSelectionTo(item, target, event, order) { Outliner.root.forEach(node => { if (node instanceof Group) { node.forEachChild(child => { - if (child.selected && !child.parent.selected) items.push(child); + if (child.selected && !child.parent.selected && !target.isChildOf?.(child)) { + items.push(child); + } }, null, true); } else if (node.selected) { items.push(node); From 03d640c56992aedd2232e6a2bd8976b4478170a0 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 2 Feb 2025 00:16:09 +0100 Subject: [PATCH 07/21] Fix per group texture not being saved in custom formats --- js/outliner/group.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/outliner/group.js b/js/outliner/group.js index 2d7544d8..ca340909 100644 --- a/js/outliner/group.js +++ b/js/outliner/group.js @@ -563,7 +563,7 @@ new Property(Group, 'string', 'bedrock_binding', {condition: {formats: ['bedrock new Property(Group, 'array', 'cem_animations', {condition: {formats: ['optifine_entity']}}); new Property(Group, 'boolean', 'cem_attach', {condition: {formats: ['optifine_entity']}}); new Property(Group, 'number', 'cem_scale', {condition: {formats: ['optifine_entity']}}); -new Property(Group, 'string', 'texture', {condition: {formats: ['optifine_entity']}}); +new Property(Group, 'string', 'texture', {condition: {features: ['per_group_texture']}}); //new Property(Group, 'vector2', 'texture_size', {condition: {formats: ['optifine_entity']}}); new Property(Group, 'vector', 'skin_original_origin', {condition: {formats: ['skin']}}); new Property(Group, 'number', 'color'); From e7dd11e6d91f9c1517720cabdd807ad0c90c648a Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Tue, 4 Feb 2025 21:26:19 +0100 Subject: [PATCH 08/21] Limit height of placeholder variable buttons list --- css/panels.css | 4 ++++ js/animations/animation_mode.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/css/panels.css b/css/panels.css index ee9c6c63..06ed0c27 100644 --- a/css/panels.css +++ b/css/panels.css @@ -1218,6 +1218,10 @@ } /* Placeholders */ + ul#placeholder_buttons { + max-height: 32%; + overflow: auto; + } #placeholder_buttons li { padding: 0px 8px; height: 30px; diff --git a/js/animations/animation_mode.js b/js/animations/animation_mode.js index 1d1813a4..45989bf3 100644 --- a/js/animations/animation_mode.js +++ b/js/animations/animation_mode.js @@ -1470,8 +1470,8 @@ Interface.definePanels(function() {
  • play_arrow - - + +
From 2c0d8f11b30191b49cbfc577a4d5be282d33ac2e Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Wed, 5 Feb 2025 00:30:36 +0100 Subject: [PATCH 09/21] Fix #2665 tiny numbers not rounded in minified bbmodel --- js/io/formats/bbmodel.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/js/io/formats/bbmodel.js b/js/io/formats/bbmodel.js index c85f8320..d1dfb318 100644 --- a/js/io/formats/bbmodel.js +++ b/js/io/formats/bbmodel.js @@ -332,15 +332,11 @@ var codec = new Codec('project', { if (options.raw) { return model; } else if (options.compressed) { - var json_string = JSON.stringify(model); + var json_string = compileJSON(model, {small: true}); var compressed = ''+LZUTF8.compress(json_string, {outputEncoding: 'StorageBinaryString'}); return compressed; } else { - if (Settings.get('minify_bbmodel') || options.minify) { - return JSON.stringify(model); - } else { - return compileJSON(model); - } + return compileJSON(model, {small: Settings.get('minify_bbmodel') || options.minify}); } }, parse(model, path) { From 71b0bd77ea7eec8623bd8354894c880222d85bdb Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Thu, 6 Feb 2025 22:59:07 +0100 Subject: [PATCH 10/21] Fix #1334 Mesh wireframes sometimes do not update --- js/outliner/mesh.js | 19 +++++++++++++++++++ js/preview/preview.js | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/js/outliner/mesh.js b/js/outliner/mesh.js index 7be1001b..f144959f 100644 --- a/js/outliner/mesh.js +++ b/js/outliner/mesh.js @@ -1136,6 +1136,10 @@ new NodePreviewController(Mesh, { Mesh.preview_controller.updatePixelGrid(element); + if (Project.view_mode == 'wireframe' && this.fixWireframe) { + this.fixWireframe(element); + } + this.dispatchEvent('update_geometry', {element}); }, updateFaces(element) { @@ -1447,5 +1451,20 @@ new NodePreviewController(Mesh, { mesh.add(box); this.dispatchEvent('update_painting_grid', {element}); + }, + fixWireframe(element) { + let geometry_orig = element.mesh.geometry; + if (!geometry_orig) return; + let geometry_clone = element.mesh.geometry.clone(); + element.mesh.geometry = geometry_clone; + geometry_orig.dispose(); } }) + +Blockbench.dispatchEvent('change_view_mode', ({view_mode}) => { + if (view_mode == 'wireframe') { + for (let mesh of Mesh.selected) { + Mesh.preview_controller.fixWireframe(mesh); + } + } +}); diff --git a/js/preview/preview.js b/js/preview/preview.js index 8d8ac2bd..ddc6b325 100644 --- a/js/preview/preview.js +++ b/js/preview/preview.js @@ -2209,6 +2209,7 @@ BARS.defineActions(function() { material: {name: true, icon: 'pages', condition: () => ((!Toolbox.selected.allowed_view_modes || Toolbox.selected.allowed_view_modes.includes('material')) && TextureGroup.all.find(tg => tg.is_material))}, }, onChange() { + let previous_view_mode = Project.view_mode; Project.view_mode = this.value; Canvas.updateViewMode(); if (Modes.id === 'animate') { @@ -2221,6 +2222,9 @@ BARS.defineActions(function() { if (icon_node) icon_node.replaceWith(icon); } }) + if (Project.view_mode != previous_view_mode) { + Blockbench.dispatchEvent('change_view_mode', {view_mode: Project.view_mode, previous_view_mode}); + } //Blockbench.showQuickMessage(tl('action.view_mode') + ': ' + tl('action.view_mode.' + this.value)); } }) From dd4fbd9bfd5f9fa79db708a1fc076b4b5611615d Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 9 Feb 2025 13:40:14 +0100 Subject: [PATCH 11/21] Ignroe box UV when determining format version for bedrock geo --- js/io/formats/bedrock.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/io/formats/bedrock.js b/js/io/formats/bedrock.js index 7265ed6e..41b586d8 100644 --- a/js/io/formats/bedrock.js +++ b/js/io/formats/bedrock.js @@ -1051,6 +1051,7 @@ function getFormatVersion() { } } for (let cube of Cube.all) { + if (cube.box_uv) continue; for (let fkey in cube.faces) { if (cube.faces[fkey].rotation) return '1.21.0'; } From c51a4201786958cc6505119a415242dfbd4e5b4e Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 9 Feb 2025 14:12:57 +0100 Subject: [PATCH 12/21] Make sure selection is valid after mirror modeling mesh --- js/modeling/mirror_modeling.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/modeling/mirror_modeling.js b/js/modeling/mirror_modeling.js index e3dec40f..8b5e8c3b 100644 --- a/js/modeling/mirror_modeling.js +++ b/js/modeling/mirror_modeling.js @@ -296,8 +296,14 @@ const MirrorModeling = { [new_face_key] = mesh.addFaces(new_face); } } - } + let selected_vertices = mesh.getSelectedVertices(true); + selected_vertices.replace(selected_vertices.filter(vkey => mesh.vertices[vkey])); + let selected_edges = mesh.getSelectedEdges(true); + selected_edges.replace(selected_edges.filter(edge => edge.allAre(vkey => mesh.vertices[vkey]))); + let selected_faces = mesh.getSelectedFaces(true); + selected_faces.replace(selected_faces.filter(fkey => mesh.faces[fkey])); + let {preview_controller} = mesh; preview_controller.updateGeometry(mesh); preview_controller.updateFaces(mesh); From 458a8bf7f2ed26a069c56dfccca9a2645f092743 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 9 Feb 2025 14:13:40 +0100 Subject: [PATCH 13/21] Change form linked ratio to update ratio when enabling link --- js/interface/form.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/interface/form.js b/js/interface/form.js index cb5317c9..48baa212 100644 --- a/js/interface/form.js +++ b/js/interface/form.js @@ -348,8 +348,9 @@ class InputForm extends EventSystem { linked_ratio_toggle.addEventListener('click', event => { data.linked_ratio = !data.linked_ratio; if (data.linked_ratio) { - updateInputs(vector_inputs[0]); - scope.updateValues(); + initial_value = vector_inputs.map(v => v.value); + // updateInputs(vector_inputs[0]); + // scope.updateValues(); } updateState(); }) From 65862e57470510ac2f2b836612d5360206d28a9d Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 9 Feb 2025 14:13:59 +0100 Subject: [PATCH 14/21] Fix graph editor limit value not applying --- js/animations/timeline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/animations/timeline.js b/js/animations/timeline.js index f07e4d0e..ece9903a 100644 --- a/js/animations/timeline.js +++ b/js/animations/timeline.js @@ -931,7 +931,7 @@ Interface.definePanels(() => { let padding = 16; let min_size = 2.4; - let unit_size = Math.clamp(max-min, min_size, 1e4); + let unit_size = Math.clamp(max-min, min_size, Timeline.graph_editor_limit); this.graph_size = (clientHeight - 2*padding) / unit_size; let blend = Math.clamp(1 - (max-min) / min_size, 0, 1) this.graph_offset = clientHeight - padding + (this.graph_size * (min - unit_size/2 * blend ) ); From e93ba411768b5f3d69becdb67f3b5f0b305e2a2e Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 9 Feb 2025 15:06:31 +0100 Subject: [PATCH 15/21] Fix face directions when extruding edge loop Fix amend edit not working when extruding edges --- js/modeling/mesh_editing.js | 9 ++++++--- js/undo.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/js/modeling/mesh_editing.js b/js/modeling/mesh_editing.js index b1f966ae..2585ea20 100644 --- a/js/modeling/mesh_editing.js +++ b/js/modeling/mesh_editing.js @@ -2208,9 +2208,9 @@ BARS.defineActions(function() { Mesh.selected.forEach(mesh => { let original_vertices = mesh.getSelectedVertices().slice(); let selected_edges = mesh.getSelectedEdges(true); + let selected_face_keys = mesh.getSelectedFaces(); 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(); } @@ -2379,13 +2379,13 @@ BARS.defineActions(function() { if (vertices.length == 2) delete mesh.faces[selected_face_keys[face_index]]; }) - // Create Face between extruded edges + // Create Faces for extruded edges let new_faces = []; selected_edges.forEach(edge => { let face, sorted_vertices; for (let fkey in mesh.faces) { let face2 = mesh.faces[fkey]; - let vertices = face2.getSortedVertices(); + let vertices = face2.vertices; if (vertices.includes(edge[0]) && vertices.includes(edge[1])) { face = face2; sorted_vertices = vertices; @@ -2400,6 +2400,9 @@ BARS.defineActions(function() { let new_face = new MeshFace(mesh, face).extend({ vertices: [a, b, c, d] }); + if (new_face.getAngleTo(face) > 90) { + new_face.invert(); + } let [face_key] = mesh.addFaces(new_face); new_face_keys.push(face_key); new_faces.push(new_face); diff --git a/js/undo.js b/js/undo.js index ebf65ba9..df1c42c3 100644 --- a/js/undo.js +++ b/js/undo.js @@ -803,7 +803,7 @@ UndoSystem.selectionSave = class { if (element instanceof Mesh) { this.geometry[element.uuid] = { faces: element.getSelectedFaces().slice(), - edges: element.getSelectedEdges().slice(), + edges: element.getSelectedEdges().map(edge => edge.slice()), vertices: element.getSelectedVertices().slice(), } } else if (element instanceof Cube && !element.box_uv) { From 43297d58cd3e39f762371d99e051f16e6dc8c8ae Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Fri, 14 Feb 2025 20:19:07 +0100 Subject: [PATCH 16/21] Fix edit sessions not snycing correctly --- js/undo.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/js/undo.js b/js/undo.js index df1c42c3..b4e541a5 100644 --- a/js/undo.js +++ b/js/undo.js @@ -192,8 +192,12 @@ class UndoSystem { this.index--; var entry = this.history[this.index]; - if (entry.before) entry.before.load(entry.post); - if (entry.selection_before) entry.selection_before.load(entry.selection_post); + if (entry.before) { + this.loadSave(entry.before, entry.post); + } + if (entry.selection_before instanceof UndoSystem.selectionSave) { + entry.selection_before.load(entry.selection_post); + } if (Project.EditSession && remote !== true) { Project.EditSession.sendAll('command', 'undo') } @@ -209,15 +213,19 @@ class UndoSystem { var entry = this.history[this.index] this.index++; - if (entry.post) entry.post.load(entry.before); - if (entry.selection_post) entry.selection_post.load(entry.selection_before); + if (entry.post) { + this.loadSave(entry.post, entry.before); + } + if (entry.selection_post instanceof UndoSystem.selectionSave) { + entry.selection_post.load(entry.selection_before); + } if (Project.EditSession && remote !== true) { Project.EditSession.sendAll('command', 'redo') } Blockbench.dispatchEvent('redo', {entry}) } remoteEdit(entry) { - this.loadSave(entry.post, entry.before, 'session') + this.loadSave(entry.post, entry.before, 'session'); if (entry.save_history !== false) { delete this.current_save; @@ -242,12 +250,23 @@ class UndoSystem { return false; } loadSave(save, reference, mode) { + if (save instanceof UndoSystem.save == false) { + save = new UndoSystem.save().fromJSON(save); + } save.load(reference, mode); } } UndoSystem.save = class { constructor(aspects) { - + if (aspects) { + this.fromState(aspects); + } + } + fromJSON(data) { + Object.assign(this, data); + return this; + } + fromState(aspects) { var scope = this; this.aspects = aspects; @@ -380,6 +399,7 @@ UndoSystem.save = class { } Blockbench.dispatchEvent('create_undo_save', {save: this, aspects}) + return this; } load(reference, mode) { let is_session = mode === 'session'; From 824000b509102dcdf60960537c17623755bea674 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Fri, 14 Feb 2025 20:22:39 +0100 Subject: [PATCH 17/21] Fix edit session issue with undo selections enabled --- js/undo.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/undo.js b/js/undo.js index b4e541a5..084f332c 100644 --- a/js/undo.js +++ b/js/undo.js @@ -77,7 +77,7 @@ class UndoSystem { return entry; } initSelection(aspects) { - if (!settings.undo_selections.value || Blockbench.hasFlag('loading_selection_save')) return; + if (!settings.undo_selections.value || Blockbench.hasFlag('loading_selection_save') || Project.EditSession) return; if (this.current_selection_save) return; this.current_selection_save = new UndoSystem.selectionSave(aspects); @@ -85,7 +85,7 @@ class UndoSystem { return this.current_selection_save; } finishSelection(message, aspects) { - if (!settings.undo_selections.value || Blockbench.hasFlag('loading_selection_save')) return; + if (!settings.undo_selections.value || Blockbench.hasFlag('loading_selection_save') || Project.EditSession) return; if (!this.current_selection_save) return; aspects = aspects || this.current_selection_save.aspects; @@ -198,8 +198,8 @@ class UndoSystem { if (entry.selection_before instanceof UndoSystem.selectionSave) { entry.selection_before.load(entry.selection_post); } - if (Project.EditSession && remote !== true) { - Project.EditSession.sendAll('command', 'undo') + if (Project.EditSession && remote !== true && entry.type != 'selection') { + Project.EditSession.sendAll('command', 'undo'); } Blockbench.dispatchEvent('undo', {entry}) } @@ -219,8 +219,8 @@ class UndoSystem { if (entry.selection_post instanceof UndoSystem.selectionSave) { entry.selection_post.load(entry.selection_before); } - if (Project.EditSession && remote !== true) { - Project.EditSession.sendAll('command', 'redo') + if (Project.EditSession && remote !== true && entry.type != 'selection') { + Project.EditSession.sendAll('command', 'redo'); } Blockbench.dispatchEvent('redo', {entry}) } From a21c60c589b097a9e41953ea9046d588ec46bd0b Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 16 Feb 2025 18:56:10 +0100 Subject: [PATCH 18/21] Fix #2644 selecting collections with invalid state can lock interface --- js/outliner/collections.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/outliner/collections.js b/js/outliner/collections.js index 3485a3ee..5e6be758 100644 --- a/js/outliner/collections.js +++ b/js/outliner/collections.js @@ -25,8 +25,7 @@ class Collection { if (Modes.animate && Animation.selected && !(event?.ctrlOrCmd || Pressing.overrides.ctrl)) { Timeline.animators.empty(); } - for (let uuid of this.children) { - let node = OutlinerNode.uuids[uuid]; + for (let node of this.getChildren()) { if (Modes.animate && Animation.selected) { if (node.constructor.animator) { let animator = Animation.selected.getBoneAnimator(node); From 41faa5173864d41b0f7621c078a4956ead40c067 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 16 Feb 2025 19:01:31 +0100 Subject: [PATCH 19/21] Fix texture not updating when moving cube in per group texture format --- js/outliner/outliner.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js index 219cc467..6ffd149d 100644 --- a/js/outliner/outliner.js +++ b/js/outliner/outliner.js @@ -886,6 +886,9 @@ function moveOutlinerSelectionTo(item, target, event, order) { } } else { item.preview_controller.updateTransform(item); + if (Format.per_group_texture && item.preview_controller.updateFaces) { + item.preview_controller.updateFaces(item); + } } } } From b7d2dba3e8bd77e6f329f7d4c988ea4976305bdd Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 16 Feb 2025 21:59:12 +0100 Subject: [PATCH 20/21] Add experimental fix for bbmodel file extensions on android --- js/file_system.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/file_system.js b/js/file_system.js index d6eb8bec..b57b39f1 100644 --- a/js/file_system.js +++ b/js/file_system.js @@ -310,7 +310,13 @@ Object.assign(Blockbench, { saveAs(blob, file_name) } else { - var blob = new Blob([options.content], {type: "text/plain;charset=utf-8"}); + let type = 'text/plain;charset=utf-8'; + if (file_name.endsWith('json')) { + type = 'application/json;charset=utf-8'; + } else if (file_name.endsWith('bbmodel')) { + type = 'model/vnd.blockbench.bbmodel'; + } + var blob = new Blob([options.content], {type}); saveAs(blob, file_name, {autoBOM: true}) } From 8b81761068f0d55e6429619cf989e9d2b99437bc Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 16 Feb 2025 23:35:32 +0100 Subject: [PATCH 21/21] Fix #1851 default actions cannot be removed from toolbars in some cases --- js/interface/actions.js | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/js/interface/actions.js b/js/interface/actions.js index 09c43d67..bf34084b 100644 --- a/js/interface/actions.js +++ b/js/interface/actions.js @@ -1534,17 +1534,21 @@ class Toolbar { } } } + /** + * Builds the toolbar from data + * @param {object} data Data used to build the toolbar + * @param {boolean} force If true, customization data will be ignored. Used when resetting toolbar + */ build(data, force) { - var scope = this; //Items this.children.length = 0; var items = data.children - if (!force && BARS.stored[scope.id] && typeof BARS.stored[scope.id] === 'object') { - items = BARS.stored[scope.id] + if (!force && BARS.stored[this.id] && typeof BARS.stored[this.id] === 'object') { + items = BARS.stored[this.id]; if (data.children) { - // Add new actions to existing toolbars + // Add new actions (newly added via bb update) to existing toolbars data.children.forEach((key, index) => { - if (typeof key == 'string' && key.length > 1 && !items.includes(key) && !Keybinds.stored[key] && BarItems[key]) { + if (typeof key == 'string' && key.length > 1 && !items.includes(key) && !Keybinds.stored[key] && BARS.stored._known?.includes(key) == false && BarItems[key]) { // Figure out best index based on item before. Otherwise use index from original array let prev_index = items.indexOf(data.children[index-1]); if (prev_index != -1) index = prev_index+1; @@ -1554,7 +1558,7 @@ class Toolbar { } } if (items && items instanceof Array) { - var content = $(scope.node).find('div.content') + var content = $(this.node).find('div.content') content.children().detach() for (var itemPosition = 0; itemPosition < items.length; itemPosition++) { let item = items[itemPosition]; @@ -1566,7 +1570,10 @@ class Toolbar { continue; } - if (typeof item == 'string') item = BarItems[item] + if (typeof item == 'string') { + BARS.stored._known?.safePush(item); + item = BarItems[item]; + } if (item) { item.pushToolbar(this); @@ -1581,8 +1588,8 @@ class Toolbar { } } } - $(scope.node).toggleClass('no_wrap', this.no_wrap) - $(scope.node).toggleClass('vertical', this.vertical) + $(this.node).toggleClass('no_wrap', this.no_wrap) + $(this.node).toggleClass('vertical', this.vertical) if (data.default_place) { this.toPlace(this.id) } @@ -1748,7 +1755,10 @@ class Toolbar { } }) BARS.stored[this.id] = arr; - if (arr.equals(this.default_children)) { + let identical_to_default = this.default_children.length == arr.length && this.default_children.allAre((item, i) => { + return arr[i] == item || (typeof arr[i] == 'string' && arr[i].startsWith(item)); + }) + if (identical_to_default) { delete BARS.stored[this.id]; } // Temporary fix @@ -1779,7 +1789,9 @@ Toolbar.prototype.menu = new Menu([ ]) const BARS = { - stored: {}, + stored: { + _known: [] + }, editing_bar: undefined, action_definers: [], condition: Condition, @@ -2145,6 +2157,9 @@ const BARS = { stored = JSON.parse(stored) if (typeof stored === 'object') { BARS.stored = stored; + if (!BARS.stored._known) { + BARS.stored._known = []; + } } } @@ -2266,9 +2281,6 @@ const BARS = { } }) } - Blockbench.onUpdateTo('4.4.0-beta.0', () => { - delete BARS.stored.brush; - }) Toolbars.brush = new Toolbar({ id: 'brush', no_wrap: true,