Merge branch 'patch'

This commit is contained in:
JannisX11 2025-02-19 21:55:24 +01:00
commit b22df10302
21 changed files with 227 additions and 52 deletions

View File

@ -1218,6 +1218,10 @@
}
/* Placeholders */
ul#placeholder_buttons {
max-height: 32%;
overflow: auto;
}
#placeholder_buttons li {
padding: 0px 8px;
height: 30px;

View File

@ -1470,8 +1470,8 @@ Interface.definePanels(function() {
<ul id="placeholder_buttons">
<li v-for="button in buttons" :key="button.id" :class="{placeholder_slider: button.type == 'slider'}" @click="button.type == 'impulse' && changeButtonValue(button, $event)" :buttontype="button.type">
<i v-if="button.type == 'impulse'" class="material-icons">play_arrow</i>
<input v-if="button.type == 'toggle'" type="checkbox" class="tab_target" :value="button.value == 1" @change="changeButtonValue(button, $event)" :id="'placeholder_button_'+button.id">
<numeric-input v-if="button.type == 'slider'" class="dark_bordered tab_target" :step="button.step" :min="button.min" :max="button.max" v-model="button.value" @input="changeButtonValue(button, $event)" />
<input v-if="button.type == 'toggle'" type="checkbox" :value="button.value == 1" @change="changeButtonValue(button, $event)" :id="'placeholder_button_'+button.id">
<numeric-input v-if="button.type == 'slider'" :step="button.step" :min="button.min" :max="button.max" v-model="button.value" @input="changeButtonValue(button, $event)" />
<label :for="'placeholder_button_'+button.id" @mousedown="slideButton(button, $event)" @touchstart="slideButton(button, $event)">{{ button.id }}</label>
</li>
</ul>

View File

@ -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 ) );

View File

@ -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})
}

View File

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

View File

@ -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();

View File

@ -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();
})

View File

@ -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 = '<lz>'+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) {

View File

@ -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';
}

View File

@ -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: `{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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');

View File

@ -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);
}
}
});

View File

@ -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);
}
}
}
}

View File

@ -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));
}
})

View File

@ -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') {

View File

@ -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)"
>
</template>
</li>

View File

@ -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) {

View File

@ -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;