mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-04-06 17:31:09 +08:00
Merge branch 'master' into next
This commit is contained in:
commit
a5c138b302
1
.github/ISSUE_TEMPLATE/issue.yaml
vendored
1
.github/ISSUE_TEMPLATE/issue.yaml
vendored
@ -29,6 +29,7 @@ body:
|
||||
id: format
|
||||
attributes:
|
||||
label: Model format in which the issue occurs
|
||||
description: Format means which option you choose when creating a new model. Not the file extension.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
@ -628,7 +628,7 @@
|
||||
}
|
||||
.settings_list li .setting_icon i {
|
||||
font-size: 26pt;
|
||||
width: 34px;
|
||||
max-width: unset;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.settings_list li:hover .setting_icon i {
|
||||
@ -2488,6 +2488,9 @@
|
||||
object-fit: contain;
|
||||
image-rendering: auto;
|
||||
}
|
||||
#tab_overview_grid > li.pixel_art img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
#tab_overview_grid > li label {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
@ -451,6 +451,7 @@
|
||||
cursor: default;
|
||||
float: left;
|
||||
color: var(--color-text);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tool i {
|
||||
display: block;
|
||||
|
@ -1367,7 +1367,7 @@
|
||||
width: 144px;
|
||||
flex-shrink: 0;
|
||||
background-color: var(--color-back);
|
||||
z-index: 4;
|
||||
z-index: 6;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
height: calc(100% + 1px);
|
||||
}
|
||||
|
@ -299,6 +299,7 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
i.fa_big {
|
||||
font-size: 18px;
|
||||
|
@ -21,6 +21,7 @@
|
||||
}
|
||||
#start_screen button {
|
||||
margin-right: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
#start_screen .recent_project {
|
||||
margin: 2px 0;
|
||||
|
@ -1049,6 +1049,7 @@
|
||||
z-index: 9;
|
||||
pointer-events: initial;
|
||||
cursor: move;
|
||||
clip-path: none !important;
|
||||
}
|
||||
.reference_image.selected, .reference_image:hover {
|
||||
outline: 1px solid var(--color-accent);
|
||||
|
@ -299,8 +299,7 @@
|
||||
<div id="right_bar" class="sidebar"></div>
|
||||
|
||||
<div id="center">
|
||||
<ul id="toast_notification_list">
|
||||
</ul>
|
||||
<ul id="toast_notification_list"></ul>
|
||||
|
||||
<div id="top_slot"></div>
|
||||
<div id="preview">
|
||||
|
@ -829,7 +829,7 @@ class Animation extends AnimationItem {
|
||||
icon: 'folder',
|
||||
condition(animation) {return isApp && Format.animation_files && animation.path && fs.existsSync(animation.path)},
|
||||
click(animation) {
|
||||
shell.showItemInFolder(animation.path);
|
||||
showItemInFolder(animation.path);
|
||||
}
|
||||
},
|
||||
'rename',
|
||||
@ -2139,180 +2139,4 @@ Interface.definePanels(function() {
|
||||
'save_all_animations',
|
||||
])
|
||||
})
|
||||
|
||||
new Panel('variable_placeholders', {
|
||||
icon: 'fas.fa-stream',
|
||||
condition: {modes: ['animate']},
|
||||
growable: true,
|
||||
resizable: true,
|
||||
default_position: {
|
||||
slot: 'left_bar',
|
||||
float_position: [0, 0],
|
||||
float_size: [300, 400],
|
||||
height: 400
|
||||
},
|
||||
component: {
|
||||
name: 'panel-placeholders',
|
||||
components: {VuePrismEditor},
|
||||
data() { return {
|
||||
text: '',
|
||||
buttons: []
|
||||
}},
|
||||
methods: {
|
||||
updateButtons() {
|
||||
let old_values = {};
|
||||
this.buttons.forEach(b => old_values[b.id] = b.value);
|
||||
this.buttons.empty();
|
||||
|
||||
let text = this.text//.toLowerCase();
|
||||
let matches = text.matchAll(/(slider|toggle|impulse)\(.+\)/gi);
|
||||
|
||||
for (let match of matches) {
|
||||
let [type, content] = match[0].substring(0, match[0].length - 1).split(/\(/);
|
||||
let [id, ...args] = content.split(/\(|, */);
|
||||
id = id.replace(/['"]/g, '');
|
||||
if (this.buttons.find(b => b.id == id)) return;
|
||||
|
||||
let variable = text.substring(0, match.index).match(/[\w.-]+ *= *$/);
|
||||
variable = variable ? variable[0].replace(/[ =]+/g, '').replace(/^v\./i, 'variable.').replace(/^q\./i, 'query.').replace(/^t\./i, 'temp.').replace(/^c\./i, 'context.') : undefined;
|
||||
|
||||
if (type == 'slider') {
|
||||
this.buttons.push({
|
||||
type,
|
||||
id,
|
||||
value: old_values[id] || 0,
|
||||
variable,
|
||||
step: isNaN(args[0]) ? undefined : parseFloat(args[0]),
|
||||
min: isNaN(args[1]) ? undefined : parseFloat(args[1]),
|
||||
max: isNaN(args[2]) ? undefined : parseFloat(args[2])
|
||||
})
|
||||
} else if (type == 'toggle') {
|
||||
this.buttons.push({
|
||||
type,
|
||||
id,
|
||||
value: old_values[id] || 0,
|
||||
variable,
|
||||
})
|
||||
} else if (type == 'impulse') {
|
||||
this.buttons.push({
|
||||
type,
|
||||
id,
|
||||
value: 0,
|
||||
variable,
|
||||
duration: parseFloat(args[0]) || 0.1
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
changeButtonValue(button, event) {
|
||||
if (button.type == 'toggle') {
|
||||
button.value = event.target.checked ? 1 : 0;
|
||||
}
|
||||
if (button.type == 'impulse') {
|
||||
button.value = 1;
|
||||
setTimeout(() => {
|
||||
button.value = 0;
|
||||
}, Math.clamp(button.duration, 0, 1) * 1000);
|
||||
}
|
||||
if (button.variable) {
|
||||
delete Animator.MolangParser.variables[button.variable];
|
||||
}
|
||||
Animator.preview();
|
||||
},
|
||||
slideButton(button, e1) {
|
||||
convertTouchEvent(e1);
|
||||
let last_event = e1;
|
||||
let started = false;
|
||||
let move_calls = 0;
|
||||
let last_val = 0;
|
||||
let total = 0;
|
||||
let clientX = e1.clientX;
|
||||
function start() {
|
||||
started = true;
|
||||
if (!e1.touches && last_event == e1 && e1.target.requestPointerLock) e1.target.requestPointerLock();
|
||||
}
|
||||
|
||||
function move(e2) {
|
||||
convertTouchEvent(e2);
|
||||
if (!started && Math.abs(e2.clientX - e1.clientX) > 5) {
|
||||
start()
|
||||
}
|
||||
if (started) {
|
||||
if (e1.touches) {
|
||||
clientX = e2.clientX;
|
||||
} else {
|
||||
let limit = move_calls <= 2 ? 1 : 100;
|
||||
clientX += Math.clamp(e2.movementX, -limit, limit);
|
||||
}
|
||||
let val = Math.round((clientX - e1.clientX) / 45);
|
||||
let difference = (val - last_val);
|
||||
if (!difference) return;
|
||||
if (button.step) {
|
||||
difference *= button.step;
|
||||
} else {
|
||||
difference *= canvasGridSize(e2.shiftKey || Pressing.overrides.shift, e2.ctrlOrCmd || Pressing.overrides.ctrl);
|
||||
}
|
||||
|
||||
|
||||
button.value = Math.clamp(Math.roundTo((parseFloat(button.value) || 0) + difference, 4), button.min, button.max);
|
||||
|
||||
last_val = val;
|
||||
last_event = e2;
|
||||
total += difference;
|
||||
move_calls++;
|
||||
|
||||
Animator.preview()
|
||||
Blockbench.setStatusBarText(trimFloatNumber(total));
|
||||
}
|
||||
}
|
||||
function off(e2) {
|
||||
if (document.exitPointerLock) document.exitPointerLock()
|
||||
removeEventListeners(document, 'mousemove touchmove', move);
|
||||
removeEventListeners(document, 'mouseup touchend', off);
|
||||
}
|
||||
addEventListeners(document, 'mouseup touchend', off);
|
||||
addEventListeners(document, 'mousemove touchmove', move);
|
||||
},
|
||||
autocomplete(text, position) {
|
||||
let test = MolangAutocomplete.VariablePlaceholdersContext.autocomplete(text, position);
|
||||
return test;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
text(text) {
|
||||
if (Project && typeof text == 'string') {
|
||||
Project.variable_placeholders = text;
|
||||
this.updateButtons();
|
||||
Project.variable_placeholder_buttons.replace(this.buttons);
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div style="flex-grow: 1; display: flex; flex-direction: column; overflow: visible;">
|
||||
|
||||
<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)" />
|
||||
<label :for="'placeholder_button_'+button.id" @mousedown="slideButton(button, $event)" @touchstart="slideButton(button, $event)">{{ button.id }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>${tl('panel.variable_placeholders.info')}</p>
|
||||
|
||||
<vue-prism-editor
|
||||
id="var_placeholder_area"
|
||||
class="molang_input tab_target capture_tab_key"
|
||||
v-model="text"
|
||||
language="molang"
|
||||
:autocomplete="autocomplete"
|
||||
:line-numbers="false"
|
||||
style="flex-grow: 1;"
|
||||
onkeyup="Animator.preview()"
|
||||
/>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -859,7 +859,7 @@ class AnimationController extends AnimationItem {
|
||||
icon: 'folder',
|
||||
condition(animation) {return isApp && Format.animation_files && animation.path && fs.existsSync(animation.path)},
|
||||
click(animation) {
|
||||
shell.showItemInFolder(animation.path);
|
||||
showItemInFolder(animation.path);
|
||||
}
|
||||
},
|
||||
'rename',
|
||||
|
@ -1431,6 +1431,7 @@ Interface.definePanels(function() {
|
||||
if (document.exitPointerLock) document.exitPointerLock()
|
||||
removeEventListeners(document, 'mousemove touchmove', move);
|
||||
removeEventListeners(document, 'mouseup touchend', off);
|
||||
Blockbench.setStatusBarText();
|
||||
}
|
||||
addEventListeners(document, 'mouseup touchend', off);
|
||||
addEventListeners(document, 'mousemove touchmove', move);
|
||||
|
@ -589,6 +589,7 @@ class Keyframe {
|
||||
function updateKeyframeValue(axis, value, data_point) {
|
||||
Timeline.selected.forEach(function(kf) {
|
||||
if (axis == 'uniform' && kf.channel == 'scale') kf.uniform = true;
|
||||
if (data_point && !kf.data_points[data_point]) return;
|
||||
kf.set(axis, value, data_point);
|
||||
})
|
||||
if (!['effect', 'locator', 'script'].includes(axis)) {
|
||||
@ -603,8 +604,8 @@ function updateKeyframeSelection() {
|
||||
}
|
||||
let has_expressions = false;
|
||||
if (kf.transform) {
|
||||
has_expressions = !!kf.data_points.find(point => {
|
||||
return !isStringNumber(point.x) || !isStringNumber(point.y) ||! isStringNumber(point.z);
|
||||
has_expressions = !!kf.data_points.find((point, i) => {
|
||||
return kf.getArray(i).find(v => typeof v == 'string');
|
||||
})
|
||||
}
|
||||
if (has_expressions != kf.has_expressions) {
|
||||
@ -1208,6 +1209,15 @@ BARS.defineActions(function() {
|
||||
time = (time + Animation.selected.length/2) % (Animation.selected.length + 0.001);
|
||||
}
|
||||
time = Timeline.snapTime(time);
|
||||
if (Math.epsilon(time, Animation.selected.length, 0.004) && formResult.offset && !occupied_times.includes(0)) {
|
||||
// Copy keyframe to start
|
||||
occupied_times.push(0);
|
||||
let new_kf = opposite_animator.createKeyframe(old_kf, 0, channel, false, false)
|
||||
if (new_kf) {
|
||||
new_kf.flip(0);
|
||||
new_keyframes.push(new_kf);
|
||||
}
|
||||
}
|
||||
if (occupied_times.includes(time)) return;
|
||||
occupied_times.push(time);
|
||||
let new_kf = opposite_animator.createKeyframe(old_kf, time, channel, false, false)
|
||||
@ -1462,6 +1472,17 @@ Interface.definePanels(function() {
|
||||
}
|
||||
}
|
||||
return channel;
|
||||
},
|
||||
firstKeyframe() {
|
||||
let data_point_length = 0;
|
||||
let keyframe;
|
||||
for (let kf of this.keyframes) {
|
||||
if (kf.data_points.length > data_point_length) {
|
||||
keyframe = kf;
|
||||
data_point_length = kf.data_points.length;
|
||||
}
|
||||
}
|
||||
return keyframe;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
@ -1474,7 +1495,7 @@ Interface.definePanels(function() {
|
||||
<label>{{ tl('panel.keyframe.type', [getKeyframeInfos()]) }}</label>
|
||||
<div
|
||||
class="in_list_button"
|
||||
v-if="keyframes[0].animator.channels[channel] && keyframes[0].data_points.length < keyframes[0].animator.channels[channel].max_data_points && keyframes[0].interpolation !== 'catmullrom'"
|
||||
v-if="firstKeyframe.animator.channels[channel] && firstKeyframe.data_points.length < firstKeyframe.animator.channels[channel].max_data_points && firstKeyframe.interpolation !== 'catmullrom'"
|
||||
v-on:click.stop="addDataPoint()"
|
||||
title="${ tl('panel.keyframe.change_effect_file') }"
|
||||
>
|
||||
@ -1482,19 +1503,19 @@ Interface.definePanels(function() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list" :style="{overflow: keyframes[0].data_points.length > 1 ? 'auto' : 'visible'}">
|
||||
<ul class="list" :style="{overflow: firstKeyframe.data_points.length > 1 ? 'auto' : 'visible'}">
|
||||
|
||||
<div v-for="(data_point, data_point_i) of keyframes[0].data_points" class="keyframe_data_point">
|
||||
<div v-for="(data_point, data_point_i) of firstKeyframe.data_points" class="keyframe_data_point">
|
||||
|
||||
<div class="keyframe_data_point_header" v-if="keyframes[0].data_points.length > 1">
|
||||
<label>{{ keyframes[0].transform ? tl('panel.keyframe.' + (data_point_i ? 'post' : 'pre')) : (data_point_i + 1) }}</label>
|
||||
<div class="keyframe_data_point_header" v-if="firstKeyframe.data_points.length > 1">
|
||||
<label>{{ firstKeyframe.transform ? tl('panel.keyframe.' + (data_point_i ? 'post' : 'pre')) : (data_point_i + 1) }}</label>
|
||||
<div class="flex_fill_line"></div>
|
||||
<div class="in_list_button" v-on:click.stop="removeDataPoint(data_point_i)" title="${ tl('panel.keyframe.remove_data_point') }">
|
||||
<i class="material-icons">clear</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="channel == 'scale' && keyframes[0].uniform && data_point.x_string == data_point.y_string && data_point.y_string == data_point.z_string">
|
||||
<template v-if="channel == 'scale' && firstKeyframe.uniform && data_point.x_string == data_point.y_string && data_point.y_string == data_point.z_string">
|
||||
<div
|
||||
class="bar flex"
|
||||
id="keyframe_bar_uniform_scale"
|
||||
@ -1541,7 +1562,7 @@ Interface.definePanels(function() {
|
||||
@focus="key == 'locator' && updateLocatorSuggestionList()"
|
||||
@input="updateInput(key, $event.target.value, data_point_i)"
|
||||
/>
|
||||
<div class="tool" v-if="key == 'effect'" :title="tl(channel == 'sound' ? 'timeline.select_sound_file' : 'timeline.select_particle_file')" @click="changeKeyframeFile(data_point, keyframes[0])">
|
||||
<div class="tool" v-if="key == 'effect'" :title="tl(channel == 'sound' ? 'timeline.select_sound_file' : 'timeline.select_particle_file')" @click="changeKeyframeFile(data_point, firstKeyframe)">
|
||||
<i class="material-icons">upload_file</i>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -305,6 +305,10 @@ function loadDataFromModelMemory() {
|
||||
Blockbench.dispatchEvent('load_from_recent_project_data', {data: project});
|
||||
}
|
||||
|
||||
function showItemInFolder(path) {
|
||||
ipcRenderer.send('show-item-in-folder', path);
|
||||
}
|
||||
|
||||
//Window Controls
|
||||
function updateWindowState(e, type) {
|
||||
let maximized = currentwindow.isMaximized();
|
||||
@ -733,11 +737,11 @@ ipcRenderer.on('update-available', (event, arg) => {
|
||||
})
|
||||
|
||||
} else {
|
||||
addStartScreenSection({
|
||||
addStartScreenSection('update_notification', {
|
||||
color: 'var(--color-back)',
|
||||
graphic: {type: 'icon', icon: 'update'},
|
||||
text: [
|
||||
{type: 'h2', text: tl('message.update_notification.title')},
|
||||
{type: 'h3', text: tl('message.update_notification.title')},
|
||||
{text: tl('message.update_notification.message')},
|
||||
{type: 'button', text: tl('generic.enable'), click: (e) => {
|
||||
settings.automatic_updates.set(true);
|
||||
|
@ -1941,7 +1941,7 @@ const BARS = {
|
||||
category: 'file',
|
||||
condition: () => {return isApp && (Project.save_path || Project.export_path)},
|
||||
click: function () {
|
||||
shell.showItemInFolder(Project.export_path || Project.save_path);
|
||||
showItemInFolder(Project.export_path || Project.save_path);
|
||||
}
|
||||
})
|
||||
new Action('reload', {
|
||||
|
@ -450,7 +450,8 @@ Interface.definePanels = function(callback) {
|
||||
//Misc
|
||||
function unselectInterface(event) {
|
||||
if (
|
||||
open_menu && $('.contextMenu').find(event.target).length === 0 &&
|
||||
open_menu &&
|
||||
!event.target.classList.contains('contextMenu') && $('.contextMenu').find(event.target).length === 0 &&
|
||||
$('.menu_bar_point.opened:hover').length === 0 &&
|
||||
!document.getElementById('mobile_menu_bar')?.contains(event.target)
|
||||
) {
|
||||
|
@ -470,10 +470,6 @@ const Settings = {
|
||||
}});
|
||||
new Setting('stretch_linked', {category: 'edit', value: true});
|
||||
new Setting('auto_keyframe', {category: 'edit', value: true});
|
||||
new Setting('bedrock_uv_rotations', {category: 'edit', value: false, name: 'Bedrock UV Rotations (Experimental)', description: 'Enable the experimental bedrock UV rotations feature.', onChange(value) {
|
||||
Formats.bedrock.uv_rotation = value;
|
||||
Formats.bedrock_block.uv_rotation = value;
|
||||
}});
|
||||
|
||||
//Grid
|
||||
new Setting('grids', {category: 'grid', value: true, onChange() {Canvas.buildGrid()}});
|
||||
|
@ -36,7 +36,7 @@ function addStartScreenSection(id, data) {
|
||||
data = id;
|
||||
id = '';
|
||||
}
|
||||
var obj = $(Interface.createElement('section', {id}))
|
||||
var obj = $(Interface.createElement('section', {class: 'start_screen_section', section_id: id}))
|
||||
if (typeof data.graphic === 'object') {
|
||||
var left = $('<div class="start_screen_left graphic"></div>')
|
||||
obj.append(left)
|
||||
@ -140,9 +140,9 @@ function addStartScreenSection(id, data) {
|
||||
if (data.last) {
|
||||
$('#start_screen > content').append(obj);
|
||||
} else if (data.insert_after) {
|
||||
$('#start_screen > content').find(`#${data.insert_after}`).after(obj);
|
||||
$('#start_screen > content').find(`.start_screen_section[section_id="${data.insert_after}"]`).after(obj);
|
||||
} else if (data.insert_before) {
|
||||
$('#start_screen > content').find(`#${data.insert_before}`).before(obj);
|
||||
$('#start_screen > content').find(`.start_screen_section[section_id="${data.insert_before}"]`).before(obj);
|
||||
} else {
|
||||
$('#start_screen > content').prepend(obj);
|
||||
}
|
||||
@ -261,7 +261,7 @@ onVueSetup(async function() {
|
||||
name: 'menu.texture.folder',
|
||||
icon: 'folder',
|
||||
click() {
|
||||
shell.showItemInFolder(recent_project.path)
|
||||
showItemInFolder(recent_project.path)
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -375,7 +375,7 @@ onVueSetup(async function() {
|
||||
template: `
|
||||
<div id="start_screen">
|
||||
<content>
|
||||
<section id="splash_screen" v-if="show_splash_screen">
|
||||
<section id="splash_screen" v-if="show_splash_screen" class="start_screen_section" section_id="splash_screen">
|
||||
<div class="splash_art_slideshow_image" :style="{backgroundImage: getBackground(slideshow[slideshow_selected].source)}">
|
||||
<p v-if="slideshow[slideshow_selected].description" class="start_screen_graphic_description" v-html="pureMarked(slideshow[slideshow_selected].description)"></p>
|
||||
</div>
|
||||
@ -387,7 +387,7 @@ onVueSetup(async function() {
|
||||
<i class="material-icons start_screen_close_button" @click="show_splash_screen = false">clear</i>
|
||||
</section>
|
||||
|
||||
<section id="start_files">
|
||||
<section id="start_files" class="start_screen_section" section_id="start_files">
|
||||
|
||||
<div class="start_screen_left" v-if="!(selected_format_id && mobile_layout)">
|
||||
<h2>${tl('mode.start.new')}</h2>
|
||||
@ -581,7 +581,7 @@ ModelLoader.loaders = {};
|
||||
let twitter_ad;
|
||||
if (Blockbench.startup_count < 20 && Blockbench.startup_count % 5 === 4) {
|
||||
twitter_ad = true;
|
||||
addStartScreenSection({
|
||||
addStartScreenSection('twitter_link', {
|
||||
color: '#1da1f2',
|
||||
text_color: '#ffffff',
|
||||
graphic: {type: 'icon', icon: 'fab.fa-twitter'},
|
||||
@ -594,7 +594,7 @@ ModelLoader.loaders = {};
|
||||
}
|
||||
//Discord
|
||||
if (Blockbench.startup_count < 6 && !twitter_ad) {
|
||||
addStartScreenSection({
|
||||
addStartScreenSection('discord_link', {
|
||||
color: '#5865F2',
|
||||
text_color: '#ffffff',
|
||||
graphic: {type: 'icon', icon: 'fab.fa-discord'},
|
||||
@ -666,7 +666,7 @@ ModelLoader.loaders = {};
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<section id="quick_setup">
|
||||
<section id="quick_setup" section_id="quick_setup" class="start_screen_section">
|
||||
<i class="material-icons start_screen_close_button" @click="close()">clear</i>
|
||||
<h2>${tl('mode.start.quick_setup')}</h2>
|
||||
|
||||
|
@ -193,6 +193,9 @@ const CustomTheme = {
|
||||
},
|
||||
getThemeThumbnailStyle(theme) {
|
||||
let style = {};
|
||||
for (let key in CustomTheme.defaultColors) {
|
||||
style[`--color-${key}`] = CustomTheme.defaultColors[key];
|
||||
}
|
||||
for (let key in theme.colors) {
|
||||
style[`--color-${key}`] = theme.colors[key];
|
||||
}
|
||||
@ -325,7 +328,8 @@ const CustomTheme = {
|
||||
<div v-if="open_category == 'css'">
|
||||
<h2 class="i_b">${tl('layout.css')}</h2>
|
||||
<div id="css_editor">
|
||||
<vue-prism-editor v-model="data.css" @change="customizeTheme(1, $event)" language="css" :line-numbers="true" />
|
||||
<p v-if="data.css && data.css.length > 65000">Hidden due to performance limitations of the built-in CSS editor</p>
|
||||
<vue-prism-editor v-else v-model="data.css" @change="customizeTheme(1, $event)" language="css" :line-numbers="true" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1343,7 +1343,7 @@ var entity_format = new ModelFormat({
|
||||
rotate_cubes: true,
|
||||
box_uv: true,
|
||||
optional_box_uv: true,
|
||||
uv_rotation: settings.bedrock_uv_rotations.value,
|
||||
uv_rotation: true,
|
||||
single_texture: true,
|
||||
bone_rig: true,
|
||||
centered_grid: true,
|
||||
@ -1382,7 +1382,7 @@ var block_format = new ModelFormat({
|
||||
rotate_cubes: true,
|
||||
box_uv: false,
|
||||
optional_box_uv: true,
|
||||
uv_rotation: settings.bedrock_uv_rotations.value,
|
||||
uv_rotation: true,
|
||||
single_texture_default: true,
|
||||
bone_rig: true,
|
||||
centered_grid: true,
|
||||
|
@ -229,8 +229,8 @@ function buildSkinnedMesh(root_group, scale) {
|
||||
let bone = new THREE.Bone();
|
||||
bone.name = group.name;
|
||||
bone.uuid = group.mesh.uuid;
|
||||
let parent_offset = THREE.fastWorldPosition(group.mesh.parent, Reusable.vec3);
|
||||
THREE.fastWorldPosition(group.mesh, bone.position).sub(parent_offset);
|
||||
bone.position.copy(group.mesh.position);
|
||||
bone.rotation.copy(group.mesh.rotation)
|
||||
if (group == root_group) {
|
||||
bone.position.set(0, 0, 0);
|
||||
}
|
||||
@ -334,7 +334,6 @@ var codec = new Codec('gltf', {
|
||||
Outliner.root.forEach(node => {
|
||||
if (node instanceof Group) {
|
||||
let armature = buildSkinnedMesh(node, options.scale);
|
||||
console.log(armature)
|
||||
gl_scene.add(armature);
|
||||
} else {
|
||||
gl_scene.add(node.mesh);
|
||||
|
@ -1838,6 +1838,252 @@ skin_presets.boat = {
|
||||
]
|
||||
}`
|
||||
};
|
||||
skin_presets.bogged = {
|
||||
display_name: 'Bogged',
|
||||
model: `{
|
||||
"name": "bogged",
|
||||
"texturewidth": 64,
|
||||
"textureheight": 32,
|
||||
"eyes": [
|
||||
[9, 12, 2, 1],
|
||||
[13, 12, 2, 1]
|
||||
],
|
||||
"bones": [
|
||||
{
|
||||
"name": "body",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "waist",
|
||||
"pivot": [0, 12, 0]
|
||||
},
|
||||
{
|
||||
"name": "head",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mushrooms",
|
||||
"parent": "head",
|
||||
"pivot": [3, 31.5, 3],
|
||||
"cubes": [
|
||||
{"origin": [-6, 31, -3], "size": [6, 4, 0], "pivot": [-3, 32.5, -3], "rotation": [0, -45, 0], "uv": [50, 22]},
|
||||
{"origin": [-6, 31, -3], "size": [6, 4, 0], "pivot": [-3, 32.5, -3], "rotation": [0, 45, 0], "uv": [50, 22]},
|
||||
{"origin": [0, 31, 3], "size": [6, 4, 0], "pivot": [3, 31.5, 3], "rotation": [0, 45, 0], "uv": [50, 16]},
|
||||
{"origin": [0, 31, 3], "size": [6, 4, 0], "pivot": [3, 31.5, 3], "rotation": [0, -45, 0], "uv": [50, 16]},
|
||||
{"origin": [-5, 25, 3], "size": [6, 5, 0], "pivot": [-2, 25, 3], "rotation": [-90, 0, 45], "uv": [50, 27]},
|
||||
{"origin": [-5, 25, 3], "size": [6, 5, 0], "pivot": [-2, 25, 3], "rotation": [-90, 0, 135], "uv": [50, 27]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hat",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.2, "uv": [32, 0], "layer": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightArm",
|
||||
"pivot": [-5, 22, 0],
|
||||
"cubes": [
|
||||
{"origin": [-6, 12, -1], "size": [2, 12, 2], "uv": [40, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightItem",
|
||||
"parent": "rightArm",
|
||||
"pivot": [-6, 15, 1]
|
||||
},
|
||||
{
|
||||
"name": "leftArm",
|
||||
"pivot": [5, 22, 0],
|
||||
"mirror": true,
|
||||
"cubes": [
|
||||
{"origin": [4, 12, -1], "size": [2, 12, 2], "uv": [40, 16], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftItem",
|
||||
"parent": "leftArm",
|
||||
"pivot": [6, 15, 1]
|
||||
},
|
||||
{
|
||||
"name": "rightLeg",
|
||||
"pivot": [-2, 12, 0],
|
||||
"cubes": [
|
||||
{"origin": [-3, 0, -1], "size": [2, 12, 2], "uv": [0, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftLeg",
|
||||
"pivot": [2, 12, 0],
|
||||
"mirror": true,
|
||||
"cubes": [
|
||||
{"origin": [1, 0, -1], "size": [2, 12, 2], "uv": [0, 16], "mirror": true}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
};
|
||||
skin_presets.bogged_layer = {
|
||||
display_name: 'Bogged/Stray Layer',
|
||||
model: `{
|
||||
"name": "bogged_layer",
|
||||
"texturewidth": 64,
|
||||
"textureheight": 32,
|
||||
"eyes": [
|
||||
[9, 12, 2, 1],
|
||||
[13, 12, 2, 1]
|
||||
],
|
||||
"bones": [
|
||||
{
|
||||
"name": "body",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftArm",
|
||||
"parent": "body",
|
||||
"pivot": [5, 22, 0],
|
||||
"mirror": true,
|
||||
"cubes": [
|
||||
{"origin": [4, 12, -2], "size": [4, 12, 4], "uv": [40, 16], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "head",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hat",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.5, "uv": [32, 0], "layer": true, , "visibility": false}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightArm",
|
||||
"pivot": [-5, 22, 0],
|
||||
"cubes": [
|
||||
{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "rightLeg",
|
||||
"pivot": [-1.9, 12, 0],
|
||||
"cubes": [
|
||||
{"origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "leftLeg",
|
||||
"pivot": [1.9, 12, 0],
|
||||
"mirror": true,
|
||||
"cubes": [
|
||||
{"origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [0, 16], "mirror": true}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
};
|
||||
skin_presets.breeze = {
|
||||
display_name: 'Breeze',
|
||||
model: `{
|
||||
"name": "breeze",
|
||||
"texturewidth": 32,
|
||||
"textureheight": 32,
|
||||
"eyes": [
|
||||
[7, 14, 3, 1],
|
||||
[14, 14, 3, 1],
|
||||
[6, 29, 5, 1],
|
||||
[15, 29, 5, 1]
|
||||
],
|
||||
"bones": [
|
||||
{
|
||||
"name": "body",
|
||||
"pivot": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"name": "rods",
|
||||
"parent": "body",
|
||||
"pivot": [0, 16, 0],
|
||||
"cubes": [
|
||||
{"origin": [-1, 11, -6], "size": [2, 8, 2], "pivot": [0, 19, -3], "rotation": [22.5, 0, 0], "uv": [0, 17]},
|
||||
{"origin": [-3.59808, 11, -1.5], "size": [2, 8, 2], "pivot": [-2.59808, 19, 1.5], "rotation": [-157.5, 60, 180], "uv": [0, 17]},
|
||||
{"origin": [1.59808, 11, -1.5], "size": [2, 8, 2], "pivot": [2.59808, 19, 1.5], "rotation": [-157.5, -60, 180], "uv": [0, 17]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "head",
|
||||
"parent": "body",
|
||||
"pivot": [0, 20, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 20, -4], "size": [8, 8, 8], "uv": [0, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyes",
|
||||
"parent": "head",
|
||||
"pivot": [0, 20, 0],
|
||||
"cubes": [
|
||||
{"origin": [-5, 22, -4.2], "size": [10, 3, 4], "uv": [4, 24], "layer": true}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
}
|
||||
skin_presets.breeze_tornado = {
|
||||
display_name: 'Breeze Tornado',
|
||||
model: `{
|
||||
"name": "breeze_wind",
|
||||
"texturewidth": 128,
|
||||
"textureheight": 128,
|
||||
"bones": [
|
||||
{
|
||||
"name": "tornado_body",
|
||||
"pivot": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"name": "tornado_bottom",
|
||||
"parent": "tornado_body",
|
||||
"pivot": [0, 0, 0],
|
||||
"cubes": [
|
||||
{"origin": [-2.5, 0, -2.5], "size": [5, 7, 5], "uv": [1, 83]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tornado_mid",
|
||||
"parent": "tornado_bottom",
|
||||
"pivot": [0, 7, 0],
|
||||
"cubes": [
|
||||
{"origin": [-2.5, 7, -2.5], "size": [5, 6, 5], "uv": [49, 71]},
|
||||
{"origin": [-4, 7, -4], "size": [8, 6, 8], "uv": [78, 32]},
|
||||
{"origin": [-6, 7, -6], "size": [12, 6, 12], "uv": [74, 28]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tornado_top",
|
||||
"parent": "tornado_mid",
|
||||
"pivot": [0, 13, 0],
|
||||
"cubes": [
|
||||
{"origin": [-2.5, 13, -2.5], "size": [5, 8, 5], "uv": [105, 57]},
|
||||
{"origin": [-6, 13, -6], "size": [12, 8, 12], "uv": [6, 6]},
|
||||
{"origin": [-9, 13, -9], "size": [18, 8, 18], "uv": [0, 0]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
}
|
||||
skin_presets.camel = {
|
||||
display_name: 'Camel',
|
||||
model: `{
|
||||
@ -5439,7 +5685,7 @@ skin_presets.silverfish = {
|
||||
}`
|
||||
};
|
||||
skin_presets.skeleton = {
|
||||
display_name: 'Skeleton',
|
||||
display_name: 'Skeleton/Stray',
|
||||
model: `{
|
||||
"name": "skeleton",
|
||||
"texturewidth": 64,
|
||||
|
@ -147,7 +147,7 @@ async function loadImages(files, event) {
|
||||
let new_textures = [];
|
||||
Undo.initEdit({textures: new_textures});
|
||||
files.forEach(function(f, i) {
|
||||
let tex = new Texture().fromFile(f).add().fillParticle();
|
||||
let tex = new Texture().fromFile(f).add(false, true).fillParticle();
|
||||
new_textures.push(tex);
|
||||
if (Format.image_editor && i == 0) {
|
||||
tex.select();
|
||||
|
@ -1232,6 +1232,9 @@ BARS.defineActions(function() {
|
||||
select(project) {
|
||||
Dialog.open.confirm();
|
||||
project.select();
|
||||
},
|
||||
isPixelArt(project) {
|
||||
return project.format.image_editor && project.textures[0]?.height < 190;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -1249,7 +1252,7 @@ BARS.defineActions(function() {
|
||||
<search-bar id="tab_overview_search_bar" v-model="search_term"></search-bar>
|
||||
</div>
|
||||
<ul id="tab_overview_grid">
|
||||
<li v-for="project in filtered_projects" @mousedown="select(project)">
|
||||
<li v-for="project in filtered_projects" @mousedown="select(project)" :class="{pixel_art: isPixelArt(project)}">
|
||||
<img :src="project.thumbnail" :style="{visibility: project.thumbnail ? 'unset' : 'hidden'}">
|
||||
{{ project.name }}
|
||||
</li>
|
||||
|
13
js/misc.js
13
js/misc.js
@ -266,12 +266,12 @@ const AutoBackup = {
|
||||
let has_backups = await AutoBackup.hasBackups();
|
||||
if (has_backups && (!isApp || !currentwindow.webContents.second_instance)) {
|
||||
|
||||
let section = addStartScreenSection({
|
||||
let section = addStartScreenSection('recover_backup', {
|
||||
color: 'var(--color-back)',
|
||||
graphic: {type: 'icon', icon: 'fa-archive'},
|
||||
insert_before: 'start_files',
|
||||
text: [
|
||||
{type: 'h2', text: tl('message.recover_backup.title')},
|
||||
{type: 'h3', text: tl('message.recover_backup.title')},
|
||||
{text: tl('message.recover_backup.message')},
|
||||
{type: 'button', text: tl('message.recover_backup.recover'), click: (e) => {
|
||||
AutoBackup.recoverAllBackups().then(() => {
|
||||
@ -438,6 +438,15 @@ function factoryResetAndReload() {
|
||||
}
|
||||
}
|
||||
|
||||
function benchmarkCode(id, iterations, code) {
|
||||
if (!iterations) iterations = 1000;
|
||||
console.time(id);
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
code();
|
||||
}
|
||||
console.timeEnd(id);
|
||||
}
|
||||
|
||||
const documentReady = new Promise((resolve, reject) => {
|
||||
$(document).ready(function() {
|
||||
resolve()
|
||||
|
@ -1013,7 +1013,11 @@ async function autoFixMeshEdit() {
|
||||
return true;
|
||||
})
|
||||
let off_corners = edges.find(edge => !edge.includes(concave_vkey))
|
||||
if (!off_corners) return;
|
||||
if (!off_corners) {
|
||||
// not sure if this always works, but its only required in special cases (if the quad edge that should be split is already connected to another face).
|
||||
let concave_index = sorted_vertices.indexOf(concave_vkey);
|
||||
off_corners = (concave_index%2) ? [sorted_vertices[1], sorted_vertices[3]] : [sorted_vertices[0], sorted_vertices[2]];
|
||||
}
|
||||
|
||||
let new_face = new MeshFace(mesh, face);
|
||||
new_face.vertices.remove(off_corners[0]);
|
||||
|
@ -252,6 +252,18 @@ class Cube extends OutlinerElement {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
selectLow(...args) {
|
||||
let was_selected = this.selected;
|
||||
super.selectLow(...args);
|
||||
if (!was_selected && Cube.selected[0]) {
|
||||
let other_selected_faces = UVEditor.selected_faces.slice();
|
||||
let own_selected_faces = UVEditor.getSelectedFaces(this, true);
|
||||
if (other_selected_faces?.length && !own_selected_faces?.length) {
|
||||
own_selected_faces.replace(other_selected_faces);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
size(axis, floored) {
|
||||
var scope = this;
|
||||
let epsilon = 0.0000001;
|
||||
@ -684,55 +696,56 @@ class Cube extends OutlinerElement {
|
||||
if (scope.autouv === 2) {
|
||||
//Relative UV
|
||||
var all_faces = ['north', 'south', 'west', 'east', 'up', 'down']
|
||||
let offset = Format.centered_grid ? 8 : 0;
|
||||
all_faces.forEach(function(side) {
|
||||
var uv = scope.faces[side].uv.slice()
|
||||
switch (side) {
|
||||
case 'north':
|
||||
uv = [
|
||||
pw - scope.to[0],
|
||||
pw - (scope.to[0]+offset),
|
||||
ph - scope.to[1],
|
||||
pw - scope.from[0],
|
||||
pw - (scope.from[0]+offset),
|
||||
ph - scope.from[1],
|
||||
];
|
||||
break;
|
||||
case 'south':
|
||||
uv = [
|
||||
scope.from[0],
|
||||
(scope.from[0]+offset),
|
||||
ph - scope.to[1],
|
||||
scope.to[0],
|
||||
(scope.to[0]+offset),
|
||||
ph - scope.from[1],
|
||||
];
|
||||
break;
|
||||
case 'west':
|
||||
uv = [
|
||||
scope.from[2],
|
||||
(scope.from[2]+offset),
|
||||
ph - scope.to[1],
|
||||
scope.to[2],
|
||||
(scope.to[2]+offset),
|
||||
ph - scope.from[1],
|
||||
];
|
||||
break;
|
||||
case 'east':
|
||||
uv = [
|
||||
pw - scope.to[2],
|
||||
pw - (scope.to[2]+offset),
|
||||
ph - scope.to[1],
|
||||
pw - scope.from[2],
|
||||
pw - (scope.from[2]+offset),
|
||||
ph - scope.from[1],
|
||||
];
|
||||
break;
|
||||
case 'up':
|
||||
uv = [
|
||||
scope.from[0],
|
||||
scope.from[2],
|
||||
scope.to[0],
|
||||
scope.to[2],
|
||||
(scope.from[0]+offset),
|
||||
(scope.from[2]+offset),
|
||||
(scope.to[0]+offset),
|
||||
(scope.to[2]+offset),
|
||||
];
|
||||
break;
|
||||
case 'down':
|
||||
uv = [
|
||||
scope.from[0],
|
||||
ph - scope.to[2],
|
||||
scope.to[0],
|
||||
ph - scope.from[2],
|
||||
(scope.from[0]+offset),
|
||||
ph - (scope.to[2]+offset),
|
||||
(scope.to[0]+offset),
|
||||
ph - (scope.from[2]+offset),
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
@ -64,15 +64,15 @@ class Group extends OutlinerNode {
|
||||
Canvas.updateAllBones([this]);
|
||||
return this;
|
||||
}
|
||||
select(event, isOutlinerClick) {
|
||||
select(event, is_outliner_click) {
|
||||
var scope = this;
|
||||
if (Blockbench.hasFlag('renaming') || this.locked) return this;
|
||||
if (!event) event = true
|
||||
if (isOutlinerClick && event.pointerType == 'touch') return;
|
||||
var allSelected = Group.selected === this && selected.length && this.matchesSelection()
|
||||
var allSelected = Group.selected === this && selected.length && this.matchesSelection();
|
||||
let previous_first_selected = Project.selected_elements[0];
|
||||
|
||||
//Clear Old Group
|
||||
if (Group.selected) Group.selected.unselect()
|
||||
if (Group.selected) Group.selected.unselect();
|
||||
if ((event.shiftKey || Pressing.overrides.shift) !== true && (event.ctrlOrCmd || Pressing.overrides.ctrl) !== true) {
|
||||
selected.length = 0
|
||||
}
|
||||
@ -88,6 +88,10 @@ class Group extends OutlinerNode {
|
||||
//Select Only Group, unselect Children
|
||||
selected.length = 0
|
||||
} else {
|
||||
// Fix for #2401
|
||||
if (previous_first_selected && previous_first_selected.isChildOf(this)) {
|
||||
selected.push(previous_first_selected);
|
||||
}
|
||||
scope.children.forEach(function(s) {
|
||||
s.selectLow()
|
||||
})
|
||||
|
@ -434,7 +434,7 @@ class OutlinerElement extends OutlinerNode {
|
||||
TickUpdates.selection = true;
|
||||
return copy;
|
||||
}
|
||||
select(event, isOutlinerClick) {
|
||||
select(event, is_outliner_click) {
|
||||
if (Modes.animate && !this.constructor.animator) {
|
||||
Blockbench.showQuickMessage('message.group_required_to_animate');
|
||||
return false;
|
||||
@ -442,7 +442,7 @@ class OutlinerElement extends OutlinerNode {
|
||||
//Shift
|
||||
var just_selected = [];
|
||||
let allow_multi_select = (!Modes.paint || (Toolbox.selected.id == 'fill_tool' && BarItems.fill_mode.value == 'selected_elements'));
|
||||
if (event && allow_multi_select && (event.shiftKey === true || Pressing.overrides.shift) && this.getParentArray().includes(selected[selected.length-1]) && isOutlinerClick) {
|
||||
if (event && allow_multi_select && (event.shiftKey === true || Pressing.overrides.shift) && this.getParentArray().includes(selected[selected.length-1]) && is_outliner_click) {
|
||||
var starting_point;
|
||||
var last_selected = selected[selected.length-1]
|
||||
this.getParentArray().forEach((s, i) => {
|
||||
@ -485,7 +485,9 @@ class OutlinerElement extends OutlinerNode {
|
||||
|
||||
//Normal
|
||||
} else {
|
||||
selected.forEachReverse(obj => obj.unselect())
|
||||
selected.forEachReverse(obj => {
|
||||
if (obj != this) obj.unselect();
|
||||
})
|
||||
if (Group.selected) Group.selected.unselect()
|
||||
this.selectLow()
|
||||
just_selected.push(this)
|
||||
@ -510,6 +512,9 @@ class OutlinerElement extends OutlinerNode {
|
||||
unselect() {
|
||||
Project.selected_elements.remove(this);
|
||||
this.selected = false;
|
||||
if (UVEditor.selected_element_faces[this.uuid]) {
|
||||
delete UVEditor.selected_element_faces[this.uuid];
|
||||
}
|
||||
TickUpdates.selection = true;
|
||||
return this;
|
||||
}
|
||||
@ -1404,7 +1409,7 @@ Interface.definePanels(function() {
|
||||
v-bind:style="{'--indentation': indentation}"
|
||||
@contextmenu.prevent.stop="node.showContextMenu($event)"
|
||||
@click="node.select($event, true)"
|
||||
@touchstart="node.select($event)" :title="node.title"
|
||||
:title="node.title"
|
||||
@dblclick.stop.self="!node.locked && renameOutliner()"
|
||||
>` +
|
||||
//Opener
|
||||
@ -1587,8 +1592,10 @@ Interface.definePanels(function() {
|
||||
let key = e1.target.getAttribute('toggle');
|
||||
let previous_values = {};
|
||||
let value = original[key];
|
||||
let toggle_config = Outliner.buttons[key];
|
||||
value = (typeof value == 'number') ? (value+1) % 3 : !value;
|
||||
|
||||
if (!toggle_config) return;
|
||||
if (!(key == 'locked' || key == 'visibility' || Modes.edit)) return;
|
||||
|
||||
function move(e2) {
|
||||
@ -1609,11 +1616,15 @@ Interface.definePanels(function() {
|
||||
} else if (!affected.includes(node) && (!node.locked || key == 'locked' || key == 'visibility')) {
|
||||
let new_affected = [node];
|
||||
if (node instanceof Group) {
|
||||
node.forEachChild(node => new_affected.push(node))
|
||||
if (toggle_config.change_children != false) {
|
||||
node.forEachChild(node => {
|
||||
if (node.buttons.find(b => b.id == key)) new_affected.push(node)
|
||||
});
|
||||
}
|
||||
affected_groups.push(node);
|
||||
} else if (node.selected && selected.length > 1) {
|
||||
selected.forEach(el => {
|
||||
if (node[key] != undefined) new_affected.safePush(el);
|
||||
} else if (node.selected && Outliner.selected.length > 1) {
|
||||
Outliner.selected.forEach(el => {
|
||||
if (el.buttons.find(b => b.id == key)) new_affected.safePush(el);
|
||||
})
|
||||
}
|
||||
new_affected.forEach(node => {
|
||||
@ -1895,7 +1906,7 @@ class Face {
|
||||
if (Format.per_group_texture && this.element.parent instanceof Group && this.element.parent.texture) {
|
||||
return Texture.all.findInArray('uuid', this.element.parent.texture);
|
||||
}
|
||||
if (this.texture !== null && (Format.single_texture || (Format.single_texture_default && !this.texture))) {
|
||||
if (this.texture !== null && (Format.single_texture || (Format.single_texture_default && (Format.per_group_texture || !this.texture)))) {
|
||||
return Texture.getDefault();
|
||||
}
|
||||
if (typeof this.texture === 'string') {
|
||||
@ -1920,8 +1931,6 @@ class Face {
|
||||
let tex = this.getTexture()
|
||||
if (tex === null) {
|
||||
copy.texture = null;
|
||||
} else if (!this.texture) {
|
||||
copy.texture = false;
|
||||
} else if (tex instanceof Texture && project) {
|
||||
copy.texture = Texture.all.indexOf(tex)
|
||||
} else if (tex instanceof Texture) {
|
||||
|
@ -723,7 +723,7 @@ Plugin.prototype.menu = new Menu([
|
||||
icon: 'folder',
|
||||
condition: plugin => (isApp && plugin.source == 'file'),
|
||||
click(plugin) {
|
||||
shell.showItemInFolder(plugin.path);
|
||||
showItemInFolder(plugin.path);
|
||||
}
|
||||
},
|
||||
]);
|
||||
@ -1301,8 +1301,8 @@ BARS.defineActions(function() {
|
||||
<div :class="{open: tab == 'installed'}" @click="setTab('installed')">${tl('dialog.plugins.installed')}</div>
|
||||
<div :class="{open: tab == 'available'}" @click="setTab('available')">${tl('dialog.plugins.available')}</div>
|
||||
</div>
|
||||
<ul class="list" id="plugin_list" ref="plugin_list">
|
||||
<li v-for="plugin in viewed_plugins" :plugin="plugin.id" :class="{plugin: true, testing: plugin.fromFile, selected: plugin == selected_plugin, installed: plugin.installed, disabled_plugin: plugin.disabled, incompatible: plugin.isInstallable() !== true}" @click="selectPlugin(plugin)" @contextmenu="selectPlugin(plugin); plugin.showContextMenu($event)">
|
||||
<ul class="list" :class="{paginated_list: pages.length > 1}" id="plugin_list" ref="plugin_list">
|
||||
<li v-for="plugin in viewed_plugins" :plugin="plugin.id" :class="{plugin: true, testing: plugin.fromFile, selected: plugin == selected_plugin, disabled_plugin: plugin.disabled, installed_plugin: plugin.installed, disabled_plugin: plugin.disabled, incompatible: plugin.isInstallable() !== true}" @click="selectPlugin(plugin)" @contextmenu="selectPlugin(plugin); plugin.showContextMenu($event)">
|
||||
<div>
|
||||
<div class="plugin_icon_area">
|
||||
<img v-if="plugin.hasImageIcon()" :src="plugin.getIcon()" width="48" height="48px" />
|
||||
@ -1327,7 +1327,7 @@ BARS.defineActions(function() {
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="plugin_browser_page" v-if="selected_plugin">
|
||||
<div id="plugin_browser_page" v-if="selected_plugin" :class="{plugin_disabled: selected_plugin.disabled, plugin_installed: selected_plugin.installed}">
|
||||
<div v-if="isMobile" @click="selectPlugin(null);" class="plugin_browser_back_button">
|
||||
<i class="material-icons icon">arrow_back_ios</i>
|
||||
${tl('generic.navigate_back')}</div>
|
||||
|
@ -785,7 +785,7 @@ class Preview {
|
||||
if (Toolbox.selected.selectElements && Modes.selected.selectElements && (data.type === 'element' || Toolbox.selected.id == 'knife_tool')) {
|
||||
if (Toolbox.selected.selectFace && data.face && data.element.type != 'mesh') {
|
||||
let face_selection = UVEditor.getSelectedFaces(data.element, true);
|
||||
if (multi_select || group_select) {
|
||||
if (data.element.selected && (multi_select || group_select)) {
|
||||
face_selection.safePush(data.face);
|
||||
} else {
|
||||
face_selection.replace([data.face]);
|
||||
@ -955,7 +955,7 @@ class Preview {
|
||||
|
||||
let vertices = data.element.getSelectedVertices(true);
|
||||
let edges = data.element.getSelectedEdges(true);
|
||||
let faces = data.element.getSelectedEdges(true);
|
||||
let faces = data.element.getSelectedFaces(true);
|
||||
|
||||
if (multi_select || group_select) {
|
||||
let index = edges.findIndex(edge => sameMeshEdge(edge, data.vertices))
|
||||
@ -1085,6 +1085,9 @@ class Preview {
|
||||
x = Math.round(x + offset) - offset;
|
||||
y = Math.round(y + offset) - offset;
|
||||
}
|
||||
if (texture.currentFrame) {
|
||||
y -= texture.display_height * texture.currentFrame;
|
||||
}
|
||||
// Position
|
||||
let brush_coord = face.UVToLocal([x * uv_factor_x, y * uv_factor_y]);
|
||||
let brush_coord_difference_x = face.UVToLocal([(x+1) * uv_factor_x, y * uv_factor_y]);
|
||||
|
@ -303,11 +303,18 @@ class ReferenceImage {
|
||||
this.node.style.left = pos_x + 'px';
|
||||
this.node.style.top = pos_y + 'px';
|
||||
|
||||
let offset_top = preview.node.offsetTop - this.node.offsetTop;
|
||||
let offset_right = preview.node.clientWidth + preview.node.offsetLeft - this.node.offsetLeft;
|
||||
let offset_bottom = preview.node.clientHeight + preview.node.offsetTop - this.node.offsetTop;
|
||||
let offset_left = preview.node.offsetLeft - this.node.offsetLeft;
|
||||
this.node.style.clipPath = `rect(${offset_top}px ${offset_right}px ${offset_bottom}px ${offset_left}px) view-box`;
|
||||
|
||||
} else {
|
||||
this.node.style.width = this.size[0] + 'px';
|
||||
this.node.style.height = this.size[1] + 'px';
|
||||
this.node.style.left = (Math.clamp(this.position[0], 0, this.node.parentNode.clientWidth) - this.size[0]/2) + 'px';
|
||||
this.node.style.top = (Math.clamp(this.position[1], 0, this.node.parentNode.clientHeight) - this.size[1]/2) + 'px';
|
||||
this.node.style.clipPath = '';
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ const Painter = {
|
||||
let texture = Painter.current.texture;
|
||||
|
||||
if (Toolbox.selected.brush && Toolbox.selected.brush.onStrokeEnd) {
|
||||
let result = Toolbox.selected.brush.onStrokeEnd({texture, x, y, uv, event, raycast_data: data});
|
||||
let result = Toolbox.selected.brush.onStrokeEnd({texture});
|
||||
if (result == false) return;
|
||||
}
|
||||
if (Painter.brushChanges) {
|
||||
@ -294,6 +294,7 @@ const Painter = {
|
||||
delete Painter.current.texture;
|
||||
delete Painter.current.textures;
|
||||
delete Painter.current.uv_rects;
|
||||
delete Painter.current.uv_islands;
|
||||
Painter.painting = false;
|
||||
Painter.currentPixel = [-1, -1];
|
||||
},
|
||||
@ -340,6 +341,7 @@ const Painter = {
|
||||
let island = Painter.getMeshUVIsland(Painter.current.face, current_face);
|
||||
island.forEach(fkey => {
|
||||
let face = Mesh.selected[0].faces[fkey];
|
||||
if (!face) return;
|
||||
for (let vkey in face.uv) {
|
||||
min_x = Math.min(min_x, face.uv[vkey][0]); max_x = Math.max(max_x, face.uv[vkey][0]);
|
||||
min_y = Math.min(min_y, face.uv[vkey][1]); max_y = Math.max(max_y, face.uv[vkey][1]);
|
||||
@ -1734,7 +1736,11 @@ const Painter = {
|
||||
} else {
|
||||
preset.color = null;
|
||||
}
|
||||
|
||||
if (form.pixel_perfect) {
|
||||
preset.pixel_perfect = true;
|
||||
} else {
|
||||
preset.pixel_perfect = false;
|
||||
}
|
||||
if (form.shape !== 'unset') {
|
||||
preset.shape = form.shape;
|
||||
} else {
|
||||
@ -2106,8 +2112,8 @@ SharedActions.add('paste', {
|
||||
layer.setSize(frame.width, frame.height);
|
||||
layer.ctx.putImageData(image_data, 0, 0);
|
||||
if (!offset) layer.center();
|
||||
texture.layers.push(layer);
|
||||
layer.select();
|
||||
|
||||
layer.addForEditing();
|
||||
layer.setLimbo();
|
||||
texture.updateChangesAfterEdit();
|
||||
|
||||
|
@ -211,7 +211,7 @@ BARS.defineActions(function() {
|
||||
|
||||
} else if (Format.id == 'java_block') {
|
||||
docs = 'https://minecraft.wiki/w/Resource_pack#Animation';
|
||||
file_name = texture.name + '.mcmeta';
|
||||
file_name = (texture.name.match(/\.png$/i) ? texture.name : texture.name + '.png') + '.mcmeta';
|
||||
content = texture.getMCMetaContent();
|
||||
text = compileJSON(content);
|
||||
}
|
||||
|
@ -888,15 +888,17 @@ class Texture {
|
||||
if (event instanceof Event) {
|
||||
Prop.active_panel = 'textures';
|
||||
}
|
||||
if (event && (event.shiftKey || event.ctrlKey)) {
|
||||
this.multi_selected = true;
|
||||
if (event.shiftKey) {
|
||||
if (event && (event.shiftKey || event.ctrlOrCmd || Pressing.overrides.ctrl || Pressing.overrides.shift)) {
|
||||
if (event.shiftKey || Pressing.overrides.shift) {
|
||||
this.multi_selected = true;
|
||||
let start_i = Texture.last_selected;
|
||||
let end_i = Texture.all.indexOf(this);
|
||||
if (start_i > end_i) [start_i, end_i] = [end_i, start_i];
|
||||
for (let i = start_i+1; i < end_i; i++) {
|
||||
Texture.all[i].multi_selected = true;
|
||||
}
|
||||
} else {
|
||||
this.multi_selected = !this.multi_selected;
|
||||
}
|
||||
Texture.last_selected = Texture.all.indexOf(this);
|
||||
return;
|
||||
@ -931,7 +933,7 @@ class Texture {
|
||||
Blockbench.dispatchEvent('update_texture_selection');
|
||||
return this;
|
||||
}
|
||||
add(undo) {
|
||||
add(undo, uv_size_from_resolution) {
|
||||
if (isApp && this.path && Project.textures.length) {
|
||||
for (var tex of Project.textures) {
|
||||
if (tex.path === this.path) return tex;
|
||||
@ -940,7 +942,7 @@ class Texture {
|
||||
if (Texture.all.find(t => t.render_mode == 'layered')) {
|
||||
this.render_mode = 'layered';
|
||||
}
|
||||
if (Format.per_texture_uv_size && undo) {
|
||||
if (Format.per_texture_uv_size && uv_size_from_resolution) {
|
||||
this.flags.add('update_uv_size_from_resolution');
|
||||
}
|
||||
if (undo) {
|
||||
@ -1081,7 +1083,7 @@ class Texture {
|
||||
Blockbench.showQuickMessage('texture.error.file')
|
||||
return this;
|
||||
}
|
||||
shell.showItemInFolder(this.path)
|
||||
showItemInFolder(this.path)
|
||||
return this;
|
||||
}
|
||||
openEditor() {
|
||||
@ -1111,10 +1113,9 @@ class Texture {
|
||||
return this;
|
||||
}
|
||||
showContextMenu(event) {
|
||||
var scope = this;
|
||||
scope.select()
|
||||
if (this != Texture.selected) this.select()
|
||||
Prop.active_panel = 'textures'
|
||||
this.menu.open(event, scope)
|
||||
this.menu.open(event, this)
|
||||
}
|
||||
openMenu() {
|
||||
this.select();
|
||||
@ -2236,7 +2237,7 @@ BARS.defineActions(function() {
|
||||
let texture_group = context instanceof TextureGroup ? context : Texture.selected?.getGroup();
|
||||
Undo.initEdit({textures: new_textures});
|
||||
results.forEach(function(f) {
|
||||
let t = new Texture({name: f.name}).fromFile(f).add(false).fillParticle();
|
||||
let t = new Texture({name: f.name}).fromFile(f).add(false, true).fillParticle();
|
||||
new_textures.push(t);
|
||||
if (texture_group) {
|
||||
t.group = texture_group.uuid;
|
||||
|
50
lang/de.json
50
lang/de.json
@ -2070,10 +2070,10 @@
|
||||
"message.auto_fix_mesh_edit.merge_vertices": "Eckpunkte vereinen",
|
||||
"message.auto_fix_mesh_edit.split_quads": "Vierecke teilen",
|
||||
"message.load_images.add_image": "Bild hinzufügen",
|
||||
"dialog.project.modded_entity_entity_class": "Entity Class",
|
||||
"dialog.project.modded_entity_entity_class": "Entity Klasse",
|
||||
"dialog.resize_texture.offset": "Versatz",
|
||||
"dialog.animated_texture_editor.code_reference": "Code Reference",
|
||||
"dialog.animated_texture_editor.code_reference.about": "This code to implement the texture animation was generated as a reference and does not necessarily work without modification. Check out the documentation to learn more.",
|
||||
"dialog.animated_texture_editor.code_reference": "Code Beispiel",
|
||||
"dialog.animated_texture_editor.code_reference.about": "Dieser Code zur Implementierung der animierten Textur wurde als Beispiel generiert, und funktioniert nicht unbedingt ohne weitere Veränderungen. Lies die Dokumentation, um mehr zu erfahren.",
|
||||
"dialog.animated_texture_editor.code_reference.docs": "Dokumentation",
|
||||
"dialog.animated_texture_editor.reframe": "Neu unterteilen",
|
||||
"dialog.animated_texture_editor.stride": "Bildgröße",
|
||||
@ -2081,7 +2081,6 @@
|
||||
"dialog.animated_texture_editor.add_frame": "Einzelbild hinzufügen",
|
||||
"dialog.animated_texture_editor.resize_frames": "Bildgröße ändern...",
|
||||
"dialog.animated_texture_editor.apply": "Anwenden",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Beveled Cuboid",
|
||||
"dialog.add_primitive.edge_size": "Kantenbreite",
|
||||
"dialog.change_animation_speed.speed": "Geschwindigkeit",
|
||||
"dialog.change_animation_speed.adjust_snapping": "Einrastintervall anpassen",
|
||||
@ -2098,32 +2097,32 @@
|
||||
"settings.pixel_grid.desc": "Im Bearbeitungsmodus ein Gitter auf den Texturen anzeigen",
|
||||
"settings.image_editor_grid_size": "Gittergröße im Bildbearbeitungsmodus",
|
||||
"settings.image_editor_grid_size.desc": "Größe des großen Gitters im Bildbearbeitungsmodus",
|
||||
"settings.ground_plane_double_side": "Ground Plane Double-sided",
|
||||
"settings.ground_plane_double_side.desc": "Make the ground plane visible from below",
|
||||
"settings.optifine_save_default_texture": "OptiFine JEM: Export selected texture",
|
||||
"settings.optifine_save_default_texture.desc": "Export the selected texture as the default texture of the model",
|
||||
"action.slider_color_select_threshold": "Color Select Threshold",
|
||||
"action.slider_color_select_threshold.desc": "How close the color of a neighboring pixel has to be to get selected by color or wand select",
|
||||
"action.stretch_tool": "Stretch",
|
||||
"action.stretch_tool.desc": "Tool to select and stretch elements",
|
||||
"settings.ground_plane_double_side": "Doppelseitige Bodenebene",
|
||||
"settings.ground_plane_double_side.desc": "Macht die Bodenebene von unten sichtbar",
|
||||
"settings.optifine_save_default_texture": "OptiFine JEM: Ausgewählte Texture Exportieren",
|
||||
"settings.optifine_save_default_texture.desc": "Exportiere die ausgewählte Textur als Standardtextur für das Modell",
|
||||
"action.slider_color_select_threshold": "Farbauswahl Schwellenwert",
|
||||
"action.slider_color_select_threshold.desc": "Wie ähnlich die Farbe benachbarter Pixel sein muss, um vom Zauberstab oder Farb-Auswahlwerkzeug ausgewählt zu werden",
|
||||
"action.stretch_tool": "Strecken",
|
||||
"action.stretch_tool.desc": "Werkzeug zum Strecken von Elementen",
|
||||
"action.knife_tool": "Messerwerkzeug",
|
||||
"action.knife_tool.desc": "Werkzeug zum Schneiden von Maschenflächen in kleinere Segmente",
|
||||
"action.duplicate_project": "Projekt duplizieren",
|
||||
"action.duplicate_project.desc": "Erstelle eine Kopie des geöffneten Projekts",
|
||||
"action.transform_space.parent": "Parent",
|
||||
"action.transform_pivot_space": "Pivot Transform Space",
|
||||
"action.solidify_mesh_selection": "Solidify Faces",
|
||||
"action.solidify_mesh_selection.desc": "Solidify the selected faces of the mesh",
|
||||
"action.transform_space.parent": "Übergeordnet",
|
||||
"action.transform_pivot_space": "Angelpunkt Transformationsraum",
|
||||
"action.solidify_mesh_selection": "Flächen solide machen",
|
||||
"action.solidify_mesh_selection.desc": "Verwandle die ausgewählten Flächen einer Masche in ein solides Objekt mit Dicke",
|
||||
"action.animated_texture_editor": "Animierte Textur...",
|
||||
"action.animated_texture_editor.desc": "Erstelle oder bearbeite die Daumenkino-Animation der ausgewählten Textur",
|
||||
"action.center_individual_pivots": "Center Individual Pivots",
|
||||
"action.center_individual_pivots.desc": "Set the pivot point of each selected element to its center",
|
||||
"action.uv_cycle": "Cycle UV",
|
||||
"action.uv_cycle.desc": "Cycle through the order of UV vertices without changing the UV positions",
|
||||
"action.uv_cycle_invert": "Cycle Invert UV",
|
||||
"action.uv_cycle_invert.desc": "Reverse the order of UV vertices without changing the UV positions",
|
||||
"action.slider_animation_controller_speed": "Controller Playback Speed",
|
||||
"action.slider_animation_controller_speed.desc": "The playback speed of animation controllers in percent",
|
||||
"action.center_individual_pivots": "Individuelle Angelpunkte zentrieren",
|
||||
"action.center_individual_pivots.desc": "Verschiebt den Angelpunkt jedes einzelnen ausgewählten Elements in dessen Mitte",
|
||||
"action.uv_cycle": "UV Punkte durchrotieren",
|
||||
"action.uv_cycle.desc": "Rotiere die Anordnung der UV Eckpunkte durch, ohne die Positionen zu verändern",
|
||||
"action.uv_cycle_invert": "UV Punkte durchtauschen",
|
||||
"action.uv_cycle_invert.desc": "Kehre die Anordnung der UV Eckpunkte um, ohne die Positionen zu verändern",
|
||||
"action.slider_animation_controller_speed": "Regler Wiedergabegeschwindigkeit",
|
||||
"action.slider_animation_controller_speed.desc": "Die Wiedergabegeschwindigkeit des Animationsreglers in Prozent",
|
||||
"action.optimize_animation": "Animation optimieren",
|
||||
"action.optimize_animation.desc": "Optimiere die ausgewählte Animation und reduziere die Anzahl der Keyframes",
|
||||
"action.change_animation_speed": "Animationsgeschwindigkeit verändern...",
|
||||
@ -2137,5 +2136,6 @@
|
||||
"menu.toolbar.overflow": "Überlauf der Werkzeugleiste",
|
||||
"edit.solidify_mesh_selection.thickness": "Dicke",
|
||||
"reference_image.sync_to_timeline": "Mit der Zeitleiste synchronisieren",
|
||||
"reference_image.toggle_playback": "Abspielen/Pausieren"
|
||||
"reference_image.toggle_playback": "Abspielen/Pausieren",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Abgeschrägter Quader"
|
||||
}
|
758
lang/ru.json
758
lang/ru.json
File diff suppressed because it is too large
Load Diff
742
lang/uk.json
742
lang/uk.json
File diff suppressed because it is too large
Load Diff
40
lang/zh.json
40
lang/zh.json
@ -401,7 +401,7 @@
|
||||
"menu.texture.refresh": "刷新",
|
||||
"menu.texture.change": "更改文件",
|
||||
"menu.texture.folder": "在文件夹中打开",
|
||||
"menu.texture.edit": "外部编辑",
|
||||
"menu.texture.edit": "编辑",
|
||||
"menu.texture.export": "另存为",
|
||||
"menu.texture.save": "保存",
|
||||
"menu.texture.properties": "属性...",
|
||||
@ -1618,7 +1618,7 @@
|
||||
"generic.delete_all": "删除所有",
|
||||
"message.child_model_only.open": "打开父项",
|
||||
"message.child_model_only.open_with_textures": "打开父项并应用纹理",
|
||||
"dialog.project.credit": "Credit",
|
||||
"dialog.project.credit": "归因",
|
||||
"dialog.convert_project.create_copy": "创建副本",
|
||||
"dialog.texture.frame_time": "帧时间",
|
||||
"dialog.texture.frame_time.desc": "设置每帧可见的时长,以刻度为单位。 每个刻度为 1/20 秒。",
|
||||
@ -1692,7 +1692,7 @@
|
||||
"dialog.load_plugins_from_query.text": "此链接的插件需要被安装。您确定要安装此插件吗?",
|
||||
"dialog.select_model.title": "选择模型",
|
||||
"dialog.select_model.bones": "骨骼",
|
||||
"dialog.select_model.cubes": "Cubes",
|
||||
"dialog.select_model.cubes": "多立方",
|
||||
"dialog.settings.create_profile": "创建配置...",
|
||||
"settings_profile.confirm_delete": "确定删除此配置?",
|
||||
"settings_profile.condition.type.selectable": "Manually Selectable",
|
||||
@ -1714,11 +1714,11 @@
|
||||
"action.animation_controller_preview_mode.paused": "已暂停",
|
||||
"action.animation_controller_preview_mode.manual": "手动预览",
|
||||
"action.animation_controller_preview_mode.play": "自动播放",
|
||||
"action.keyframe_interpolation.bezier": "Bézier",
|
||||
"action.keyframe_bezier_linked": "Link Bézier Handles",
|
||||
"action.keyframe_interpolation.bezier": "贝塞尔",
|
||||
"action.keyframe_bezier_linked": "链接贝塞尔手柄",
|
||||
"action.keyframe_bezier_linked.desc": "Connect the right and left keyframe bézier handle",
|
||||
"action.reset_keyframe_handles": "Reset Keyframe Handles",
|
||||
"action.reset_keyframe_handles.desc": "Reset the bézier handles of the selected keyframes",
|
||||
"action.reset_keyframe_handles.desc": "重置已选中贝塞尔手柄",
|
||||
"action.looped_animation_playback": "循环播放",
|
||||
"action.looped_animation_playback.desc": "无限循环预览动画",
|
||||
"timeline.bind_to_actor": "Bind to Actor",
|
||||
@ -1739,7 +1739,7 @@
|
||||
"menu.color_picker.slider_mode": "Slider Mode",
|
||||
"menu.color_picker.slider_mode.hsv": "色相、饱和度、明度",
|
||||
"menu.color_picker.slider_mode.rgb": "RGB",
|
||||
"menu.texture.render_mode.additive": "Additive",
|
||||
"menu.texture.render_mode.additive": "添加",
|
||||
"panel.color.picker_options": "取色器设置",
|
||||
"panel.animation_controllers": "动画控制器",
|
||||
"display.preset.armor_stand": "地面(盔甲架)",
|
||||
@ -1768,7 +1768,7 @@
|
||||
"message.add_reference_image.project": "添加至当前项目",
|
||||
"message.add_reference_image.app": "添加至所有项目",
|
||||
"message.import_particle_texture.import": "导入粒子材质",
|
||||
"message.import_particle_texture.message": "Would you like to import a texture file to be used for your particle?",
|
||||
"message.import_particle_texture.message": "你要导入纹理文件用在粒子吗?",
|
||||
"message.save_codec_selector.title": "保存模型格式",
|
||||
"message.save_codec_selector.message": "选择保存模型的格式。",
|
||||
"message.save_codec_selector.project_file": "Blockbench 项目 (.bbmodel)",
|
||||
@ -1787,8 +1787,8 @@
|
||||
"dialog.proportional_editing.selection": "Selection",
|
||||
"dialog.proportional_editing.selection.linear": "Linear Distance",
|
||||
"dialog.proportional_editing.selection.connections": "Connections",
|
||||
"dialog.mirror_painting_texture_center.middle": "Middle",
|
||||
"dialog.mirror_painting_texture_center.custom": "Custom",
|
||||
"dialog.mirror_painting_texture_center.middle": "中间",
|
||||
"dialog.mirror_painting_texture_center.custom": "风俗中间",
|
||||
"dialog.settings.reset_to_default": "重置至原值",
|
||||
"keybindings.item.num_slider.increase": "Increase",
|
||||
"keybindings.item.num_slider.decrease": "Decrease",
|
||||
@ -1821,7 +1821,7 @@
|
||||
"menu.uv.flip_x": "反转 X 轴",
|
||||
"menu.uv.flip_y": "反转 Y 轴",
|
||||
"menu.mirror_painting.enabled": "启用",
|
||||
"menu.mirror_painting.configure_texture_center": "Configure Texture Center...",
|
||||
"menu.mirror_painting.configure_texture_center": "配置纹理中间...",
|
||||
"reference_image.position": "位置",
|
||||
"reference_image.size": "尺寸",
|
||||
"reference_image.rotation": "旋转",
|
||||
@ -1840,7 +1840,7 @@
|
||||
"codec.common.encoding": "编码",
|
||||
"codec.common.armature": "Export Groups as Armature",
|
||||
"codec.common.export_animations": "导出动作",
|
||||
"codec.common.embed_textures": "Embed Textures",
|
||||
"codec.common.embed_textures": "内嵌纹理",
|
||||
"preview.center_camera": "Center Camera",
|
||||
"display.reference.frame_top": "Item Frame Top",
|
||||
"display.reference.frame_top_invisible": "Item Frame Top (Invisible)",
|
||||
@ -1872,9 +1872,9 @@
|
||||
"dialog.unsaved_work.text": "The following projects contain unsaved changes. Do you want to save your changes?",
|
||||
"dialog.unsaved_work.discard_all": "Discard All",
|
||||
"dialog.unsaved_work.save_all": "Save All",
|
||||
"dialog.resize_texture.mode": "Mode",
|
||||
"dialog.resize_texture.mode.crop": "Crop/Expand",
|
||||
"dialog.resize_texture.mode.scale": "Scale",
|
||||
"dialog.resize_texture.mode": "模式",
|
||||
"dialog.resize_texture.mode.crop": "裁切/扩展",
|
||||
"dialog.resize_texture.mode.scale": "缩放",
|
||||
"dialog.plugins.is_installed": "Installed",
|
||||
"dialog.plugins.is_disabled": "Disabled",
|
||||
"dialog.plugins.disable": "Disable",
|
||||
@ -1965,7 +1965,7 @@
|
||||
"message.texture_refresh_conflict.keep_ours": "Keep Blockbench's version",
|
||||
"message.texture_refresh_conflict.keep_theirs": "Load file and discard all changes and layers in Blockbench",
|
||||
"message.group_required_to_animate": "You cannot animate this type of element. Create a group to animate it.",
|
||||
"dialog.texture.uv_size": "UV Size",
|
||||
"dialog.texture.uv_size": "UV尺寸",
|
||||
"dialog.advanced_screenshot.angle_preset": "Angle Preset",
|
||||
"dialog.advanced_screenshot.resolution": "Resolution",
|
||||
"dialog.advanced_screenshot.zoom_to_fit": "Zoom To Fit",
|
||||
@ -2004,8 +2004,8 @@
|
||||
"action.swap_colors.desc": "Swap the main color with the secondary color",
|
||||
"action.apply_mesh_rotation": "Apply Rotation",
|
||||
"action.apply_mesh_rotation.desc": "Reset the element rotation of the mesh, and apply it to the geometry instead",
|
||||
"action.flip_texture_x.desc": "Flip the texture or layer horizontally",
|
||||
"action.flip_texture_y.desc": "Flip the texture or layer vertically",
|
||||
"action.flip_texture_x.desc": "水平翻转纹理或层",
|
||||
"action.flip_texture_y.desc": "翻转纹理或层垂直",
|
||||
"action.rotate_texture_cw.desc": "Rotate the texture or layer clockwise",
|
||||
"action.rotate_texture_ccw.desc": "Rotate the texture or layer counter-clockwise",
|
||||
"action.crop_texture_to_selection": "Crop Texture to Selection",
|
||||
@ -2081,7 +2081,6 @@
|
||||
"dialog.animated_texture_editor.add_frame": "Add Frame",
|
||||
"dialog.animated_texture_editor.resize_frames": "Resize Frames...",
|
||||
"dialog.animated_texture_editor.apply": "Apply",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Beveled Cuboid",
|
||||
"dialog.add_primitive.edge_size": "Edge Size",
|
||||
"dialog.change_animation_speed.speed": "Speed",
|
||||
"dialog.change_animation_speed.adjust_snapping": "Adjust Snapping",
|
||||
@ -2137,5 +2136,6 @@
|
||||
"menu.toolbar.overflow": "Toolbar Overflow",
|
||||
"edit.solidify_mesh_selection.thickness": "Thickness",
|
||||
"reference_image.sync_to_timeline": "Sync To Timeline",
|
||||
"reference_image.toggle_playback": "Play/Pause"
|
||||
"reference_image.toggle_playback": "Play/Pause",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Beveled Cuboid"
|
||||
}
|
@ -401,7 +401,7 @@
|
||||
"menu.texture.refresh": "重新載入",
|
||||
"menu.texture.change": "更改檔案",
|
||||
"menu.texture.folder": "在資料夾中開啟",
|
||||
"menu.texture.edit": "Edit",
|
||||
"menu.texture.edit": "編輯",
|
||||
"menu.texture.export": "儲存為",
|
||||
"menu.texture.save": "儲存",
|
||||
"menu.texture.properties": "屬性...",
|
||||
@ -1440,14 +1440,14 @@
|
||||
"edit.loop_cut.direction": "方向",
|
||||
"message.meshes_and_box_uv": "Meshes are not compatible with Box UV. Go to File > Project... and switch to Per-face UV.",
|
||||
"data.texture_mesh": "Texture Mesh",
|
||||
"generic.left": "Left",
|
||||
"generic.right": "Right",
|
||||
"mode.start.quick_setup": "Quick Setup",
|
||||
"generic.left": "左",
|
||||
"generic.right": "右",
|
||||
"mode.start.quick_setup": "快設置",
|
||||
"mode.start.keymap": "Keymap",
|
||||
"mode.start.quick_setup.more_themes": "More...",
|
||||
"mode.start.quick_setup.more_themes": "等...",
|
||||
"dialog.resize_texture.animation_frames": "Animation Frames",
|
||||
"dialog.export_emission_map.title": "Export Emission Map",
|
||||
"dialog.export_emission_map.format": "Format",
|
||||
"dialog.export_emission_map.format": "格式",
|
||||
"dialog.export_emission_map.format.luminance": "Luminance",
|
||||
"dialog.export_emission_map.format.luminance_inverted": "Luminance Inverted",
|
||||
"dialog.export_emission_map.format.colors": "Colors",
|
||||
@ -1518,7 +1518,7 @@
|
||||
"format.java_block.info.size": "The model is limited to a size of 3 by 3 by 3 blocks. Display settings can make item models larger though",
|
||||
"format.java_block.info.animation": "This format does not support animations in vanilla Minecraft. If you are creating a mod, you can use GeckoLib to animate models. If not, the only way to animate is to switch out the model using commands or animated textures.",
|
||||
"format.bedrock.info.textures": "Each model can only have one texture",
|
||||
"format.bedrock_block": "Bedrock Block",
|
||||
"format.bedrock_block": "Bedrock方塊",
|
||||
"format.bedrock_block.desc": "Block model for Minecraft Bedrock Edition",
|
||||
"format.modded_entity.info.integer_size": "The size of individual cubes is limited to integers.",
|
||||
"format.modded_entity.info.format": "Models are written in Java code instead of dedicated data structures like all other Blockbench export formats.",
|
||||
@ -2081,7 +2081,6 @@
|
||||
"dialog.animated_texture_editor.add_frame": "Add Frame",
|
||||
"dialog.animated_texture_editor.resize_frames": "Resize Frames...",
|
||||
"dialog.animated_texture_editor.apply": "Apply",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Beveled Cuboid",
|
||||
"dialog.add_primitive.edge_size": "Edge Size",
|
||||
"dialog.change_animation_speed.speed": "Speed",
|
||||
"dialog.change_animation_speed.adjust_snapping": "Adjust Snapping",
|
||||
@ -2137,5 +2136,6 @@
|
||||
"menu.toolbar.overflow": "Toolbar Overflow",
|
||||
"edit.solidify_mesh_selection.thickness": "Thickness",
|
||||
"reference_image.sync_to_timeline": "Sync To Timeline",
|
||||
"reference_image.toggle_playback": "Play/Pause"
|
||||
"reference_image.toggle_playback": "Play/Pause",
|
||||
"dialog.add_primitive.shape.beveled_cuboid": "Beveled Cuboid"
|
||||
}
|
@ -101,8 +101,8 @@ class CanvasFrame {
|
||||
}
|
||||
}
|
||||
|
||||
var trimHeight = bound.bottom - bound.top,
|
||||
trimWidth = bound.right - bound.left,
|
||||
var trimHeight = bound.bottom - bound.top + 1,
|
||||
trimWidth = bound.right - bound.left + 1,
|
||||
trimmed = this.ctx.getImageData(bound.left, bound.top, trimWidth, trimHeight);
|
||||
|
||||
copy.canvas.width = trimWidth;
|
||||
|
7
main.js
7
main.js
@ -1,4 +1,4 @@
|
||||
const {app, BrowserWindow, Menu, ipcMain} = require('electron')
|
||||
const {app, BrowserWindow, Menu, ipcMain, shell} = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
@ -247,6 +247,9 @@ ipcMain.on('request-color-picker', async (event, arg) => {
|
||||
})
|
||||
}
|
||||
})
|
||||
ipcMain.on('show-item-in-folder', async (event, path) => {
|
||||
shell.showItemInFolder(path);
|
||||
})
|
||||
|
||||
app.on('ready', () => {
|
||||
|
||||
@ -266,7 +269,7 @@ app.on('ready', () => {
|
||||
}
|
||||
|
||||
app_was_loaded = true;
|
||||
if (process.execPath && process.execPath.match(/electron\.\w+$/)) {
|
||||
if (process.execPath && process.execPath.match(/node_modules[\\\/]electron/)) {
|
||||
|
||||
console.log('[Blockbench] App launched in development mode')
|
||||
|
||||
|
876
package-lock.json
generated
876
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -122,7 +122,7 @@
|
||||
"publish-windows": "npm run bundle && electron-builder -w --publish=onTagOrDraft && node ./scripts/rename_portable.js && electron-builder --windows portable --publish=onTagOrDraft",
|
||||
"pwa": "node ./scripts/generate_pwa.js",
|
||||
"prepublish": "npm run bundle && npm run pwa",
|
||||
"webapp": "git checkout gh-pages && git merge master && git push && git checkout master"
|
||||
"webapp": "git checkout gh-pages && git pull && git merge master && git push && git checkout master"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/notarize": "^2.3.0",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"light": "#f4f3ff",
|
||||
"accent_text": "#000006",
|
||||
"subtle_text": "#848891",
|
||||
"bright_ui_text": "#000006",
|
||||
"grid": "#495061",
|
||||
"wireframe": "#576f82",
|
||||
"checkerboard": "#1c2026"
|
||||
|
@ -20,6 +20,7 @@
|
||||
"light": "#111111",
|
||||
"accent_text": "#000006",
|
||||
"subtle_text": "#6b6d72",
|
||||
"bright_ui_text": "#000006",
|
||||
"grid": "#b0b2b8",
|
||||
"wireframe": "#8797a3",
|
||||
"checkerboard": "#eeeeee"
|
||||
|
Loading…
x
Reference in New Issue
Block a user