Merge branch 'patch'

This commit is contained in:
JannisX11 2023-12-16 13:38:08 +01:00
commit 1083f1ada2
26 changed files with 367 additions and 157 deletions

View File

@ -1940,7 +1940,7 @@ span.controller_state_section_info {
--color-uv-background: transparent;
--uv-line-width: 1px;
}
#uv_frame.overlay_mode * {
#uv_frame.overlay_mode .uv_face {
pointer-events: none;
}
@ -2700,6 +2700,9 @@ span.controller_state_section_info {
margin: 2px 4px 0 4px;
width: calc(100% - 8px);
}
#center #panel_color .sp-top.sp-cf {
height: var(--height);
}
/* Skin Pose */
#skin_pose_selector {
display: flex;

BIN
icon_maskable.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -229,6 +229,7 @@ const Timeline = {
},
revealTime(time) {
let body = document.getElementById('timeline_body');
if (!body) return;
var scroll = body.scrollLeft;
var playhead = time * Timeline.vue._data.size + 8;
if (playhead < scroll || playhead > scroll + document.getElementById('timeline_vue').clientWidth - Timeline.vue._data.head_width) {

View File

@ -2136,8 +2136,13 @@ BARS.defineActions(function() {
let player_skin_setup = false;
function updateBase(mode) {
let root_has_binding = Outliner.root.find(g => g instanceof Group && g.bedrock_binding)
if (mode == 'attachable_first') {
Project.model_3d.position.set(-20, 21, 0);
if (root_has_binding) {
Project.model_3d.position.set(-20, 21, 0);
} else {
Project.model_3d.position.set(-8, 6, -18);
}
Project.model_3d.rotation.set(
Math.degToRad(-95),
Math.degToRad(45),
@ -2151,8 +2156,13 @@ BARS.defineActions(function() {
if (mode == 'attachable_third') {
let angle = Math.degToRad(15);
let arm_offset = Reusable.vec1.set(1, -31, 1).applyAxisAngle(Reusable.vec2.set(1, 0, 0), angle);
Project.model_3d.position.set(5, 22, 0).add(arm_offset);
if (root_has_binding) {
let arm_offset = Reusable.vec1.set(1, -31, 1).applyAxisAngle(Reusable.vec2.set(1, 0, 0), angle);
Project.model_3d.position.set(5, 22, 0).add(arm_offset);
} else {
let arm_offset = Reusable.vec1.set(1, -7, 1).applyAxisAngle(Reusable.vec2.set(1, 0, 0), angle);
Project.model_3d.position.set(5, 22, 0).add(arm_offset);
}
Project.model_3d.rotation.set(angle, 0, 0);
player_preview_model.enable()

View File

@ -66,3 +66,5 @@ Blockbench.DisplaySlot = DisplaySlot;
Blockbench.Reusable = Reusable;
Blockbench.Texture = Texture;
Blockbench.TextureLayer = TextureLayer;
Blockbench.SharedActions = SharedActions;

View File

@ -295,7 +295,7 @@ function buildForm(dialog) {
case 'save':
if (data.type == 'folder' && !isApp) break;
let input = $(`<input class="dark_bordered half" class="focusable_input" type="text" id="${form_id}" disabled>`);
let input = $(`<input class="dark_bordered half" class="focusable_input" type="text" id="${form_id}" style="pointer-events: none;" disabled>`);
input[0].value = settings.streamer_mode.value ? `[${tl('generic.redacted')}]` : data.value || '';
let input_wrapper = $('<div class="input_wrapper"></div>');
input_wrapper.append(input);
@ -588,7 +588,7 @@ window.Dialog = class Dialog {
}
return form_result;
}
setFormValues(values) {
setFormValues(values, update = true) {
for (let form_id in this.form) {
let data = this.form[form_id];
if (values[form_id] != undefined && typeof data == 'object' && data.bar) {
@ -642,7 +642,7 @@ window.Dialog = class Dialog {
}
}
}
this.updateFormValues();
if (update) this.updateFormValues();
}
getFormResult() {
let result = {}

View File

@ -746,6 +746,9 @@ Interface.CustomElements.SelectInput = function(id, data) {
let value = data.value || data.default || Object.keys(options)[0];
let select = Interface.createElement('bb-select', {id, class: 'half', value: value}, getNameFor(options[value]));
function setKey(key, options) {
if (!options) {
options = typeof data.options == 'function' ? data.options() : data.options;
}
value = key;
select.setAttribute('value', key);
select.textContent = getNameFor(options[key]);

View File

@ -847,6 +847,16 @@ addEventListeners(document, 'keydown mousedown', function(e) {
ReferenceImageMode.deactivate();
used = true;
}
} else if (Prop.active_panel == 'uv' && Modes.paint && Texture.selected && Texture.selected.selection.is_custom) {
if (Keybinds.extra.cancel.keybind.isTriggered(e)) {
SharedActions.run('unselect_all', e);
used = true;
}
} else if (Modes.paint && TextureLayer.selected && TextureLayer.selected.in_limbo) {
if (Keybinds.extra.confirm.keybind.isTriggered(e)) {
TextureLayer.selected.resolveLimbo(false);
used = true;
}
}
if (ActionControl.open) {
used = ActionControl.handleKeys(e) || used

View File

@ -758,7 +758,7 @@ function setupMobilePanelSelector() {
let panel_b = Panels[key];
if (panel_b.slot == 'bottom') {
$(panel_b.node).detach();
panel_b.slot = 'left_bar';
panel_b.position_data.slot = 'left_bar';
}
}
if (panel) {

View File

@ -21,7 +21,7 @@ const SharedActions = {
return {
delete() {
lsit.remove(handler);
list.remove(handler);
}
}
},

View File

@ -336,7 +336,7 @@ const CustomTheme = {
CustomTheme.data.colors[scope_key] = last_color;
field.spectrum('set', last_color);
},
beforeShow(a, b) {
beforeShow() {
last_color = CustomTheme.data.colors[scope_key];
field.spectrum('set', last_color);
}

View File

@ -598,10 +598,10 @@ var codec = new Codec('project', {
ani.uuid = guid();
}
if (base_ani.animators) {
for (let key in animators) {
for (let key in base_ani.animators) {
if (uuid_map[key]) {
animators[uuid_map[key]] = animators[key];
delete animators[key];
base_ani.animators[uuid_map[key]] = base_ani.animators[key];
delete base_ani.animators[key];
}
}
}

View File

@ -1137,7 +1137,7 @@ var codec = new Codec('bedrock', {
return;
}
}
if (data && index !== undefined) {
if (data) {
if (Group.all.find(group => group.bedrock_binding)) {
data.format_version = '1.16.0';

View File

@ -126,16 +126,50 @@ new ModelFormat('image', {
]
},
new() {
if (newProject(this)) {
TextureGenerator.addBitmapDialog(() => {
setTimeout(() => {
Undo.history.empty();
Undo.index = 0;
UVEditor.vue.centerView();
}, 1);
});
return true;
}
newProject(this);
let callback = () => {
setTimeout(() => {
Undo.history.empty();
Undo.index = 0;
UVEditor.vue.centerView();
}, 1);
};
let size_presets = {
'': 'Unset',
'16x16': '16 x 16',
'32x32': '32 x 32',
'64x64': '64 x 64',
'128x128': '128 x 128',
'256x256': '256 x 256',
'512x512': '512 x 512',
'1920x1080': '1920 x 1080',
};
let previous_size_preset = '';
let dialog = new Dialog({
id: 'add_bitmap',
title: tl('action.create_texture'),
buttons: ['dialog.confirm'],
form: {
name: {label: 'generic.name', value: 'texture'},
section2: "_",
size_preset:{label: 'dialog.create_texture.resolution', type: 'select', options: size_presets},
resolution: {label: 'dialog.create_texture.resolution', type: 'vector', dimensions: 2, value: [16, 16], min: 1, max: 2048},
color: {label: 'data.color', type: 'color', colorpicker: TextureGenerator.background_color, toggle_enabled: true, toggle_default: false},
},
onFormChange(result) {
console.log(result)
if (result.size_preset && result.size_preset != previous_size_preset) {
let size = result.size_preset.split('x').map(v => parseInt(v));
dialog.setFormValues({resolution: size}, false);
}
previous_size_preset = result.size_preset;
},
onConfirm: function(results) {
results.type = 'blank';
TextureGenerator.addBitmap(results, callback);
}
}).show();
},
onActivation() {
Interface.preview.classList.add('image_mode');

View File

@ -160,7 +160,7 @@ codec.export = null;
codec.rebuild = function(model_id, pose) {
let [preset_id, variant] = model_id.split('.');
let preset = skin_presets[preset_id];
let model = JSON.parse(preset.model || (variant == 'java' ? preset.model_java : preset.model_bedrock));
let model = JSON.parse(preset.model || (variant == 'java' ? preset.model_java : preset.model_bedrock) || preset[variant]);
codec.parse(model, undefined, undefined, pose && pose !== 'none');
if (pose && pose !== 'none') {
setTimeout(() => {
@ -245,6 +245,7 @@ function generateTemplate(width = 64, height = 64, cubes, name = 'name', eyes, l
}
const model_options = {};
let selected_model = '';
const skin_dialog = new Dialog({
title: tl('dialog.skin.title'),
id: 'skin',
@ -254,7 +255,7 @@ const skin_dialog = new Dialog({
type: 'select',
options: model_options
},
variant: {
game_edition: {
label: 'dialog.skin.variant',
type: 'inline_select',
default: 'java_edition',
@ -263,7 +264,17 @@ const skin_dialog = new Dialog({
bedrock_edition: 'Bedrock Edition',
},
condition(form) {
return !skin_presets[form.model].model;
return skin_presets[form.model].model_bedrock;
}
},
variant: {
label: 'dialog.skin.variant',
type: 'select',
options() {
return (selected_model && skin_presets[selected_model].variants) || {}
},
condition(form) {
return skin_presets[form.model].variants;
}
},
resolution: {label: 'dialog.create_texture.resolution', type: 'select', value: 16, options: {
@ -287,7 +298,19 @@ const skin_dialog = new Dialog({
pose: {type: 'checkbox', label: 'dialog.skin.pose', value: true, condition: form => (!!skin_presets[form.model].pose)},
layer_template: {type: 'checkbox', label: 'dialog.skin.layer_template', value: false}
},
draggable: true,
onFormChange(result) {
selected_model = result.model;
let variants = skin_presets[result.model].variants;
if (variants) {
for (let key in variants) {
if (!result.variant || !variants[result.variant]) {
result.variant = key;
skin_dialog.setFormValues({variant: key}, false);
break;
}
}
}
},
onConfirm(result) {
if (result.model == 'flat_texture') {
if (result.texture) {
@ -299,9 +322,22 @@ const skin_dialog = new Dialog({
} else {
if (newProject(format)) {
let preset = skin_presets[result.model];
let model = JSON.parse(preset.model || (result.variant == 'java_edition' ? preset.model_java : preset.model_bedrock));
let raw_model;
if (preset.model_bedrock) {
raw_model = result.game_edition == 'java_edition' ? preset.model_java : preset.model_bedrock;
} else if (preset.variants) {
raw_model = preset.variants[result.variant].model;
} else {
raw_model = preset.model;
}
let model = JSON.parse(raw_model);
codec.parse(model, result.resolution/16, result.texture, result.pose, result.layer_template);
Project.skin_model = result.model + '.' + (result.variant == 'java_edition' ? 'java' : 'bedrock');
Project.skin_model = result.model;
if (preset.model_bedrock) {
Project.skin_model += '.' + (result.game_edition == 'java_edition' ? 'java' : 'bedrock');
} else if (preset.variants) {
Project.skin_model += '.' + result.variant;
}
}
}
},
@ -1192,84 +1228,173 @@ skin_presets.banner = {
skin_presets.bat = {
display_name: 'Bat',
pose: true,
model: `{
"name": "bat",
"texturewidth": 64,
"textureheight": 64,
"bones": [
{
"name": "Head",
"pivot": [0, 24, 0],
"cubes": [
{"name": "Head", "origin": [-3, 21, -3], "size": [6, 6, 6], "uv": [0, 0]}
variants: {
new: {
name: 'New',
model: `{
"name": "bat_v2",
"texturewidth": 32,
"textureheight": 32,
"eyes": [
[1, 10, 2, 1],
[5, 10, 2, 1]
],
"bones": [
{
"name": "Head",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-2, 7, -1], "size": [4, 3, 2], "uv": [0, 7]}
]
},
{
"name": "rightEar",
"parent": "Head",
"pivot": [-1.5, 9, 0],
"cubes": [
{"origin": [-4, 8, 0], "size": [3, 5, 0], "uv": [1, 15]}
]
},
{
"name": "leftEar",
"parent": "Head",
"pivot": [1.1, 10, 0],
"cubes": [
{"origin": [1, 8, 0], "size": [3, 5, 0], "uv": [8, 15]}
]
},
{
"name": "body",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-1.5, 2, -1], "size": [3, 5, 2], "uv": [0, 0]}
]
},
{
"name": "feet",
"parent": "body",
"pivot": [0, 2, 0],
"cubes": [
{"origin": [-1.5, 0, 0], "size": [3, 2, 0], "uv": [16, 16]}
]
},
{
"name": "rightWing",
"parent": "body",
"pivot": [-1.5, 7, 0],
"cubes": [
{"origin": [-3.5, 2, 0], "size": [2, 7, 0], "uv": [12, 0]}
]
},
{
"name": "rightWingTip",
"parent": "rightWing",
"pivot": [-3.5, 7, 0],
"cubes": [
{"origin": [-9.5, 1, 0], "size": [6, 8, 0], "uv": [16, 0]}
]
},
{
"name": "leftWing",
"parent": "body",
"pivot": [1.5, 7, 0],
"cubes": [
{"origin": [1.5, 2, 0], "size": [2, 7, 0], "uv": [12, 7]}
]
},
{
"name": "leftWingTip",
"parent": "leftWing",
"pivot": [3.5, 7, 0],
"cubes": [
{"origin": [3.5, 1, 0], "size": [6, 8, 0], "uv": [16, 8]}
]
}
]
},
{
"name": "rightEar",
"parent": "Head",
"pivot": [0, 24, 0],
"cubes": [
{"name": "rightEar", "origin": [-4, 26, -2], "size": [3, 4, 1], "uv": [24, 0]}
}`
},
old: {
name: 'Old',
model: `{
"name": "bat",
"texturewidth": 64,
"textureheight": 64,
"bones": [
{
"name": "Head",
"pivot": [0, 24, 0],
"cubes": [
{"name": "Head", "origin": [-3, 21, -3], "size": [6, 6, 6], "uv": [0, 0]}
]
},
{
"name": "rightEar",
"parent": "Head",
"pivot": [0, 24, 0],
"cubes": [
{"name": "rightEar", "origin": [-4, 26, -2], "size": [3, 4, 1], "uv": [24, 0]}
]
},
{
"name": "leftEar",
"parent": "Head",
"pivot": [0, 24, 0],
"mirror": true,
"cubes": [
{"name": "leftEar", "origin": [1, 26, -2], "size": [3, 4, 1], "uv": [24, 0]}
]
},
{
"name": "body",
"pivot": [0, 24, 0],
"rotation": [30, 0, 0],
"cubes": [
{"name": "body", "origin": [-3, 8, -3], "size": [6, 12, 6], "uv": [0, 16]},
{"name": "body", "origin": [-5, -8, 0], "size": [10, 16, 1], "uv": [0, 34]}
]
},
{
"name": "rightWing",
"parent": "body",
"pivot": [0, 24, 0],
"pose": [0, -10, 0],
"cubes": [
{"name": "rightWing", "origin": [-12, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
]
},
{
"name": "rightWingTip",
"parent": "rightWing",
"pivot": [-12, 23, 1.5],
"pose": [0, -15, 0],
"cubes": [
{"name": "rightWingTip", "origin": [-20, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
]
},
{
"name": "leftWing",
"parent": "body",
"pivot": [0, 24, 0],
"pose": [0, 10, 0],
"mirror": true,
"cubes": [
{"name": "leftWing", "origin": [2, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
]
},
{
"name": "leftWingTip",
"parent": "leftWing",
"pivot": [12, 23, 1.5],
"pose": [0, 15, 0],
"mirror": true,
"cubes": [
{"name": "leftWingTip", "origin": [12, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
]
}
]
},
{
"name": "leftEar",
"parent": "Head",
"pivot": [0, 24, 0],
"mirror": true,
"cubes": [
{"name": "leftEar", "origin": [1, 26, -2], "size": [3, 4, 1], "uv": [24, 0]}
]
},
{
"name": "body",
"pivot": [0, 24, 0],
"rotation": [30, 0, 0],
"cubes": [
{"name": "body", "origin": [-3, 8, -3], "size": [6, 12, 6], "uv": [0, 16]},
{"name": "body", "origin": [-5, -8, 0], "size": [10, 16, 1], "uv": [0, 34]}
]
},
{
"name": "rightWing",
"parent": "body",
"pivot": [0, 24, 0],
"pose": [0, -10, 0],
"cubes": [
{"name": "rightWing", "origin": [-12, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
]
},
{
"name": "rightWingTip",
"parent": "rightWing",
"pivot": [-12, 23, 1.5],
"pose": [0, -15, 0],
"cubes": [
{"name": "rightWingTip", "origin": [-20, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
]
},
{
"name": "leftWing",
"parent": "body",
"pivot": [0, 24, 0],
"pose": [0, 10, 0],
"mirror": true,
"cubes": [
{"name": "leftWing", "origin": [2, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
]
},
{
"name": "leftWingTip",
"parent": "leftWing",
"pivot": [12, 23, 1.5],
"pose": [0, 15, 0],
"mirror": true,
"cubes": [
{"name": "leftWingTip", "origin": [12, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
]
}
]
}`
}`
},
}
};
skin_presets.bed = {
display_name: 'Bed',

View File

@ -943,6 +943,10 @@ BARS.defineActions(function() {
rotation: cube.rotation,
vertices: []
})
let rotation_euler = new THREE.Euler(0, 0, 0, 'ZYX').fromArray(cube.rotation.map(Math.degToRad));
rotation_euler.reorder('XYZ');
mesh.rotation.V3_set(rotation_euler.toArray().map(r => Math.roundTo(Math.radToDeg(r), 4)));
var adjustedFrom = cube.from.slice();
var adjustedTo = cube.to.slice();
adjustFromAndToForInflateAndStretch(adjustedFrom, adjustedTo, cube);

View File

@ -62,15 +62,17 @@ class Mode extends KeybindItem {
}
UVEditor.beforeMoving();
for (let id in Panels) {
let old_pos_data = Panels[id].position_data;
Panels[id].position_data = Interface.getModeData().panels[id];
if (!Panels[id].position_data) {
Panels[id].position_data = Interface.getModeData().panels[id] = JSON.parse(JSON.stringify(old_pos_data))
if (!Blockbench.isMobile) {
for (let id in Panels) {
let old_pos_data = Panels[id].position_data;
Panels[id].position_data = Interface.getModeData().panels[id];
if (!Panels[id].position_data) {
Panels[id].position_data = Interface.getModeData().panels[id] = JSON.parse(JSON.stringify(old_pos_data))
}
Panels[id].updateSlot();
}
Panels[id].updateSlot();
updateSidebarOrder();
}
updateSidebarOrder();
Canvas.updateRenderSides()
if (this.tool && BarItems[this.tool] && Condition(BarItems[this.tool])) {

View File

@ -311,11 +311,11 @@ const PredicateOverrideEditor = {
<select-input v-model="generator.type" :options="available_predicate_options" @input="updateGeneratorType()" />
<label>${tl('dialog.predicate_overrides.variants')}</label>
<numeric-input v-model.number="generator.variants" class="dark_bordered" min="1" step="1" style="width: 70px;" />
<numeric-input v-model.number="generator.variants" min="1" step="1" style="width: 70px;" />
<template v-if="generator.type == 'custom_model_data'">
<label>${tl('dialog.predicate_overrides.start_value')}</label>
<numeric-input v-model.number="generator.start_value" min="0" step="1" class="dark_bordered" style="width: 45px;" />
<numeric-input v-model.number="generator.start_value" min="0" step="1" style="width: 45px;" />
</template>
<label>${tl('dialog.predicate_overrides.model')}</label>

View File

@ -2085,7 +2085,7 @@ BARS.defineActions(function() {
new Toggle('toggle_all_grids', {
name: tl('settings.grids'),
description: tl('settings.grids.desc'),
icon: 'grid',
icon: 'grid_on',
category: 'view',
linked_setting: 'grids',
condition: () => !Modes.paint

View File

@ -820,6 +820,9 @@ const ReferenceImageMode = {
})
files.forEach(file => {
let ref = new ReferenceImage({source: file.content, name: file.name});
if (Format.image_editor) {
ref.layer = 'viewport';
}
if (save_mode == 'project') {
ref.addAsReference(true);
} else {

View File

@ -111,14 +111,20 @@ Interface.definePanels(() => {
})
],
onResize() {
Interface.Panels.color.vue.width = 0;
Panels.color.vue.width = 0;
Vue.nextTick(() => {
let disp_before = this.vue.$refs.square_picker.style.display;
this.vue.$refs.square_picker.style.display = 'none';
let max = this.isInSidebar()
? 1000
: Math.min(1000, (this.height - this.vue.$el.clientHeight - this.handle.clientHeight) * (this.vue.picker_type == 'box' ? 1.25 : 1));
Interface.Panels.color.vue.width = Math.clamp(this.width, 100, max);
Panels.color.vue.width = Math.clamp(this.width, 100, 1000);
if (!this.isInSidebar()) {
if (this.vue.picker_type == 'box') {
let height = this.height - this.vue.$el.clientHeight - this.handle.clientHeight - 6;
Panels.color.vue.picker_height = Math.clamp(height, 100, 1000);
} else {
let max = Math.min(1000, (this.height - this.vue.$el.clientHeight - this.handle.clientHeight) * (this.vue.picker_type == 'box' ? 1.25 : 1));
Interface.Panels.color.vue.width = Math.clamp(this.width, 100, max);
}
}
this.vue.$refs.square_picker.style.display = disp_before;
Vue.nextTick(() => {
$('#main_colorpicker').spectrum('reflow');
@ -128,6 +134,7 @@ Interface.definePanels(() => {
component: {
data: {
width: 100,
picker_height: 100,
open_tab: StateMemory.color_picker_tab || 'picker',
picker_type: Settings.get('color_wheel') ? 'wheel' : 'box',
main_color: '#ffffff',
@ -152,10 +159,6 @@ Interface.definePanels(() => {
history: (saved_colors && saved_colors.history instanceof Array) ? saved_colors.history : []
},
methods: {
togglePickerType() {
settings.color_wheel.set(!settings.color_wheel.value);
Panels.color.onResize();
},
colorPickerMenu(event) {
new Menu('color_picker_menu', [
{
@ -165,9 +168,11 @@ Interface.definePanels(() => {
children: [
{name: 'menu.color_picker.picker_type.square', icon: Settings.get('color_wheel') ? 'far.fa-circle' : 'far.fa-dot-circle', click: () => {
settings.color_wheel.set(false);
Panels.color.onResize();
}},
{name: 'menu.color_picker.picker_type.wheel', icon: Settings.get('color_wheel') ? 'far.fa-dot-circle' : 'far.fa-circle', click: () => {
settings.color_wheel.set(true);
Panels.color.onResize();
}}
]
},
@ -347,7 +352,7 @@ Interface.definePanels(() => {
</div>
<div v-show="open_tab == 'picker' || open_tab == 'both'" @wheel="onMouseWheel($event)">
<div v-show="picker_type == 'box'" ref="square_picker" :style="{maxWidth: width + 'px'}">
<div v-show="picker_type == 'box'" ref="square_picker" :style="{maxWidth: width + 'px', '--height': picker_height + 'px'}">
<input id="main_colorpicker">
</div>
<color-wheel v-if="picker_type == 'wheel' && width" :value="selected_color" @input="changeColor" :width="width" :height="width"></color-wheel>

View File

@ -11,9 +11,7 @@ const Painter = {
if (!options.no_undo && !options.no_undo_init) {
Undo.initEdit({textures: [texture], bitmap: true})
}
if (texture.mode === 'link') {
texture.convertToInternal();
}
if (!texture.internal) texture.convertToInternal();
let edit_name = options.no_undo ? null : (options.edit_name || 'Edit texture');
let {canvas, ctx, offset} = texture.getActiveCanvas();
@ -2036,6 +2034,7 @@ SharedActions.add('paste', {
Undo.initEdit({textures: [texture], bitmap: true});
if (!texture.layers_enabled) {
texture.flags.add('temporary_layers');
texture.activateLayers(false);
}
let offset = Clipbench.image ? [Math.clamp(Clipbench.image.x, 0, texture.width), Math.clamp(Clipbench.image.y, 0, texture.height)] : undefined;
@ -2093,17 +2092,19 @@ SharedActions.add('duplicate', {
copy_canvas.width = rect.width;
copy_canvas.height = rect.height;
selection.maskCanvas(copy_ctx, offset);
copy_ctx.drawImage(canvas, -rect.start_x, -rect.start_y);
selection.maskCanvas(copy_ctx, [rect.start_x, rect.start_y]);
copy_ctx.drawImage(canvas, -rect.start_x + offset[0], -rect.start_y + offset[1]);
canvas = copy_canvas;
offset = [rect.start_x, rect.start_y];
}
Undo.initEdit({textures: [texture], bitmap: true});
if (!texture.layers_enabled) {
texture.flags.add('temporary_layers');
texture.activateLayers(false);
}
let new_layer = new TextureLayer({name: layer.name + ' - copy', offset}, texture);
let new_layer = new TextureLayer({name: layer ? (layer.name + ' - copy') : 'selection', offset}, texture);
let image_data = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
new_layer.setSize(canvas.width, canvas.height);
new_layer.ctx.putImageData(image_data, 0, 0);
@ -2115,7 +2116,6 @@ SharedActions.add('duplicate', {
Undo.finishEdit('Duplicate texture selection');
updateInterfacePanels();
BARS.updateConditions();
}
})
SharedActions.add('delete', {
@ -2206,11 +2206,13 @@ BARS.defineActions(function() {
} else if (before) {
a = 0;
}
} else if (opacity < 1) {
} else if (opacity < 1 || blend_mode != BlendModes.default) {
let before = Painter.getAlphaMatrix(texture, px, py);
let new_val = (before||0);
if (before) {
if (a > before) {
a = Math.clamp(a, 0, (opacity - before) / (1 - before));
} else if (before) {
a = 0;
}
new_val = new_val + (1-new_val) * a;
if (new_val > before || before == undefined) Painter.setAlphaMatrix(texture, px, py, new_val);

View File

@ -145,7 +145,7 @@ const TextureGenerator = {
TextureGenerator.generateColorMapTemplate(options, makeTexture);
} else {
Undo.initEdit({textures: [], selected_texture: true})
TextureGenerator.generateBlank(options.resolution[1], options.resolution[0], options.color, makeTexture)
TextureGenerator.generateBlank(options.resolution[1], options.resolution[0], options.color, makeTexture);
}
},
generateBlank(height, width, color, cb) {
@ -158,8 +158,14 @@ const TextureGenerator = {
ctx.fillStyle = new tinycolor(color).toRgbString();
ctx.fillRect(0, 0, width, height);
}
cb(canvas.toDataURL())
let texture = cb(canvas.toDataURL());
texture.uv_width = width;
texture.uv_height = height;
if (Format.per_texture_uv_size) {
Project.texture_width = width;
Project.texture_height = height;
}
return texture;
},
//constructors
boxUVCubeTemplate: function(obj, min_size) {
@ -1717,10 +1723,10 @@ const TextureGenerator = {
changeUVResolution(width, height, texture) {
let factor_x = width / Project.getUVWidth(texture);
let factor_y = height / Project.getUVHeight(texture);
if (!Format.per_texture_uv_size) {
Project.texture_width = width;
Project.texture_height = height;
}
Project.texture_width = width;
Project.texture_height = height;
if (texture) {
texture.uv_width = width;
texture.uv_height = height;

View File

@ -1276,11 +1276,13 @@ class Texture {
// Nothing
} else if (formResult.fill === 'repeat' && Format.animated_textures && formResult.size[0] < formResult.size[1]) {
// Animated
} else if ((Format.single_texture || Texture.all.length == 1)) {
} else if (Format.single_texture || Texture.all.length == 1 || Format.per_texture_uv_size) {
if (Format.per_texture_uv_size) {
this.uv_width = Project.texture_width * (formResult.size[0] / old_width);
this.uv_height = Project.texture_height * (formResult.size[1] / old_height);
scope.uv_width = scope.uv_width * (formResult.size[0] / old_width);
scope.uv_height = scope.uv_height * (formResult.size[1] / old_height);
Project.texture_width = scope.uv_width;
Project.texture_height = scope.uv_height;
} else {
Undo.current_save.uv_mode = {
box_uv: Project.box_uv,
@ -1663,6 +1665,7 @@ class Texture {
}
}
updateChangesAfterEdit() {
if (!this.internal) this.convertToInternal();
if (this.layers_enabled) {
this.updateLayerChanges(true);
} else {
@ -1700,17 +1703,14 @@ class Texture {
}
return this;
}
edit(cb, options) {
var scope = this;
if (!options) options = false;
edit(cb, options = 0) {
if (cb) {
Painter.edit(scope, cb, options);
Painter.edit(this, cb, options);
} else if (scope.mode === 'link') {
} else if (this.mode === 'link') {
this.convertToInternal();
}
scope.saved = false;
this.saved = false;
}
}
Texture.prototype.menu = new Menu([

View File

@ -2102,7 +2102,7 @@ Interface.definePanels(function() {
: Math.floor(Math.clamp(UVEditor.panel.width - 8, 64, 1e5));
this.width = size;
if (Format.image_editor) {
this.height = Interface.center_screen.clientHeight - 38;
this.height = Interface.preview.clientHeight - 38;
if (Blockbench.isMobile) {
let panel = Interface.getBottomPanel();
if (panel) this.height -= panel.height;
@ -3762,7 +3762,7 @@ Interface.definePanels(function() {
<template v-for="element in getDisplayedUVElements()">
<template v-if="element.type == 'cube' && !element.box_uv">
<div class="cube_uv_face"
<div class="cube_uv_face uv_face"
v-for="(face, key) in element.faces" :key="element.uuid + ':' + key"
v-if="(face.getTexture() == texture || texture == 0) && face.texture !== null && (display_uv !== 'selected_faces' || selected_faces.includes(key))"
:title="face_names[key]"
@ -3799,7 +3799,7 @@ Interface.definePanels(function() {
</div>
</template>
<div v-else-if="element.type == 'cube'" class="cube_box_uv"
<div v-else-if="element.type == 'cube'" class="cube_box_uv uv_face"
:key="element.uuid"
@mousedown.prevent="dragFace(null, $event)"
@touchstart.prevent="dragFace(null, $event)"
@ -3814,7 +3814,7 @@ Interface.definePanels(function() {
</div>
<template v-if="element.type == 'mesh'">
<div class="mesh_uv_face"
<div class="mesh_uv_face uv_face"
v-for="(face, key) in filterMeshFaces(element.faces)" :key="element.uuid + ':' + key"
v-if="face.vertices.length > 2 && (display_uv !== 'selected_faces' || selected_faces.includes(key)) && face.getTexture() == texture"
:class="{selected: selected_faces.includes(key)}"

View File

@ -14,7 +14,7 @@
"sizes": "1024x1024"
},
{
"src": "icon_full.png",
"src": "icon_maskable.png",
"type": "image/png",
"sizes": "256x256",
"purpose": "maskable"