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() {
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 ) );
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})
}
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,
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/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();
})
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) {
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';
}
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: `{
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/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);
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);
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');
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/outliner/outliner.js b/js/outliner/outliner.js
index 74e688c5..6ffd149d 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);
@@ -884,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);
+ }
}
}
}
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));
}
})
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') {
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)"
>
diff --git a/js/undo.js b/js/undo.js
index 74fdd36c..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;
@@ -192,10 +192,14 @@ 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 (Project.EditSession && remote !== true) {
- Project.EditSession.sendAll('command', 'undo')
+ 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 && entry.type != 'selection') {
+ Project.EditSession.sendAll('command', 'undo');
}
Blockbench.dispatchEvent('undo', {entry})
}
@@ -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 (Project.EditSession && remote !== true) {
- Project.EditSession.sendAll('command', 'redo')
+ 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 && entry.type != 'selection') {
+ 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';
@@ -803,7 +823,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) {
@@ -854,7 +874,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) {
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;