2
0
mirror of https://github.com/JannisX11/blockbench.git synced 2025-03-19 17:01:55 +08:00
This commit is contained in:
JannisX11 2019-04-07 18:53:33 +02:00
parent 123d46a37a
commit 46e5f39312
44 changed files with 4325 additions and 1869 deletions

@ -13,11 +13,32 @@ License: MIT
*display: inline;
*zoom: 1;
/* https://github.com/bgrins/spectrum/issues/40 */
z-index: 9999994;
z-index: 1;
overflow: hidden;
}
.sp-container:not(.sp-flat) {
z-index: 22;
}
.sp-container.sp-flat {
position: relative;
background: transparent;
box-shadow: none;
border: none;
width: 100%;
}
.sp-container.sp-flat {
position: relative;
background: transparent;
box-shadow: none;
}
.sp-container.sp-flat .sp-picker-container {
width: calc(100% - 70px);
}
.sp-container.sp-flat .sp-button-container {
display: none;
}
.sp-container.sp-flat .sp-input-container {
width: 100%;
}
/* Fix for * { box-sizing: border-box; } */
@ -46,19 +67,19 @@ License: MIT
top:0;
left:0;
bottom:0;
right:20%;
right: 32px;
}
.sp-hue {
position: absolute;
top:0;
right:0;
bottom:0;
left:84%;
width: 24px;
height: 100%;
}
.sp-clear-enabled .sp-hue {
top:33px;
top:30px;
height: 77.5%;
}
@ -104,9 +125,6 @@ License: MIT
height: 22px;
margin-top: 16px;
}
.sp-alpha-inner {
border: solid 1px var(--color-border);
}
.sp-clear {
display: none;
@ -288,9 +306,6 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
.sp-top {
margin-bottom: 3px;
}
.sp-color, .sp-hue, .sp-clear {
border: solid 1px var(--color-border);
}
/* Input */
.sp-input-container {
@ -393,7 +408,7 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
outline: none;
}
.sp-replacer:hover, .sp-replacer.sp-active {
color: var(--color-text_acc);
color: var(--color-light);
}
.sp-replacer.sp-disabled {
cursor:default;
@ -411,7 +426,6 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
position:relative;
width:25px;
height: 20px;
border: solid 1px #222;
margin-right: 5px;
float:left;
z-index: 0;
@ -450,6 +464,16 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
text-decoration:none;
}
#main_colorpicker_preview {
width: 100%;
height: 12px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
}
#main_colorpicker_preview > div {
width: 100%;
height: 12px;
}
.sp-palette span:hover, .sp-palette span.sp-thumb-active {
border-color: #000;

File diff suppressed because it is too large Load Diff

@ -19,7 +19,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '2.5.1';
const appVersion = '2.6.0';
</script>
<script src="lib/vue.min.js"></script>
<script src="lib/vue_sortable.js"></script>
@ -30,6 +30,7 @@
<script src="lib/jimp.min.js"></script>
<script src="lib/jszip.min.js"></script>
<script src="lib/gif.js"></script>
<script src="lib/peer.min.js"></script>
<script src="lib/spectrum.js"></script>
<script src="lib/three.js"></script>
<script src="lib/three_custom.js"></script>
@ -43,6 +44,7 @@
<script src="js/keyboard.js"></script>
<script src="js/settings.js"></script>
<script src="js/undo.js"></script>
<script src="js/edit_sessions.js"></script>
<script type="text/javascript">
if (isApp === true) {
@ -129,7 +131,7 @@
</div>
<div class="button_bar" v-if="plugin.isInstallable()">
<button type="button" class="" v-on:click="plugin.uninstall()" v-if="plugin.installed"><i class="material-icons">delete</i><span class="tl">dialog.plugins.uninstall</span></button>
<button type="button" class="" v-on:click="plugin.download()" v-else><i class="material-icons">add</i><span class="tl">dialog.plugins.install</span></button>
<button type="button" class="" v-on:click="plugin.download(true)" v-else><i class="material-icons">add</i><span class="tl">dialog.plugins.install</span></button>
<button type="button" class="local_only" v-on:click="plugin.reload()" v-if="plugin.installed && plugin.fromFile && isApp"><i class="material-icons">refresh</i><span class="tl">dialog.plugins.reload</span></button>
</div>
<div class="button_bar tiny tl" v-else>{{ checkIfInstallable(plugin) }}</div>
@ -149,6 +151,35 @@
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
<div class="dialog draggable paddinged" id="edit_sessions">
<h2 class="dialog_handle tl">dialog.edit_session.title</h2>
<div class="dialog_bar">
<label class="name_space_left tl">edit_session.username</label>
<input type="text" class="dark_bordered half" id="edit_session_username">
</div>
<div class="dialog_bar">
<label class="name_space_left tl">edit_session.token</label>
<input type="text" class="dark_bordered half f_left" id="edit_session_token">
<div id="edit_session_copy_button" class="tool" onclick="EditSession.copyToken()"><div class="tooltip tl">action.paste</div><i class="fa fa_big fa-clipboard"></i></div>
</div>
<div class="edit_session_inactive">
<p class="tl">edit_session.about</p>
<p>This feature is in BETA. Bugs may occur while using it.</p>
</div>
<div class="edit_session_active hidden">
<p><b class="tl">edit_session.status</b>: <span class="tl" id="edit_session_status">edit_session.connected</span></p>
</div>
<div class="dialog_bar">
<button type="button" class="edit_session_inactive confirm_btn tl" onclick="EditSession.join();">edit_session.join</button>
<button type="button" class="edit_session_inactive tl" onclick="EditSession.start();">edit_session.create</button>
<button type="button" class="edit_session_active tl" onclick="EditSession.quit();">edit_session.quit</button>
<button type="button" class="cancel_btn tl" onclick="hideDialog();">dialog.cancel</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
<div class="dialog draggable paddinged" id="toolbar_edit">
<h2 class="dialog_handle tl">dialog.toolbar_edit.title</h2>
@ -276,9 +307,7 @@
<div class="dialog draggable paddinged" id="scaling">
<h2 class="dialog_handle tl">dialog.scale.title</h2>
<div class="dialog_bar narrow">
<label class="tl">dialog.scale.axis</label>
</div>
<label class="tl">dialog.scale.axis</label>
<div class="dialog_bar" style="height: 32px;">
<input type="checkbox" class="toggle_panel" id="model_scale_x_axis" onchange="scaleAll()" checked>
@ -289,20 +318,28 @@
<label class="toggle_panel" for="model_scale_z_axis">Z</label>
</div>
<div class="dialog_bar narrow">
<label class="tl">dialog.scale.scale</label>
<label class="tl">data.origin</label>
<div class="dialog_bar">
<label for="scaling_origin_x" class="inline_label tl">X</label>
<input type="number" id="scaling_origin_x" class="dark_bordered mediun_width" oninput="scaleAll()">
<label for="scaling_origin_y" class="inline_label tl">Y</label>
<input type="number" id="scaling_origin_y" class="dark_bordered mediun_width" oninput="scaleAll()">
<label for="scaling_origin_z" class="inline_label tl">Z</label>
<input type="number" id="scaling_origin_z" class="dark_bordered mediun_width" oninput="scaleAll()">
</div>
<label class="tl">dialog.scale.scale</label>
<div class="dialog_bar" style="height: 32px;">
<input type="range" id="model_scale_range" value="1" min="0" max="4" step="0.02" oninput="modelScaleSync()">
<input type="number" class="f_left" id="model_scale_label" min="0" max="4" step="0.02" value="1" oninput="modelScaleSync(true)">
</div>
<div class="dialog_bar narrow" id="scaling_clipping_warning"></div>
<div class="dialog_bar">
<button type="button" onclick="scaleAll(true)" class="large confirm_btn tl">dialog.scale.confirm</button>
<button type="button" class="large cancel_btn tl" onclick="cancelScaleAll()">dialog.cancel</button>
<button type="button" class="large hidden tl" id="scale_overflow_btn" onclick="scaleAllSelectOverflow()">dialog.scale.select_overflow</button>
<button type="button" onclick="scaleAll(true)" class="confirm_btn tl">dialog.scale.confirm</button>
<button type="button" class="cancel_btn tl" onclick="cancelScaleAll()">dialog.cancel</button>
<button type="button" class="hidden tl" id="scale_overflow_btn" onclick="scaleAllSelectOverflow()">dialog.scale.select_overflow</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
@ -422,7 +459,7 @@
<div class="dialog_bar">
<button type="button" class="large tl confirm_btn cancel_btn" onclick="saveProjectSettings()">dialog.confirm</button>
<button type="button" class="large tl confirm_btn cancel_btn" onclick="saveProjectSettings();hideDialog();">dialog.confirm</button>
<button type="button" class="large tl" id="entity_mode_convert" onclick="entityMode.convert()">dialog.project.to_entitymodel</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
@ -466,9 +503,11 @@
<input type="text" class="dark_bordered" style="width: 96%" v-model="setting.value" v-on:input="saveSettings()">
</template>
<template v-else-if="setting.type === 'select'">
<select v-model="setting.value" class="dark_bordered">
<option v-for="(text, id) in setting.options" v-bind:value="id">{{ text }}</option>
</select>
<div class="bar_select">
<select v-model="setting.value">
<option v-for="(text, id) in setting.options" v-bind:value="id">{{ text }}</option>
</select>
</div>
</template>
</li>
</ul>
@ -697,12 +736,15 @@
<div id="action_selector" v-if="open">
<input type="text" v-model="search_input">
<i class="material-icons" id="action_search_bar_icon">search</i>
<ul>
<li v-for="(item, i) in actions" v-on:click="ActionControl.click(item, $event)" v-bind:class="{selected: i === index}" v-on:mouseenter="index = i">
<div class="icon_wrapper normal" v-html="Blockbench.getIconNode(item.icon, item.color).outerHTML"></div>
{{ item.name }}
</li>
</ul>
<div>
<ul>
<li v-for="(item, i) in actions" v-on:click="ActionControl.click(item, $event)" v-bind:class="{selected: i === index}" v-on:mouseenter="index = i">
<div class="icon_wrapper normal" v-html="Blockbench.getIconNode(item.icon, item.color).outerHTML"></div>
{{ item.name }}
</li>
</ul>
<div class="small_text" v-if="actions[index]">{{ actions[index].description }}</div>
</div>
</div>
<header>
@ -864,7 +906,7 @@
>
<div class="texture_icon_wrapper">
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="[E]" v-if="texture.show_icon" />
<i class="material-icons texture_error" title="Image Error" v-if="texture.error">error_outline</i>
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
</div>
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" v-on:click="texture.save()">
@ -873,7 +915,7 @@
</i>
<i class="material-icons texture_particle_icon" v-if="texture.particle">bubble_chart</i>
<div class="texture_name">{{ texture.name }}</div>
<div class="texture_res">{{!Blockbench.entity_mode ? (texture.ratio == 1 ? texture.res + 'px' : (texture.res + 'px, ' + texture.frameCount+'f')) : (texture.res + ' x ' + texture.res*texture.ratio + 'px')}}</div>
<div class="texture_res">{{ texture.error ? texture.getErrorMessage() : (!Blockbench.entity_mode ? (texture.ratio == 1 ? texture.res + 'px' : (texture.res + 'px, ' + texture.frameCount+'f')) : (texture.res + ' x ' + texture.res*texture.ratio + 'px'))}}</div>
</li>
</ul>
</div>
@ -882,9 +924,13 @@
<div id="options" class="panel selection_only">
<p class="tl">panel.options.angle</p>
<div class="toolbar_wrapper rotation"></div>
<p class="tl">panel.options.origin</p>
<p class="tl">data.origin</p>
<div class="toolbar_wrapper origin"></div>
</div>
<div id="color" class="panel">
<div id="main_colorpicker_preview"><div></div></div>
<input id="main_colorpicker">
</div>
<div id="outliner" class="panel grow">
<div class="toolbar_wrapper outliner"></div>
<ul id="cubes_list" class="list">
@ -951,6 +997,9 @@
<div class="f_right">
{{ Prop.fps }} FPS
</div>
<div class="f_right" v-if="Prop.session">
{{ Prop.connections }} Clients
</div>
<div id="status_progress" v-if="Prop.progress" v-bind:style="{width: Prop.progress*100+'%'}"></div>
</div>
<script>

@ -19,7 +19,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '2.5.0';
const appVersion = '2.6.0';
</script>
<script src="lib/vue.min.js"></script>
<script src="lib/vue_sortable.js"></script>
@ -30,6 +30,7 @@
<script src="lib/jimp.min.js"></script>
<script src="lib/jszip.min.js"></script>
<script src="lib/gif.js"></script>
<script src="lib/peer.min.js"></script>
<script src="lib/spectrum.js"></script>
<script src="lib/three.js"></script>
<script src="lib/three_custom.js"></script>
@ -43,6 +44,7 @@
<script src="js/keyboard.js"></script>
<script src="js/settings.js"></script>
<script src="js/undo.js"></script>
<script src="js/edit_sessions.js"></script>
<script type="text/javascript">
if (isApp === true) {
@ -86,6 +88,7 @@
?></div>
<div style="display: none;"></div>
<div id="blackout" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"></div>
<div id="overlay_message_box" style="display: none;">
@ -144,7 +147,7 @@
</div>
<div class="button_bar" v-if="plugin.isInstallable()">
<button type="button" class="" v-on:click="plugin.uninstall()" v-if="plugin.installed"><i class="material-icons">delete</i><span class="tl">dialog.plugins.uninstall</span></button>
<button type="button" class="" v-on:click="plugin.download()" v-else><i class="material-icons">add</i><span class="tl">dialog.plugins.install</span></button>
<button type="button" class="" v-on:click="plugin.download(true)" v-else><i class="material-icons">add</i><span class="tl">dialog.plugins.install</span></button>
<button type="button" class="local_only" v-on:click="plugin.reload()" v-if="plugin.installed && plugin.fromFile && isApp"><i class="material-icons">refresh</i><span class="tl">dialog.plugins.reload</span></button>
</div>
<div class="button_bar tiny tl" v-else>{{ checkIfInstallable(plugin) }}</div>
@ -164,6 +167,35 @@
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
<div class="dialog draggable paddinged" id="edit_sessions">
<h2 class="dialog_handle tl">dialog.edit_session.title</h2>
<div class="dialog_bar">
<label class="name_space_left tl">edit_session.username</label>
<input type="text" class="dark_bordered half" id="edit_session_username">
</div>
<div class="dialog_bar">
<label class="name_space_left tl">edit_session.token</label>
<input type="text" class="dark_bordered half f_left" id="edit_session_token">
<div id="edit_session_copy_button" class="tool" onclick="EditSession.copyToken()"><div class="tooltip tl">action.paste</div><i class="fa fa_big fa-clipboard"></i></div>
</div>
<div class="edit_session_inactive">
<p class="tl">edit_session.about</p>
<p>This feature is in BETA. Bugs may occur while using it.</p>
</div>
<div class="edit_session_active hidden">
<p><b class="tl">edit_session.status</b>: <span class="tl" id="edit_session_status">edit_session.connected</span></p>
</div>
<div class="dialog_bar">
<button type="button" class="edit_session_inactive confirm_btn tl" onclick="EditSession.join();">edit_session.join</button>
<button type="button" class="edit_session_inactive tl" onclick="EditSession.start();">edit_session.create</button>
<button type="button" class="edit_session_active tl" onclick="EditSession.quit();">edit_session.quit</button>
<button type="button" class="cancel_btn tl" onclick="hideDialog();">dialog.cancel</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
<div class="dialog draggable paddinged" id="toolbar_edit">
<h2 class="dialog_handle tl">dialog.toolbar_edit.title</h2>
@ -291,9 +323,7 @@
<div class="dialog draggable paddinged" id="scaling">
<h2 class="dialog_handle tl">dialog.scale.title</h2>
<div class="dialog_bar narrow">
<label class="tl">dialog.scale.axis</label>
</div>
<label class="tl">dialog.scale.axis</label>
<div class="dialog_bar" style="height: 32px;">
<input type="checkbox" class="toggle_panel" id="model_scale_x_axis" onchange="scaleAll()" checked>
@ -304,20 +334,28 @@
<label class="toggle_panel" for="model_scale_z_axis">Z</label>
</div>
<div class="dialog_bar narrow">
<label class="tl">dialog.scale.scale</label>
<label class="tl">data.origin</label>
<div class="dialog_bar">
<label for="scaling_origin_x" class="inline_label tl">X</label>
<input type="number" id="scaling_origin_x" class="dark_bordered mediun_width" oninput="scaleAll()">
<label for="scaling_origin_y" class="inline_label tl">Y</label>
<input type="number" id="scaling_origin_y" class="dark_bordered mediun_width" oninput="scaleAll()">
<label for="scaling_origin_z" class="inline_label tl">Z</label>
<input type="number" id="scaling_origin_z" class="dark_bordered mediun_width" oninput="scaleAll()">
</div>
<label class="tl">dialog.scale.scale</label>
<div class="dialog_bar" style="height: 32px;">
<input type="range" id="model_scale_range" value="1" min="0" max="4" step="0.02" oninput="modelScaleSync()">
<input type="number" class="f_left" id="model_scale_label" min="0" max="4" step="0.02" value="1" oninput="modelScaleSync(true)">
</div>
<div class="dialog_bar narrow" id="scaling_clipping_warning"></div>
<div class="dialog_bar">
<button type="button" onclick="scaleAll(true)" class="large confirm_btn tl">dialog.scale.confirm</button>
<button type="button" class="large cancel_btn tl" onclick="cancelScaleAll()">dialog.cancel</button>
<button type="button" class="large hidden tl" id="scale_overflow_btn" onclick="scaleAllSelectOverflow()">dialog.scale.select_overflow</button>
<button type="button" onclick="scaleAll(true)" class="confirm_btn tl">dialog.scale.confirm</button>
<button type="button" class="cancel_btn tl" onclick="cancelScaleAll()">dialog.cancel</button>
<button type="button" class="hidden tl" id="scale_overflow_btn" onclick="scaleAllSelectOverflow()">dialog.scale.select_overflow</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
</div>
@ -437,7 +475,7 @@
<div class="dialog_bar">
<button type="button" class="large tl confirm_btn cancel_btn" onclick="saveProjectSettings()">dialog.confirm</button>
<button type="button" class="large tl confirm_btn cancel_btn" onclick="saveProjectSettings();hideDialog();">dialog.confirm</button>
<button type="button" class="large tl" id="entity_mode_convert" onclick="entityMode.convert()">dialog.project.to_entitymodel</button>
</div>
<div id="dialog_close_button" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"><i class="material-icons">clear</i></div>
@ -481,9 +519,11 @@
<input type="text" class="dark_bordered" style="width: 96%" v-model="setting.value" v-on:input="saveSettings()">
</template>
<template v-else-if="setting.type === 'select'">
<select v-model="setting.value" class="dark_bordered">
<option v-for="(text, id) in setting.options" v-bind:value="id">{{ text }}</option>
</select>
<div class="bar_select">
<select v-model="setting.value">
<option v-for="(text, id) in setting.options" v-bind:value="id">{{ text }}</option>
</select>
</div>
</template>
</li>
</ul>
@ -712,12 +752,15 @@
<div id="action_selector" v-if="open">
<input type="text" v-model="search_input">
<i class="material-icons" id="action_search_bar_icon">search</i>
<ul>
<li v-for="(item, i) in actions" v-on:click="ActionControl.click(item, $event)" v-bind:class="{selected: i === index}" v-on:mouseenter="index = i">
<div class="icon_wrapper normal" v-html="Blockbench.getIconNode(item.icon, item.color).outerHTML"></div>
{{ item.name }}
</li>
</ul>
<div>
<ul>
<li v-for="(item, i) in actions" v-on:click="ActionControl.click(item, $event)" v-bind:class="{selected: i === index}" v-on:mouseenter="index = i">
<div class="icon_wrapper normal" v-html="Blockbench.getIconNode(item.icon, item.color).outerHTML"></div>
{{ item.name }}
</li>
</ul>
<div class="small_text" v-if="actions[index]">{{ actions[index].description }}</div>
</div>
</div>
<header>
@ -879,16 +922,16 @@
>
<div class="texture_icon_wrapper">
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="[E]" v-if="texture.show_icon" />
<i class="material-icons texture_error" title="Image Error" v-if="texture.error">error_outline</i>
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
</div>
<i class="material-icons texture_particle_icon" v-if="texture.particle">bubble_chart</i>
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" v-on:click="texture.save()">
<template v-if="texture.saved">check_circle</template>
<template v-else>save</template>
</i>
<i class="material-icons texture_particle_icon" v-if="texture.particle">bubble_chart</i>
<div class="texture_name">{{ texture.name }}</div>
<div class="texture_res">{{!Blockbench.entity_mode ? (texture.ratio == 1 ? texture.res + 'px' : (texture.res + 'px, ' + texture.frameCount+'f')) : (texture.res + ' x ' + texture.res*texture.ratio + 'px')}}</div>
<div class="texture_res">{{ texture.error ? texture.getErrorMessage() : (!Blockbench.entity_mode ? (texture.ratio == 1 ? texture.res + 'px' : (texture.res + 'px, ' + texture.frameCount+'f')) : (texture.res + ' x ' + texture.res*texture.ratio + 'px'))}}</div>
</li>
</ul>
</div>
@ -897,9 +940,13 @@
<div id="options" class="panel selection_only">
<p class="tl">panel.options.angle</p>
<div class="toolbar_wrapper rotation"></div>
<p class="tl">panel.options.origin</p>
<p class="tl">data.origin</p>
<div class="toolbar_wrapper origin"></div>
</div>
<div id="color" class="panel">
<div id="main_colorpicker_preview"><div></div></div>
<input id="main_colorpicker">
</div>
<div id="outliner" class="panel grow">
<div class="toolbar_wrapper outliner"></div>
<ul id="cubes_list" class="list">
@ -966,6 +1013,9 @@
<div class="f_right">
{{ Prop.fps }} FPS
</div>
<div class="f_right" v-if="Prop.session">
{{ Prop.connections }} Clients
</div>
<div id="status_progress" v-if="Prop.progress" v-bind:style="{width: Prop.progress*100+'%'}"></div>
</div>
<script>

@ -891,7 +891,7 @@
scope.keyframe = kf
}
}
Undo.initEdit({keyframes: scope.keyframe ? [scope.keyframe] : null})
Undo.initEdit({keyframes: scope.keyframe ? [scope.keyframe] : []})
if (!scope.keyframe) {
var ba = Animator.selected.getBoneAnimator()
scope.keyframe = ba.addKeyframe(null, Timeline.second, channel);
@ -1083,9 +1083,6 @@
beforeFirstChange(event)
var difference = value - (previousValue||0)
if (Toolbox.selected.transformerMode === 'scale') {
axis = 'x';
}
scope.keyframe.offset(axis, difference);
scope.keyframe.select()
@ -1180,7 +1177,7 @@
updateSelection()
} else if (Modes.id === 'animate') {
Undo.finishEdit('change keyframe')
Undo.finishEdit('change keyframe', {keyframes: [scope.keyframe]})
} else if (Modes.id === 'display') {
Undo.finishEdit('edit display slot')

@ -19,6 +19,22 @@ class BarItem {
this.condition = data.condition;
this.nodes = []
this.toolbars = []
//Key
this.category = data.category ? data.category : 'misc'
if (!data.private) {
if (data.keybind) {
this.default_keybind = data.keybind
}
if (Keybinds.stored[this.id]) {
this.keybind = new Keybind(Keybinds.stored[this.id])
} else {
this.keybind = new Keybind(data.keybind)
}
this.keybind.setAction(this.id)
this.work_in_dialog = data.work_in_dialog === true
this.uses = 0;
Keybinds.actions.push(this)
}
}
conditionMet() {
if (this.condition === undefined) {
@ -112,19 +128,6 @@ class Action extends BarItem {
super(data)
var scope = this;
this.type = 'action'
this.category = data.category ? data.category : 'misc'
//Key
if (data.keybind) {
this.default_keybind = data.keybind
}
if (Keybinds.stored[this.id]) {
this.keybind = new Keybind(Keybinds.stored[this.id])
} else {
this.keybind = new Keybind(data.keybind)
}
this.keybind.setAction(this.id)
this.work_in_dialog = data.work_in_dialog === true
this.uses = 0;
//Icon
this.icon = data.icon
this.color = data.color
@ -148,7 +151,6 @@ class Action extends BarItem {
if (data.linked_setting) {
this.toggleLinkedSetting(false)
}
Keybinds.actions.push(this)
}
trigger(event) {
var scope = this;
@ -302,12 +304,16 @@ class NumSlider extends Widget {
}
} else {
this.getInterval = function(event) {
event = event||false;
return canvasGridSize(event.shiftKey, event.ctrlKey);
};
}
if (typeof data.getInterval === 'function') {
this.getInterval = data.getInterval;
}
if (this.keybind) {
this.keybind.shift = null;
}
var scope = this;
this.node = $( `<div class="tool wide widget nslide_tool">
<div class="tooltip">${this.name}</div>
@ -447,6 +453,17 @@ class NumSlider extends Widget {
this.onAfter(difference)
}
}
trigger(event) {
if (typeof this.onBefore === 'function') {
this.onBefore()
}
var difference = this.getInterval(false) * event.shiftKey ? -1 : 1;
this.change(difference)
this.update()
if (typeof this.onAfter === 'function') {
this.onAfter(difference)
}
}
setValue(value, trim) {
if (typeof value === 'string') {
value = parseFloat(value)
@ -503,9 +520,25 @@ class BarSlider extends Widget {
if (typeof data.onChange === 'function') {
this.onChange = data.onChange
}
if (typeof data.onBefore === 'function') {
this.onBefore = data.onBefore
}
if (typeof data.onAfter === 'function') {
this.onAfter = data.onAfter
}
$(this.node).children('input').on('input', function(event) {
scope.change(event)
})
if (scope.onBefore) {
$(this.node).children('input').on('mousedown', function(event) {
scope.onBefore(event)
})
}
if (scope.onAfter) {
$(this.node).children('input').on('change', function(event) {
scope.onAfter(event)
})
}
}
change(event) {
this.set( parseFloat( $(event.target).val() ) )
@ -527,23 +560,24 @@ class BarSelect extends Widget {
var scope = this;
this.type = 'select'
this.icon = 'list'
this.node = $('<div class="tool widget"><select class="dark_bordered"></select></div>').get(0)
this.node = $('<div class="tool widget bar_select"><select></select></div>').get(0)
if (data.width) {
$(this.node).children('select').css('width', data.width+'px')
}
this.value = data.value
this.values = []
var select = $(this.node).find('select')
if (data.options) {
for (var key in data.options) {
if (data.options.hasOwnProperty(key)) {
if (!this.value) {
this.value = key
}
var name = data.options[key]
if (name === true) {
name = tl('action.'+this.id+'.'+key)
}
select.append('<option id="'+key+'">'+name+'</option>')
if (!this.value) {
this.value = key
}
var name = data.options[key]
if (name === true) {
name = tl('action.'+this.id+'.'+key)
}
select.append(`<option id="${key}" ${key == this.value ? 'selected' : ''}>${name}</option>`)
this.values.push(key);
}
}
this.addLabel(data.label)
@ -554,6 +588,26 @@ class BarSelect extends Widget {
scope.change(event)
})
}
trigger(event) {
var scope = this;
if (BARS.condition(scope.condition, scope)) {
if (event && event.type === 'click' && event.altKey && scope.keybind) {
var record = function() {
document.removeEventListener('keyup', record)
scope.keybind.record()
}
document.addEventListener('keyup', record, false)
return true;
}
var index = this.values.indexOf(this.value)+1
if (index >= this.values.length) index = 0;
this.set(this.values[index])
scope.uses++;
return true;
}
return false;
}
change(event) {
this.set( $(event.target).find('option:selected').prop('id') )
if (this.onChange) {
@ -584,14 +638,20 @@ class BarText extends Widget {
}
}
set(text) {
this.text = text;
$(this.nodes).text(text)
return this;
}
update() {
if (typeof this.onUpdate === 'function') {
this.onUpdate()
}
return this;
}
trigger(event) {
Blockbench.showQuickMessage(this.text)
return this;
}
}
class ColorPicker extends Widget {
constructor(data) {
@ -646,7 +706,6 @@ class ColorPicker extends Widget {
return this.value;
}
}
class Toolbar {
constructor(data) {
var scope = this;
@ -922,7 +981,8 @@ const BARS = {
options: {
move: true,
scale: true
}
},
category: 'edit'
})
new Action({
id: 'swap_tools',
@ -1022,94 +1082,13 @@ const BARS = {
keybind: new Keybind({key: 88, ctrl: true, shift: null}),
click: function (event) {Clipbench.copy(event, true)}
})
new Action({
id: 'duplicate',
icon: 'content_copy',
category: 'edit',
condition: () => (!display_mode && !Animator.open && (selected.length || selected_group)),
keybind: new Keybind({key: 68, ctrl: true}),
click: function () {
if (selected_group && (selected_group.matchesSelection() || selected.length === 0)) {
var cubes_before = elements.length
Undo.initEdit({outliner: true, cubes: [], selection: true})
var g = selected_group.duplicate()
g.select().isOpen = true;
Undo.finishEdit('duplicate_group', {outliner: true, cubes: elements.slice().slice(cubes_before), selection: true})
} else {
duplicateCubes();
}
}
})
new Action({
id: 'delete',
icon: 'delete',
category: 'edit',
condition: () => (!display_mode && !Animator.open && selected.length),
keybind: new Keybind({key: 46}),
click: function () {
deleteCubes();
}
})
new Action({
id: 'sort_outliner',
icon: 'sort_by_alpha',
category: 'edit',
click: function () {
Undo.initEdit({outliner: true});
sortOutliner();
Undo.finishEdit('sort_outliner')
}
})
new Action({
id: 'local_move',
icon: 'check_box',
category: 'edit',
linked_setting: 'local_move',
click: function () {
BarItems.local_move.toggleLinkedSetting()
updateSelection()
}
})
new Action({
id: 'select_window',
icon: 'filter_list',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
keybind: new Keybind({key: 70, ctrl: true}),
click: function () {
showDialog('selection_creator')
$('#selgen_name').focus()
}
})
new Action({
id: 'invert_selection',
icon: 'swap_vert',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
click: function () {invertSelection()}
})
new Action({
id: 'select_all',
icon: 'select_all',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
keybind: new Keybind({key: 65, ctrl: true}),
click: function () {selectAll()}
})
new Action({
id: 'collapse_groups',
icon: 'format_indent_decrease',
category: 'edit',
condition: () => TreeElements.length > 0,
click: function () {collapseAllGroups()}
})
//Move Cube Keys
new Action({
id: 'move_up',
icon: 'arrow_upward',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 38, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(-1, 2, e)}
})
@ -1117,7 +1096,7 @@ const BARS = {
id: 'move_down',
icon: 'arrow_downward',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 40, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(1, 2, e)}
})
@ -1125,7 +1104,7 @@ const BARS = {
id: 'move_left',
icon: 'arrow_back',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 37, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(-1, 0, e)}
})
@ -1133,7 +1112,7 @@ const BARS = {
id: 'move_right',
icon: 'arrow_forward',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 39, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(1, 0, e)}
})
@ -1141,7 +1120,7 @@ const BARS = {
id: 'move_forth',
icon: 'keyboard_arrow_up',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 33, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(-1, 1, e)}
})
@ -1149,7 +1128,7 @@ const BARS = {
id: 'move_back',
icon: 'keyboard_arrow_down',
category: 'transform',
condition: () => (selected.length && !open_menu),
condition: () => (selected.length && !open_menu && Modes.id === 'edit'),
keybind: new Keybind({key: 34, ctrl: null, shift: null}),
click: function (e) {moveCubesRelative(1, 1, e)}
})
@ -1218,21 +1197,18 @@ const BARS = {
id: 'zoom_in',
icon: 'zoom_in',
category: 'view',
condition: isApp,
click: function () {setZoomLevel('in')}
})
new Action({
id: 'zoom_out',
icon: 'zoom_out',
category: 'view',
condition: isApp,
click: function () {setZoomLevel('out')}
})
new Action({
id: 'zoom_reset',
icon: 'zoom_out_map',
category: 'view',
condition: isApp,
click: function () {setZoomLevel('reset')}
})
@ -1387,7 +1363,8 @@ const BARS = {
Toolbars.keyframe = new Toolbar({
id: 'keyframe',
children: [
'slider_keyframe_time'
'slider_keyframe_time',
'reset_keyframe'
],
default_place: true
})
@ -1421,7 +1398,6 @@ const BARS = {
children: [
'brush_mode',
'fill_mode',
'brush_color',
'slider_brush_size',
'slider_brush_opacity',
'slider_brush_softness'
@ -1978,11 +1954,12 @@ const MenuBar = {
setup: function() {
new BarMenu('file', [
'project_window',
{name: 'menu.file.new', id: 'new', icon: 'insert_drive_file', children: [
'_',
{name: 'menu.file.new', id: 'new', icon: 'insert_drive_file', condition: () => (!EditSession.active || EditSession.hosting), children: [
'new_block_model',
'new_entity_model',
]},
{name: 'menu.file.recent', id: 'recent', icon: 'history', condition: function() {return isApp && recent_projects.length}, children: function() {
{name: 'menu.file.recent', id: 'recent', icon: 'history', condition: function() {return isApp && recent_projects.length && (!EditSession.active || EditSession.hosting)}, children: function() {
var arr = []
recent_projects.forEach(function(p) {
switch (p.icon_id) {
@ -2012,11 +1989,13 @@ const MenuBar = {
'export_class_entity',
'export_optifine_part',
'export_optifine_full',
'export_obj'
'export_obj',
'upload_sketchfab'
]},
'save',
'_',
'settings_window',
'edit_session',
'update_window',
'donate',
'reload'
@ -2068,7 +2047,7 @@ const MenuBar = {
], () => (!display_mode && !Animator.open))
new BarMenu('filter', [
'plugins_window',
'_'
'_',
/*
plaster
optimize

@ -27,6 +27,7 @@ class Animation {
var ba = this.getBoneAnimator(group)
var kfs = data.bones[key]
if (kfs && ba) {
ba.keyframes.length = 0;
kfs.forEach(kf_data => {
var kf = new Keyframe(kf_data)
ba.pushKeyframe(kf)
@ -98,8 +99,10 @@ class Animation {
rename() {
var scope = this;
Blockbench.textPrompt('message.rename_animation', this.name, function(name) {
if (name) {
if (name && name !== scope.name) {
Undo.initEdit({animations: [scope]})
scope.name = name
Undo.finishEdit('rename animation')
}
})
return this;
@ -107,8 +110,10 @@ class Animation {
editUpdateVariable() {
var scope = this;
Blockbench.textPrompt('message.animation_update_var', this.anim_time_update, function(name) {
if (name) {
if (name && name !== scope.anim_time_update) {
Undo.initEdit({animations: [this]})
scope.anim_time_update = name
Undo.finishEdit('change animation variable')
}
})
return this;
@ -140,18 +145,31 @@ class Animation {
}
Blockbench.dispatchEvent('display_animation_frame')
}
add() {
add(undo) {
if (undo) {
Undo.initEdit({animations: []})
}
if (!Animator.animations.includes(this)) {
Animator.animations.push(this)
}
if (undo) {
this.select()
Undo.finishEdit('add animation', {animations: [this]})
}
return this;
}
remove() {
remove(undo) {
if (undo) {
Undo.initEdit({animations: [this]})
}
if (Animator.selected === this) {
Animator.selected = false
}
Animator.animations.remove(this)
Blockbench.dispatchEvent('remove_animation', {animation: this})
if (undo) {
Undo.finishEdit('remove animation', {animation: null})
}
Blockbench.dispatchEvent('remove_animation', {animations: [this]})
return this;
}
getMaxLength() {
@ -186,7 +204,7 @@ class Animation {
animation.editUpdateVariable()
}},
{name: 'generic.delete', icon: 'delete', click: function(animation) {
animation.remove()
animation.remove(true)
}},
/*
rename
@ -247,6 +265,7 @@ class BoneAnimator {
}
this.keyframes.push(keyframe)
keyframe.parent = this;
TickUpdates.keyframes = true;
return keyframe;
}
pushKeyframe(keyframe) {
@ -289,9 +308,11 @@ class BoneAnimator {
displayScale(arr) {
var bone = this.group.mesh
if (arr) {
bone.scale.x = bone.scale.y = bone.scale.z = arr[0] ? arr[0] : 0.00001
bone.scale.x = arr[0] ? arr[0] : 0.00001;
bone.scale.y = arr[1] ? arr[1] : 0.00001;
bone.scale.z = arr[2] ? arr[2] : 0.00001;
} else {
bone.scale.x = bone.scale.y = bone.scale.z = 1
bone.scale.x = bone.scale.y = bone.scale.z = 1;
}
return this;
}
@ -329,12 +350,10 @@ class BoneAnimator {
} else {
let alpha = Math.lerp(before.time, after.time, time)
result = [
before.getLerp(after, 'x', alpha, allow_expression)
before.getLerp(after, 'x', alpha, allow_expression),
before.getLerp(after, 'y', alpha, allow_expression),
before.getLerp(after, 'z', alpha, allow_expression)
]
if (before.channel !== 'scale') {
result[1] = before.getLerp(after, 'y', alpha, allow_expression)
result[2] = before.getLerp(after, 'z', alpha, allow_expression)
}
if (before.isQuaternion && after.isQuaternion) {
result[3] = before.getLerp(after, 'q', alpha, allow_expression)
}
@ -343,12 +362,10 @@ class BoneAnimator {
let keyframe = result
let method = allow_expression ? 'get' : 'calc'
result = [
keyframe[method]('x')
keyframe[method]('x'),
keyframe[method]('y'),
keyframe[method]('z')
]
if (keyframe.channel !== 'scale') {
result[1] = keyframe[method]('y')
result[2] = keyframe[method]('z')
}
if (keyframe.isQuaternion) {
result[3] = keyframe[method]('w')
}
@ -405,7 +422,7 @@ class BoneAnimator {
if (this.group && this.group.parent && this.group.parent !== 'root') {
this.group.parent.openUp()
}
Vue.nextTick(Timeline.update)
TickUpdates.keyframes = true;
return this;
}
}
@ -424,8 +441,8 @@ class Keyframe {
this.uuid = guid()
if (typeof data === 'object') {
this.extend(data)
if (this.channel === 'scale' && data.x === undefined) {
this.x = 1
if (this.channel === 'scale' && data.x == undefined && data.y == undefined && data.z == undefined) {
this.x = this.y = this.z = 1;
}
}
}
@ -507,9 +524,6 @@ class Keyframe {
}
}
getArray() {
if (this.channel === 'scale') {
return this.get('x')
}
var arr = [
this.get('x'),
this.get('y'),
@ -659,6 +673,7 @@ class Keyframe {
updateKeyframeSelection()
}
},
'copy',
{name: 'generic.delete', icon: 'delete', click: function(keyframe) {
keyframe.select({shiftKey: true})
removeSelectedKeyframes()
@ -696,8 +711,7 @@ function updateKeyframeSelection() {
if (Timeline.selected.length && !multi_channel) {
var first = Timeline.selected[0]
$('#keyframe_type_label').text(tl('panel.keyframe.type', [tl('timeline.'+first.channel)] ))
$('#keyframe_bar_x').show()
$('#keyframe_bar_y, #keyframe_bar_z').toggle(first.channel !== 'scale')
$('#keyframe_bar_x, #keyframe_bar_y, #keyframe_bar_z').show()
$('#keyframe_bar_w').toggle(first.channel === 'rotation' && first.isQuaternion)
var values = [
@ -747,6 +761,7 @@ function removeSelectedKeyframes() {
}
}
updateKeyframeSelection()
Animator.preview()
Undo.finishEdit('remove keyframes')
}
@ -779,7 +794,7 @@ const Animator = {
if (!Timeline.is_setup) {
Timeline.setup()
}
Timeline.update()
TickUpdates.keyframes = true;
if (outlines.children.length) {
outlines.children.length = 0
Canvas.updateAllPositions()
@ -859,7 +874,7 @@ const Animator = {
},
buildFile: function(options) {
if (typeof options !== 'object') {
options = {}
options = false
}
var animations = {}
Animator.animations.forEach(function(a) {
@ -935,7 +950,7 @@ const Timeline = {
setTimecode: function(time) {
let m = Math.floor(time/60)
let s = Math.floor(time%60)
let f = Math.floor((time%1) * 30)
let f = Math.floor((time%1) * 100)
if ((s+'').length === 1) {s = '0'+s}
if ((f+'').length === 1) {f = '0'+f}
$('#timeline_corner').text(m + ':' + s + ':' + f)
@ -1036,7 +1051,7 @@ const Timeline = {
var seconds
= times[0]*60
+ limitNumber(times[1], 0, 59)
+ limitNumber(times[2]/30, 0, 29)
+ limitNumber(times[2]/100, 0, 99)
if (Math.abs(seconds-Timeline.second) > 1e-3 ) {
Timeline.setTime(seconds, true)
if (Animator.selected) {
@ -1045,25 +1060,47 @@ const Timeline = {
}
})
$('#timeline_inner').on('mousewheel', function() {
if (event.ctrlKey) {
var offset = 1 - event.deltaY/600
Timeline.vue._data.size = limitNumber(Timeline.vue._data.size * offset, 10, 1000)
this.scrollLeft *= offset
let l = (event.offsetX / this.clientWidth) * 500 * (event.deltaY<0?1:-0.2)
this.scrollLeft += l
} else {
this.scrollLeft += event.deltaY/2
}
Timeline.updateSize()
event.preventDefault();
});
BarItems.slider_animation_speed.update()
Timeline.is_setup = true
Timeline.setTime(0)
},
update: function() {
//Draggable
$('#timeline_inner .keyframe').draggable({
$('#timeline_inner .keyframe:not(.ui-draggable)').draggable({
axis: 'x',
distance: 10,
distance: 4,
start: function(event, ui) {
Undo.initEdit({keyframes: Timeline.keyframes, keep_saved: true})
var id = $(ui.helper).attr('id')
var clicked = Timeline.vue._data.keyframes.findInArray('uuid', id)
if (clicked) {
clicked.select()
}
var i = 0;
while (i < Timeline.vue._data.keyframes.length) {
for (var i = 0; i < Timeline.vue._data.keyframes.length; i++) {
var kf = Timeline.vue._data.keyframes[i]
if (kf.uuid === id || kf.selected) {
if (kf.selected) {
kf.time_before = kf.time
}
i++;
}
},
drag: function(event, ui) {
@ -1102,6 +1139,7 @@ const Timeline = {
Animator.preview()
},
stop: function(event, ui) {
/*
var id = $(ui.helper).attr('id')
var i = 0;
while (i < Timeline.vue._data.keyframes.length) {
@ -1110,7 +1148,7 @@ const Timeline = {
kf.dragging = true
}
i++;
}
}*/
Undo.finishEdit('drag keyframes')
}
})
@ -1200,7 +1238,6 @@ const Timeline = {
var kf = bone.addKeyframe(false, Timeline.second, channel?channel:'rotation')
kf.select()
Undo.finishEdit('add_keyframe')
Vue.nextTick(Timeline.update)
},
showMenu: function(event) {
if (event.target.id === 'timeline_inner') {
@ -1220,12 +1257,12 @@ const Timeline = {
Undo.initEdit({keyframes: bone.keyframes, keep_saved: true})
var kf = bone.addKeyframe(false, Math.round(time*30)/30, row === 2 ? 'scale' : (row === 1 ? 'position' : 'rotation'))
kf.select().callMarker()
Vue.nextTick(Timeline.update)
Undo.finishEdit('add_keyframe')
} else {
Blockbench.showQuickMessage('message.no_bone_selected')
}
}}
}},
'paste'
])
}
@ -1257,7 +1294,7 @@ BARS.defineActions(function() {
click: function () {
var animation = new Animation({
name: 'animation.' + (Project.parent||'model') + '.new'
}).add().select()
}).add(true).rename()
}
})
@ -1408,7 +1445,27 @@ BARS.defineActions(function() {
Undo.initEdit({keyframes: Timeline.selected, keep_saved: true})
},
onAfter: function() {
Undo.finishEdit('edit keyframe')
Undo.finishEdit('move keyframes')
}
})
new Action({
id: 'reset_keyframe',
icon: 'replay',
category: 'animation',
condition: () => Animator.open,
click: function () {
Undo.initEdit({keyframes: Timeline.selected, keep_saved: true})
Timeline.selected.forEach((kf) => {
var n = kf.channel === 'scale' ? 1 : 0;
kf.extend({
x: n,
y: n,
z: n,
w: kf.isQuaternion ? 0 : undefined
})
})
Undo.finishEdit('reset keyframes')
Animator.preview()
}
})

211
js/api.js

@ -8,6 +8,7 @@ class API {
this.selection = selected;
this.flags = []
this.drag_handlers = {}
this.events = {}
this.entity_mode = false
if (isApp) {
this.platform = process.platform
@ -25,6 +26,7 @@ class API {
Undo.finishEdit()
}
reload() {
localStorage.removeItem('backup_model')
if (isApp) {
preventClosing = false
Blockbench.flags.push('allow_reload')
@ -138,7 +140,7 @@ class API {
var buttons = []
options.buttons.forEach(function(b, i) {
var btn = $('<button type="button" class="large">'+b+'</button>')
var btn = $('<button type="button">'+b+'</button>')
btn.click(function(e) {
hideDialog()
setTimeout(function() {
@ -437,8 +439,18 @@ class API {
} else {
options.content = nativeImage.createFromPath(options.content).toPNG()
}
}
if (options.custom_writer) {
} else if (options.savetype === 'zip') {
var fileReader = new FileReader();
fileReader.onload = function(event) {
var buffer = Buffer.from(new Uint8Array(this.result));
fs.writeFileSync(file_path, buffer)
if (cb) {
cb(file_path)
}
};
fileReader.readAsArrayBuffer(options.content);
} else if (options.custom_writer) {
options.custom_writer(options.content, file_path)
} else {
fs.writeFileSync(file_path, options.content)
@ -469,35 +481,27 @@ class API {
return this.flags[flag]
}
//Events
dispatchEvent(event_name, event) {
if (!this.listeners) {
return;
}
var i = 0;
while (i < this.listeners.length) {
if (this.listeners[i].name === event_name) {
this.listeners[i].callback(event)
dispatchEvent(event_name, data) {
var list = this.events[event_name]
if (!list) return;
for (var i = 0; i < list.length; i++) {
if (typeof list[i] === 'function') {
list[i](data)
}
i++;
}
}
addListener(event_name, cb) {
if (!this.listeners) {
this.listeners = []
if (!this.events[event_name]) {
this.events[event_name] = []
}
this.listeners.push({name: event_name, callback: cb})
this.events[event_name].safePush(cb)
}
on(event_name, cb) {
return Blockbench.addListener(event_name, cb)
}
removeListener(event_name, cb) {
if (!this.listeners) {
return;
}
var i = 0;
while (i < this.listeners.length) {
if (this.listeners[i].name === event_name && this.listeners[i].callback === cb) {
this.listeners.splice(i, 1)
}
i++;
}
if (!this.events[event_name]) return;
this.events[event_name].remove(cb);
}
//File Drag
addDragHandler(id, options, cb) {
@ -525,11 +529,13 @@ function Dialog(settings) {
var scope = this;
this.title = settings.title
this.lines = settings.lines
this.form = settings.form
this.id = settings.id
this.width = settings.width
this.fadeTime = settings.fadeTime
this.draggable = settings.draggable
this.singleButton = settings.singleButton
this.buttons = settings.buttons
if (!parseInt(settings.fadeTime)) this.fadeTime = 200
@ -559,50 +565,149 @@ function Dialog(settings) {
$(this.object).find('.cancel_btn:not([disabled])').click()
}
this.show = function() {
var jq_dialog = $('<div class="dialog paddinged" style="width: auto;" id="'+scope.id+'"><h2 class="dialog_handle">'+scope.title+'</h2></div>')
var jq_dialog = $(`<div class="dialog paddinged" style="width: auto;" id="${scope.id}"><h2 class="dialog_handle">${tl(scope.title)}</h2></div>`)
scope.object = jq_dialog.get(0)
var max_label_width = 0;
scope.lines.forEach(function(l) {
if (typeof l === 'object' && (l.label || l.widget)) {
if (scope.lines) {
scope.lines.forEach(function(l) {
if (typeof l === 'object' && (l.label || l.widget)) {
var bar = $('<div class="dialog_bar"></div>')
if (l.label) {
bar.append('<label class="name_space_left">'+tl(l.label)+(l.nocolon?'':':')+'</label>')
max_label_width = Math.max(getStringWidth(tl(l.label)), max_label_width)
}
if (l.node) {
bar.append(l.node)
} else if (l.widget) {
var widget = l.widget
if (typeof l.widget === 'string') {
widget = BarItems[l.widget]
} else if (typeof l.widget === 'function') {
widget = l.widget()
var bar = $('<div class="dialog_bar"></div>')
if (l.label) {
bar.append('<label class="name_space_left">'+tl(l.label)+(l.nocolon?'':':')+'</label>')
max_label_width = Math.max(getStringWidth(tl(l.label)), max_label_width)
}
bar.append(widget.getNode())
max_label_width = Math.max(getStringWidth(widget.name), max_label_width)
if (l.node) {
bar.append(l.node)
} else if (l.widget) {
var widget = l.widget
if (typeof l.widget === 'string') {
widget = BarItems[l.widget]
} else if (typeof l.widget === 'function') {
widget = l.widget()
}
bar.append(widget.getNode())
max_label_width = Math.max(getStringWidth(widget.name), max_label_width)
}
jq_dialog.append(bar)
} else {
jq_dialog.append(l)
}
jq_dialog.append(bar)
} else {
jq_dialog.append(l)
}
})
if (max_label_width) {
document.styleSheets[0].insertRule('.dialog#'+this.id+' .dialog_bar label {width: '+(max_label_width+14)+'px}')
})
}
if (this.singleButton) {
if (scope.form) {
for (var form_id in scope.form) {
var data = scope.form[form_id]
if (data && Condition(data.condition)) {
var bar = $('<div class="dialog_bar"></div>')
if (data.label) {
bar.append(`<label class="name_space_left" for="${form_id}">${tl(data.label)+(data.nocolon?'':':')}</label>`)
max_label_width = Math.max(getStringWidth(tl(data.label)), max_label_width)
}
/*
type: +text
label
placeholder
*/
switch (data.type) {
default:
bar.append(`<input class="dark_bordered half" type="text" id="${form_id}" value="${data.value||''}" placeholder="${data.placeholder||''}">`)
break;
case 'textarea':
bar.append(`<textarea style="height: ${data.height||150}px;" id="${form_id}"></textarea>`)
break;
case 'text':
bar.append(`<p>${tl(data.text)}</p>`)
bar.addClass('small_text')
break;
case 'number':
bar.append(`<input class="dark_bordered half" type="number" id="${form_id}" value="${data.value||0}" min="${data.min}" max="${data.max}" step="${data.step||1}">`)
break;
case 'color':
if (!data.colorpicker) {
data.colorpicker = new ColorPicker({
id: 'cp_'+form_id,
label: false,
private: true
})
}
bar.append(data.colorpicker.getNode())
break;
case 'checkbox':
bar.append(`<input type="checkbox" id="${form_id}"${data.value ? ' checked' : ''}>`)
break;
}
if (data.readonly) {
bar.find('input').attr('readonly', 'readonly')
}
jq_dialog.append(bar)
}
}
}
if (max_label_width) {
document.styleSheets[0].insertRule('.dialog#'+this.id+' .dialog_bar label {width: '+(max_label_width+8)+'px}')
}
if (this.buttons) {
var buttons = []
scope.buttons.forEach(function(b, i) {
var btn = $('<button type="button">'+b+'</button>')
buttons.push(btn)
})
buttons[scope.confirmIndex||0].addClass('confirm_btn')
buttons[scope.cancelIndex||1].addClass('cancel_btn')
jq_dialog.append($('<div class="dialog_bar button_bar"></div>').append(buttons))
} else if (this.singleButton) {
jq_dialog.append('<div class="dialog_bar">' +
'<button type="button" class="large cancel_btn confirm_btn"'+ (this.confirmEnabled ? '' : ' disabled') +'>'+tl('dialog.close')+'</button>' +
'</div>')
} else {
jq_dialog.append(['<div class="dialog_bar">',
'<button type="button" class="large confirm_btn"'+ (this.confirmEnabled ? '' : ' disabled') +'>'+tl('dialog.confirm')+'</button>',
'<button type="button" class="large cancel_btn"'+ (this.cancelEnabled ? '' : ' disabled') +'>'+tl('dialog.cancel')+'</button>',
'</div>'].join(''))
}
jq_dialog.append('<div id="dialog_close_button" onclick="$(\'.dialog#\'+open_dialog).find(\'.cancel_btn:not([disabled])\').click()"><i class="material-icons">clear</i></div>')
$(this.object).find('.confirm_btn').click(this.onConfirm)
$(this.object).find('.cancel_btn').click(this.onCancel)
var confirmFn = function(e) {
var result = {}
if (scope.form) {
for (var form_id in scope.form) {
var data = scope.form[form_id]
switch (data.type) {
default:
result[form_id] = jq_dialog.find('input#'+form_id).val()
break;
case 'text': break;
case 'textarea':
result[form_id] = jq_dialog.find('textarea#'+form_id).val()
break;
case 'number':
result[form_id] = parseFloat(jq_dialog.find('input#'+form_id).val())||0
break;
case 'color':
result[form_id] = data.colorpicker.get();
break;
case 'checkbox':
result[form_id] = jq_dialog.find('input#'+form_id).is(':checked')
break;
}
}
}
scope.onConfirm(result, e)
}
confirmFn.bind(this)
$(this.object).find('.confirm_btn').click(confirmFn)
$(this.object).find('.cancel_btn').click(() => {this.onCancel()})
//Draggable
if (this.draggable !== false) {
jq_dialog.addClass('draggable')

@ -14,11 +14,8 @@ var dialog_win = null,
recent_projects= undefined;
$(document).ready(function() {
if (electron.process.argv.length >= 2) {
if (electron.process.argv[1].substr(-5) == '.json') {
readFile(electron.process.argv[1], true)
}
}
//Setup
$('.open-in-browser').click((event) => {
event.preventDefault();
shell.openExternal(event.target.href);
@ -33,6 +30,36 @@ $(document).ready(function() {
if (__dirname.includes('C:\\xampp\\htdocs\\blockbench')) {
Blockbench.addFlag('dev')
}
//Load Model
var model_loaded = false
if (electron.process.argv.length >= 2) {
var extension = pathToExtension(electron.process.argv[1])
if (['json', 'bbmodel', 'jem', 'jpm'].includes(extension)) {
Blockbench.read([electron.process.argv[1]], {}, (files) => {
loadModel(files[0].content, files[0].path || files[0].path)
addRecentProject({name: pathToName(files[0].path, 'mobs_id'), path: files[0].path})
model_loaded = true
})
}
}
if (!model_loaded && localStorage.getItem('backup_model') && !currentwindow.webContents.second_instance) {
var backup_model = localStorage.getItem('backup_model')
localStorage.removeItem('backup_model')
Blockbench.showMessageBox({
translateKey: 'recover_backup',
icon: 'fa-archive',
buttons: [tl('dialog.continue'), tl('dialog.cancel')],
confirm: 0,
cancel: 1
}, function(result) {
if (result === 0) {
loadModel(backup_model, 'backup.bbmodel')
}
})
}
});
(function() {
console.log('Electron '+process.versions.electron+', Node '+process.versions.node)
@ -44,7 +71,7 @@ function getLatestVersion(init) {
$.getJSON('https://raw.githubusercontent.com/JannisX11/blockbench/master/package.json', (data) => {
if (data.version) {
latest_version = data.version
if (compareVersions(latest_version, appVersion) && init === true) {
if (compareVersions(latest_version, appVersion) && init === true && !open_dialog) {
Blockbench.showMessageBox({
translateKey: 'update_notification',
@ -93,7 +120,7 @@ function addRecentProject(data) {
icon_id = 2;
}
recent_projects.push({name: data.name, path: data.path, icon_id})
if (recent_projects.length > 8) {
if (recent_projects.length > 12) {
recent_projects.shift()
}
updateRecentProjects()
@ -187,7 +214,7 @@ function changeImageEditor(texture) {
var dialog = new Dialog({
title: tl('message.image_editor.title'),
id: 'image_editor',
lines: ['<div class="dialog_bar"><select class="dark_bordered input_wide">'+
lines: ['<div class="dialog_bar"><select class="input_wide">'+
'<option id="ps">Photoshop</option>'+
'<option id="gimp">Gimp</option>'+
'<option id="pdn">Paint.NET</option>'+
@ -329,18 +356,16 @@ function findEntityTexture(mob, return_path) {
} else if (return_path === 'raw') {
return ['entity', ...path.split('/')].join(osfs)
} else {
if (fs.existsSync(texture_path + '.png')) {
var texture = new Texture({keep_size: true}).fromPath(texture_path + '.png').add()
} else if (fs.existsSync(texture_path + '.tga')) {
var texture = new Texture({keep_size: true}).fromPath(texture_path + '.tga').add()
} else if (settings.default_path && settings.default_path.value) {
texture_path = settings.default_path.value + osfs + 'entity' + osfs + path.split('/').join(osfs)
if (fs.existsSync(texture_path + '.png')) {
var texture = new Texture({keep_size: true}).fromPath(texture_path + '.png').add()
} else if (fs.existsSync(texture_path + '.tga')) {
var texture = new Texture({keep_size: true}).fromPath(texture_path + '.tga').add()
function tryItWith(extension) {
if (fs.existsSync(texture_path+'.'+extension)) {
var texture = new Texture({keep_size: true}).fromPath(texture_path+'.'+extension).add()
}
}
if (!tryItWith('png') && !tryItWith('tga')) {
if (settings.default_path && settings.default_path.value) {
texture_path = settings.default_path.value + osfs + 'entity' + osfs + path.split('/').join(osfs)
tryItWith('png') || tryItWith('tga')
}
}
}
@ -378,6 +403,11 @@ function saveFile(props) {
BarItems.export_entity.trigger()
}
}
if (Blockbench.entity_mode && Prop.animation_path) {
Blockbench.writeFile(Prop.animation_path, {
content: autoStringify(Animator.buildFile())
})
}
}
function writeFileEntity(content, filepath) {
@ -388,15 +418,8 @@ function writeFileEntity(content, filepath) {
try {
data = fs.readFileSync(filepath, 'utf-8')
} catch (err) {}
var obj = {}
if (content.bones && content.bones.length) {
var has_parents = false;
for (var i = 0; i < content.bones.length && !has_parents; i++) {
if (content.bones[i].parent) has_parents = true;
}
if (has_parents) {
obj.format_version = '1.8.0'
}
var obj = {
format_version: '1.10.0'
}
if (data) {
try {
@ -547,6 +570,7 @@ function createBackup(init) {
if (init || elements.length === 0) return;
var model = buildBBModel()
localStorage.setItem('backup_model', model)
var file_name = 'backup_'+d.getDate()+'.'+(d.getMonth()+1)+'.'+(d.getYear()-100)+'_'+d.getHours()+'.'+d.getMinutes()
var file_path = folder_path+osfs+file_name+'.bbmodel'
@ -556,17 +580,6 @@ function createBackup(init) {
}
})
}
//Zoom
function setZoomLevel(mode) {
switch (mode) {
case 'in': Prop.zoom += 5; break;
case 'out': Prop.zoom -= 5; break;
case 'reset': Prop.zoom = 100; break;
}
var level = (Prop.zoom - 100) / 12
currentwindow.webContents.setZoomLevel(level)
resizeWindow()
}
//Close
window.onbeforeunload = function() {
if (preventClosing === true) {
@ -618,6 +631,7 @@ function showSaveDialog(close) {
function closeBlockbenchWindow() {
preventClosing = false;
Blockbench.dispatchEvent('before_closing')
localStorage.removeItem('backup_model')
if (!Blockbench.hasFlag('update_restart')) {
return currentwindow.close();

@ -24,6 +24,8 @@ const Prop = {
fps: 0,
zoom: 100,
progress: 0,
session: false,
connections: 0,
facing: 'north'
}
const Project = {
@ -140,7 +142,6 @@ function onVueSetup(func) {
}
onVueSetup.funcs.push(func)
}
function canvasGridSize(shift, ctrl) {
if (!shift && !ctrl) {
return 16 / limitNumber(settings.edit_size.value, 1, 1024)
@ -156,7 +157,6 @@ function canvasGridSize(shift, ctrl) {
return 16 / limitNumber(settings.shift_size.value, 1, 1024)
}
}
function updateNslideValues() {
//if (!selected.length && (!Blockbench.entity_mode || !selected_group)) return;
@ -309,18 +309,6 @@ function unselectAll() {
})
updateSelection()
}
function invertSelection() {
elements.forEach(function(s) {
if (selected.includes(s)) {
selected.splice(selected.indexOf(s), 1)
} else {
selected.push(s)
}
})
if (selected_group) selected_group.unselect()
updateSelection()
Blockbench.dispatchEvent('invert_selection')
}
function createSelection() {
if ($('#selgen_new').is(':checked')) {
selected.length = 0
@ -359,7 +347,6 @@ class Mode extends KeybindItem {
this.condition = data.condition;
this.onSelect = data.onSelect;
this.onUnselect = data.onUnselect;
this.category = data.category;
Modes.options[this.id] = this;
}
select() {
@ -407,17 +394,20 @@ BARS.defineActions(function() {
new Mode({
id: 'edit',
default_tool: 'move_tool',
category: 'navigate',
keybind: new Keybind({key: 49})
})
new Mode({
id: 'paint',
default_tool: 'brush_tool',
category: 'navigate',
keybind: new Keybind({key: 50})
})
new Mode({
id: 'display',
selectCubes: false,
default_tool: 'move_tool',
category: 'navigate',
keybind: new Keybind({key: 51}),
condition: () => !Blockbench.entity_mode,
onSelect: () => {
@ -430,6 +420,7 @@ BARS.defineActions(function() {
new Mode({
id: 'animate',
default_tool: 'move_tool',
category: 'navigate',
keybind: new Keybind({key: 51}),
condition: () => Blockbench.entity_mode,
onSelect: () => {
@ -440,6 +431,17 @@ BARS.defineActions(function() {
}
})
})
//Backup
setInterval(function() {
if (TreeElements.length || textures.length) {
try {
var model = buildBBModel()
localStorage.setItem('backup_model', model)
} catch (err) {
console.log('Unable to create backup. ', err)
}
}
}, 1e3*30)
//Misc
const TickUpdates = {
Run: function() {
@ -451,6 +453,18 @@ const TickUpdates = {
delete TickUpdates.selection;
updateSelection()
}
if (TickUpdates.main_uv) {
delete TickUpdates.main_uv;
main_uv.loadData()
}
if (TickUpdates.texture_list) {
delete TickUpdates.texture_list;
loadTextureDraggable();
}
if (TickUpdates.keyframes) {
delete TickUpdates.keyframes;
Vue.nextTick(Timeline.update)
}
}
}
const Screencam = {
@ -524,6 +538,7 @@ const Screencam = {
if (!options.length) {
options.length = 1000
}
var preview = quad_previews.current;
var interval = options.fps ? (1000/options.fps) : 100
var gif = new GIF({
repeat: options.repeat,
@ -532,6 +547,11 @@ const Screencam = {
})
var frame_count = (options.length/interval)
if (options.turnspeed) {
preview.controls.autoRotate = true;
preview.controls.autoRotateSpeed = options.turnspeed;
}
gif.on('finished', blob => {
var reader = new FileReader()
reader.onload = () => {
@ -550,7 +570,7 @@ const Screencam = {
var frames = 0;
var loop = setInterval(() => {
var img = new Image()
img.src = quad_previews.current.canvas.toDataURL()
img.src = preview.canvas.toDataURL()
img.onload = () => {
gif.addFrame(img, {delay: interval})
}
@ -567,6 +587,9 @@ const Screencam = {
if (Animator.open && Timeline.playing) {
Timeline.pause()
}
if (options.turnspeed) {
preview.controls.autoRotate = false;
}
}, options.length)
}
}
@ -597,7 +620,7 @@ const Clipbench = {
Clipbench.setCubes(selected)
}
if (cut) {
deleteCubes()
BarItems.delete.trigger()
}
}
},
@ -637,7 +660,7 @@ const Clipbench = {
var img = clipboard.readImage()
if (img) {
var dataUrl = img.toDataURL()
var texture = new Texture({name: 'pasted', folder: 'blocks' }).fromDataURL(dataUrl).add().fillParticle()
var texture = new Texture({name: 'pasted', folder: 'blocks' }).fromDataURL(dataUrl).fillParticle().add(true)
setTimeout(function() {
texture.openMenu()
},40)
@ -756,6 +779,13 @@ const Clipbench = {
if (isApp) {
clipboard.writeHTML(JSON.stringify({type: 'keyframes', content: Clipbench.keyframes}))
}
},
setText: function(text) {
if (isApp) {
clipboard.writeText(text)
} else {
document.execCommand('copy')
}
}
}

@ -150,7 +150,7 @@ class refModel {
case 'bow':
this.onload = function() {
var side = display_slot.includes('left') ? -1 : 1;
setDisplayArea(side*5.4, -5.6, 24.7, side*64, side*-25, side*55, 1,1,1)
setDisplayArea(side*4.2, -4.9, 25, -20, -19, -8, 1,1,1)
}
break;
}
@ -1302,7 +1302,6 @@ enterDisplaySettings = function() { //Enterung Display Setting Mode, changes th
buildGrid()
setShading()
DisplayMode.loadThirdRight()
Canvas.updateRenderSides()
display_area.updateMatrixWorld()
display_base.updateMatrixWorld()
@ -1482,6 +1481,7 @@ function loadDisp(key) { //Loads The Menu and slider values, common for all Radi
DisplayMode.vue._data.slot = display[key]
DisplayMode.slot = display[key]
DisplayMode.updateDisplayBase()
Canvas.updateRenderSides()
}
DisplayMode.loadThirdRight = function() { //Loader

239
js/edit_sessions.js Normal file

@ -0,0 +1,239 @@
const EditSession = {
active: false,
hosting: false,
BBKey: '1h3sq3hoj6vfkh',
start: function() {
if (EditSession.active) return;
EditSession.hosting = true;
Prop.session = true;
EditSession.setState(true);
var peer = EditSession.peer = new Peer({key: '1h3sq3hoj6vfkh'});
EditSession.username = $('#edit_session_username').val()
peer.on('open', (token) => {
$('#edit_session_token').val(token)
EditSession.token = token;
Clipbench.setText(token)
Blockbench.dispatchEvent('create_session', {peer, token})
})
peer.on('connection', (conn) => {
EditSession.initConnection(conn)
Prop.connections = Object.keys(peer.connections).length
console.log(tl('edit_session.joined', [conn.metadata.username]))
Blockbench.showQuickMessage(tl('edit_session.joined', [conn.metadata.username]))
//New Login
var model = buildBBModel({uuids: true, bitmaps: true, history: true})
conn.on('open', function() {
Blockbench.dispatchEvent('user_joins_session', {conn})
conn.send({
type: 'init_model',
fromHost: EditSession.hosting,
sender: EditSession.peer.id,
data: model
})
})
conn.on('close', function() {
Blockbench.dispatchEvent('user_leaves_session', {conn})
Blockbench.showQuickMessage(tl('edit_session.left', [conn.metadata.username]))
delete peer.connections[conn.peer]
Prop.connections = Object.keys(peer.connections).length
})
})
},
join: function() {
if (EditSession.active) return;
EditSession.hosting = false;
EditSession.peer = new Peer({key: '1h3sq3hoj6vfkh'});
var token = $('#edit_session_token').val()
var username = $('#edit_session_username').val()
if (!token || !EditSession._matchToken(token)) {
Blockbench.showMessageBox({
translateKey: 'invalid_session',
icon: 'cloud_off',
buttons: [tl('dialog.ok')],
}, result => {
showDialog('edit_sessions');
})
}
EditSession.token = token;
var conn = EditSession.peer.connect(token, {metadata: {username: username}});
conn.on('error', (a, b) => {
Blockbench.showMessageBox({
translateKey: 'invalid_session',
icon: 'cloud_off',
buttons: [tl('dialog.ok')],
}, result => {
showDialog('edit_sessions');
})
})
conn.on('open', () => {
hideDialog()
EditSession.host = conn;
EditSession.setState(true);
EditSession.initConnection(conn)
Blockbench.dispatchEvent('join_session', {conn})
})
},
quit: function() {
Blockbench.dispatchEvent('quit_session', {})
if (EditSession.hosting) {
EditSession.sendAll('command', 'quit_session')
} else {
EditSession.host.close()
}
setTimeout(function() {
EditSession.setState(false)
EditSession.peer.destroy()
Prop.session = false;
Prop.connections = 0;
Blockbench.showQuickMessage('edit_session.quit_session', 1500)
}, 400)
},
setState: function(active) {
EditSession.active = active;
$('#edit_session_username, #edit_session_token').attr('readonly', active)
if (active) {
$('.edit_session_inactive').hide()
$('.edit_session_active').show()
$('#edit_session_status').text(EditSession.hosting ? tl('edit_session.hosting') : tl('edit_session.connected'))
$('#edit_session_copy_button .tooltip').text(tl('action.copy'))
} else {
EditSession.hosting = false;
$('.edit_session_active').hide()
$('.edit_session_inactive').show()
$('#edit_session_copy_button .tooltip').text(tl('action.paste'))
$('#edit_session_token').val('')
}
},
dialog: function() {
showDialog('edit_sessions');
if (!EditSession.active && isApp) {
var token = clipboard.readText()
if (EditSession._matchToken(token)) {
$('#edit_session_token').val(token)
}
var username = process.env.USERNAME
if (username) {
$('#edit_session_username').val(username)
}
}
},
copyToken: function() {
var input = $('#edit_session_token')
if (EditSession.active) {
input.focus()
document.execCommand('selectAll')
document.execCommand('copy')
} else {
if (isApp) {
var token = clipboard.readText()
if (EditSession._matchToken(token)) {
$('#edit_session_token').val(token)
}
} else {
input.focus()
document.execCommand('selectAll')
document.execCommand('paste')
}
}
},
initNewModel: function(force) {
if (EditSession.active && EditSession.hosting) {
var model = buildBBModel({uuids: true, bitmaps: true, raw: true})
if (force) {
model.flag = 'force'
}
EditSession.sendAll('init_model', JSON.stringify(model))
}
},
initConnection: function(conn) {
conn.on('data', EditSession.receiveData)
},
sendAll: function(type, data) {
var tag = {type, data}
Blockbench.dispatchEvent('send_session_data', tag)
for (var key in EditSession.peer.connections) {
var conns = EditSession.peer.connections[key];
conns.forEach(conn => {
conn.send({
type: tag.type,
fromHost: EditSession.hosting,
sender: EditSession.peer.id,
data: tag.data
});
})
}
if (Blockbench.hasFlag('log_session')) {
console.log('Sent Data:', type, data)
}
},
sendEdit: function(entry) {
var new_entry = {
before: omitKeys(entry.before, ['aspects']),
post: omitKeys(entry.post, ['aspects']),
save_history: entry.save_history,
action: entry.action
}
EditSession.sendAll('edit', JSON.stringify(new_entry))
},
receiveData: function(tag) {
if (Blockbench.hasFlag('log_session')) {
console.log('Received Data:', tag)
}
if (EditSession.hosting && !tag.hostOnly && Object.keys(EditSession.peer.connections).length > 1) {
//Redistribute
for (var id in EditSession.peer.connections) {
if (id !== tag.sender) {
EditSession.peer.connections[id][0].send(tag);
}
}
}
var data = tag.data;
if (typeof data === 'string' && (data.includes('"') || data.includes('['))) {
try {
data = tag.data = JSON.parse(data)
} catch (err) {
console.log(err)
return;
}
}
Blockbench.dispatchEvent('receive_session_data', tag)
if (tag.type === 'edit') {
Undo.remoteEdit(data)
} else if (tag.type === 'init_model') {
force = data.flag === 'force';
newProject(false, force)
loadBBModel(data)
} else if (tag.type === 'command') {
switch (data) {
case 'undo': Undo.undo(true); break;
case 'redo': Undo.redo(true); break;
case 'quit_session': EditSession.quit(); break;
}
} else if (tag.type === 'change_project_meta') {
for (var key in data) {
Project = data[key];
}
}
},
_matchToken: function(token) {
return !!(token.length === 16 && token.match(/[a-z0-9]{16}/))
}
}
BARS.defineActions(function() {
new Action({
id: 'edit_session',
icon: 'people',
category: 'blockbench',
click: EditSession.dialog
})
})

@ -13,8 +13,9 @@ var OutlinerButtons = {
}
Undo.initEdit({cubes: obj.forSelected(), outliner: true, selection: true})
obj.forSelected(function(cube) {
cube.remove(true)
cube.remove()
})
updateSelection()
Undo.finishEdit('remove', {cubes: [], outliner: true, selection: true})
}
},
@ -150,8 +151,9 @@ class OutlinerElement {
constructor(uuid) {
this.uuid = uuid || guid()
}
sortInBefore(element) {
sortInBefore(element, index_mod) {
var index = -1;
index_mod = index_mod || 0;
if (element.parent === 'root') {
index = TreeElements.indexOf(element)
@ -169,7 +171,7 @@ class OutlinerElement {
if (index < 0)
arr.push(this)
else {
arr.splice(index, 0, this)
arr.splice(index+index_mod, 0, this)
}
TickUpdates.outliner = true;
@ -217,21 +219,8 @@ class OutlinerElement {
return this;
}
removeFromParent() {
var scope = this;
if (this.parent === 'root') {
TreeElements.forEach(function(s, i) {
if (s === scope) {
TreeElements.splice(i, 1)
}
})
} else if (typeof this.parent === 'object') {
var childArray = this.parent.children
childArray.forEach(function(s, i) {
if (s === scope) {
childArray.splice(i, 1)
}
})
}
this.getParentArray().remove(this);
return this;
}
getParentArray() {
if (this.parent === 'root') {
@ -264,12 +253,14 @@ class OutlinerElement {
$('#cubes_list').animate({
scrollTop: scroll_amount
}, 200);
return this;
}
updateElement() {
var scope = this;
var old_name = this.name;
scope.name = '_&/3%6-7A';
scope.name = old_name;
return this;
}
getDepth() {
var d = 0;
@ -314,6 +305,7 @@ class OutlinerElement {
scope.name = scope.old_name
delete scope.old_name
}
return this;
}
isIconEnabled(btn) {
switch (btn.id) {
@ -598,9 +590,7 @@ class Cube extends OutlinerElement {
}
}
delete Canvas.meshes[this.uuid]
if (selected.includes(this)) {
selected.splice(selected.indexOf(this), 1)
}
selected.remove(this)
elements.splice(this.index, 1)
if (Transformer.dragging) {
outlines.remove(outlines.getObjectByName(this.uuid+'_ghost_outline'))
@ -1161,7 +1151,7 @@ class Group extends OutlinerElement {
//Clear Old Group
if (selected_group) selected_group.unselect()
if (event.shiftKey === true || event.ctrlKey === true) {
if (event.shiftKey !== true && event.ctrlKey !== true) {
selected.length = 0
}
//Select This Group
@ -1293,9 +1283,14 @@ class Group extends OutlinerElement {
Undo.finishEdit('removed_group')
}
}
createUniqueName() {
createUniqueName(group_arr) {
var scope = this;
var others = getAllOutlinerGroups();
if (group_arr && group_arr.length) {
group_arr.forEach(g => {
others.safePush(g)
})
}
var name = this.name.replace(/\d+$/, '');
function check(n) {
for (var i = 0; i < others.length; i++) {
@ -1306,7 +1301,7 @@ class Group extends OutlinerElement {
if (check(this.name)) {
return this.name;
}
for (var num = 2; num < 256; num++) {
for (var num = 2; num < 2e3; num++) {
if (check(name+num)) {
scope.name = name+num;
return scope.name;
@ -1364,6 +1359,7 @@ class Group extends OutlinerElement {
return this;
}
duplicate(destination) {
var copied_groups = [];
function duplicateArray(g1, g2) {
var array = g1.children
var i = 0;
@ -1378,28 +1374,32 @@ class Group extends OutlinerElement {
}
} else {
var copy = array[i].getChildlessCopy()
duplicateArray(array[i], copy)
copy.addTo(g2)
if (destination == 'cache') {
copy.parent = undefined;
} else if (Blockbench.entity_mode) {
copy.createUniqueName()
copy.createUniqueName(copied_groups)
}
copied_groups.push(copy)
duplicateArray(array[i], copy)
}
i++;
}
}
var base_group = this.getChildlessCopy()
if (destination !== 'cache') {
base_group.createUniqueName()
copied_groups.push(base_group)
}
duplicateArray(this, base_group)
base_group.parent = undefined;
if (!destination) {
base_group.addTo(this.parent)
base_group.sortInBefore(this, 1).select()
} else if (destination !== 'cache') {
base_group.addTo(destination)
}
if (destination !== 'cache') {
base_group.createUniqueName()
Canvas.updatePositions()
TickUpdates.outliner = true;
}
@ -1681,21 +1681,11 @@ function parseGroups(array, importGroup, startIndex) {
}
}
//Outliner
function toggleOutlinerOptions(force) {
if (force === undefined) {
force = !$('.panel#outliner').hasClass('more_options')
}
if (force) {
$('.panel#outliner').addClass('more_options')
BarItems.outliner_toggle.setIcon('dns')
} else {
$('.panel#outliner').removeClass('more_options')
BarItems.outliner_toggle.setIcon('view_stream')
}
}
function loadOutlinerDraggable() {
function getOrder(loc, obj) {
if (obj.type === 'group') {
if (!obj) {
return;
} else if (obj.type === 'group') {
if (loc < 8) return -1;
if (loc > 24) return 1;
} else {
@ -1788,15 +1778,17 @@ function loadOutlinerDraggable() {
})
})
}
function collapseAllGroups() {
getAllOutlinerGroups().forEach(function(g) {
g.isOpen = false
var name = g.name
g.name = '_$X0v_'
g.name = name
})
}
function dropOutlinerObjects(item, target, event, order) {
if (item.type === 'group' && target && target.parent) {
var is_parent = false;
function iterate(g) {
if (!(is_parent = g === item) && g.parent.type === 'group') {
iterate(g.parent)
}
}
iterate(target)
if (is_parent) return;
}
if (item.type === 'cube' && selected.includes( item )) {
var items = selected.slice();
} else {
@ -1943,25 +1935,6 @@ function addGroup() {
}
//Misc
function deleteCubes(array) {
Undo.initEdit({cubes: selected, outliner: true, selection: true})
if (selected_group) {
selected_group.remove(true)
return;
}
if (array == undefined) {
array = selected.slice(0)
} else if (array.constructor !== Array) {
array = [array]
} else {
array = array.slice(0)
}
array.forEach(function(s) {
s.remove(false)
})
updateSelection()
Undo.finishEdit('delete')
}
function duplicateCubes() {
Undo.initEdit({cubes: [], outliner: true, selection: true})
selected.forEach(function(obj, i) {
@ -2007,14 +1980,6 @@ function stopRenameCubes(save) {
Blockbench.removeFlag('renaming')
}
}
function sortOutliner() {
Undo.initEdit({outliner: true})
if (TreeElements.length < 1) return;
TreeElements.sort(function(a,b) {
return sort_collator.compare(a.name, b.name)
});
Undo.finishEdit('sort_outliner')
}
function toggleCubeProperty(key) {
var state = selected[0][key]
if (typeof state === 'number') {
@ -2084,7 +2049,15 @@ BARS.defineActions(function() {
category: 'edit',
keybind: new Keybind({key: 115}),
click: function () {
toggleOutlinerOptions()
var state = !$('.panel#outliner').hasClass('more_options')
if (state) {
$('.panel#outliner').addClass('more_options')
BarItems.outliner_toggle.setIcon('dns')
} else {
$('.panel#outliner').removeClass('more_options')
BarItems.outliner_toggle.setIcon('view_stream')
}
}
})
new BarText({
@ -2104,4 +2077,135 @@ BARS.defineActions(function() {
}
}
})
new Action({
id: 'duplicate',
icon: 'content_copy',
category: 'edit',
condition: () => (!display_mode && !Animator.open && (selected.length || selected_group)),
keybind: new Keybind({key: 68, ctrl: true}),
click: function () {
if (selected_group && (selected_group.matchesSelection() || selected.length === 0)) {
var cubes_before = elements.length
Undo.initEdit({outliner: true, cubes: [], selection: true})
var g = selected_group.duplicate()
g.select().isOpen = true;
Undo.finishEdit('duplicate_group', {outliner: true, cubes: elements.slice().slice(cubes_before), selection: true})
} else {
duplicateCubes();
}
}
})
new Action({
id: 'delete',
icon: 'delete',
category: 'edit',
condition: () => (!display_mode && !Animator.open && (selected.length || selected_group)),
keybind: new Keybind({key: 46}),
click: function () {
var array;
Undo.initEdit({cubes: selected, outliner: true, selection: true})
if (selected_group) {
selected_group.remove(true)
return;
}
if (array == undefined) {
array = selected.slice(0)
} else if (array.constructor !== Array) {
array = [array]
} else {
array = array.slice(0)
}
array.forEach(function(s) {
s.remove(false)
})
updateSelection()
Undo.finishEdit('delete')
}
})
new Action({
id: 'sort_outliner',
icon: 'sort_by_alpha',
category: 'edit',
click: function () {
Undo.initEdit({outliner: true});
if (TreeElements.length < 1) return;
TreeElements.sort(function(a,b) {
return sort_collator.compare(a.name, b.name)
});
Undo.finishEdit('sort_outliner')
}
})
new Action({
id: 'local_move',
icon: 'check_box',
category: 'edit',
linked_setting: 'local_move',
click: function () {
BarItems.local_move.toggleLinkedSetting()
updateSelection()
}
})
new Action({
id: 'element_colors',
icon: 'check_box',
category: 'edit',
linked_setting: 'outliner_colors',
click: function () {
BarItems.element_colors.toggleLinkedSetting()
updateSelection()
}
})
new Action({
id: 'select_window',
icon: 'filter_list',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
keybind: new Keybind({key: 70, ctrl: true}),
click: function () {
showDialog('selection_creator')
$('#selgen_name').focus()
}
})
new Action({
id: 'invert_selection',
icon: 'swap_vert',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
click: function () {
elements.forEach(function(s) {
if (selected.includes(s)) {
selected.splice(selected.indexOf(s), 1)
} else {
selected.push(s)
}
})
if (selected_group) selected_group.unselect()
updateSelection()
Blockbench.dispatchEvent('invert_selection')
}
})
new Action({
id: 'select_all',
icon: 'select_all',
category: 'edit',
condition: () => (!display_mode && !Animator.open),
keybind: new Keybind({key: 65, ctrl: true}),
click: function () {selectAll()}
})
new Action({
id: 'collapse_groups',
icon: 'format_indent_decrease',
category: 'edit',
condition: () => TreeElements.length > 0,
click: function () {
getAllOutlinerGroups().forEach(function(g) {
g.isOpen = false
var name = g.name
g.name = '_$X0v_'
g.name = name
})
}
})
})

@ -60,6 +60,9 @@ class Panel {
.click((event) => {
setActivePanel(this.id)
})
.contextmenu((event) => {
setActivePanel(this.id)
})
.prepend(this.handle)
}
moveTo(ref_panel, before) {
@ -162,7 +165,7 @@ class ResizeLine {
var Interface = {
default_data: {
left_bar_width: 328,
left_bar_width: 338,
right_bar_width: 300,
quad_view_x: 50,
quad_view_y: 50,
@ -254,6 +257,7 @@ function setupInterface() {
} catch (err) {}
$('.entity_mode_only').hide()
$('.edit_session_active').hide()
$('.sidebar').droppable({
accept: 'h3',
@ -272,7 +276,7 @@ function setupInterface() {
bottom: Toolbars.main_uv
},
onResize: function() {
var size = limitNumber($(this.node).width()-4, 64, 1200)
var size = limitNumber($(this.node).width()-10, 64, 1200)
size = Math.floor(size/16)*16
main_uv.setSize(size)
}
@ -293,11 +297,38 @@ function setupInterface() {
})
Interface.Panels.options = new Panel({
id: 'options',
condition: function() {return !display_mode && !Animator.open},
condition: function() {return Modes.id === 'edit'},
toolbars: {
rotation: Toolbars.rotation,
origin: Toolbars.origin,
}
})
Interface.Panels.color = new Panel({
id: 'color',
condition: () => Modes.id === 'paint',
toolbars: {
},
onResize: t => {
$('#main_colorpicker').spectrum('reflow');
var h = $('.panel#color .sp-container.sp-flat').height()-20;
$('.panel#color .sp-palette').css('max-height', h+'px')
}
})
Interface.Panels.color.picker = $('#main_colorpicker').spectrum({
preferredFormat: "hex",
color: 'ffffff',
flat: true,
showAlpha: true,
showInput: true,
maxSelectionSize: 128,
showPalette: true,
palette: [],
localStorageKey: 'brush_color_palette',
move: function(c) {
$('#main_colorpicker_preview > div').css('background-color', c.toRgbString())
}
})
Interface.Panels.outliner = new Panel({
id: 'outliner',
condition: function() {return !display_mode},
@ -310,9 +341,11 @@ function setupInterface() {
menu: new Menu([
'add_cube',
'add_group',
'_',
'sort_outliner',
'select_all',
'collapse_groups',
'element_colors',
'outliner_toggle'
])
})
@ -361,6 +394,7 @@ function setupInterface() {
'open_backup_folder',
'save'
])
//$(document).contextmenu()
//Tooltip Fix
@ -417,6 +451,9 @@ function setupInterface() {
})
$(document).contextmenu(function(event) {
if (!$(event.target).hasClass('allow_default_menu')) {
/*if (event.target.nodeName === 'INPUT' && $(event.target).is(':focus')) {
Interface.text_edit_menu.open(event, event.target)
}*/
return false;
}
})
@ -438,20 +475,6 @@ function setupInterface() {
eval(obj.attr('onmouseup'))
})
$('#timeline_inner').on('mousewheel', function() {
if (event.ctrlKey) {
var offset = 1 - event.deltaY/600
Timeline.vue._data.size = limitNumber(Timeline.vue._data.size * offset, 10, 1000)
this.scrollLeft *= offset
let l = (event.offsetX / this.clientWidth) * 500 * (event.deltaY<0?1:-0.2)
this.scrollLeft += l
} else {
this.scrollLeft += event.deltaY/2
}
Timeline.updateSize()
event.preventDefault();
});
//Mousemove
$(document).mousemove(function(event) {
mouse_pos.x = event.clientX
@ -548,11 +571,37 @@ function setProjectTitle(title) {
$('title').text('Blockbench')
}
}
//Zoom
function setZoomLevel(mode) {
if (Prop.active_panel === 'uv') {
var zoom = main_uv.zoom
switch (mode) {
case 'in': zoom *= 1.5; break;
case 'out': zoom *= 0.66; break;
case 'reset': zoom = 1; break;
}
zoom = limitNumber(zoom, 1, 4)
main_uv.setZoom(zoom)
} else if (isApp) {
switch (mode) {
case 'in': Prop.zoom += 5; break;
case 'out': Prop.zoom -= 5; break;
case 'reset': Prop.zoom = 100; break;
}
var level = (Prop.zoom - 100) / 12
currentwindow.webContents.setZoomLevel(level)
resizeWindow()
}
}
//Dialogs
function showDialog(dialog) {
var obj = $('.dialog#'+dialog)
$('.dialog').hide(0)
if (open_menu) {
open_menu.hide()
}
$('#blackout').fadeIn(200)
obj.fadeIn(200)
open_dialog = dialog

292
js/io.js

@ -1,6 +1,6 @@
//New
function newProject(entity_mode) {
if (showSaveDialog()) {
function newProject(entity_mode, force) {
if (force || showSaveDialog()) {
if (Toolbox.selected.id !== 'move_tool') BarItems.move_tool.select();
elements.length = 0;
TreeElements.length = 1;
@ -95,7 +95,9 @@ function loadModel(data, filepath, add) {
var extension = pathToExtension(filepath)
if (extension === 'bbmodel') {
loadBBModel(model)
setTimeout(() => {
loadBBModel(model)
}, 8)
} else if (extension === 'jpm') {
loadJPMModel(model)
} else if (extension === 'jem') {
@ -109,14 +111,13 @@ function loadModel(data, filepath, add) {
}
loadBlockModel(model, filepath, add)
}
loadTextureDraggable()
loadOutlinerDraggable()
Canvas.updateAll()
Blockbench.removeFlag('importing')
if (!add) {
Prop.project_saved = true;
}
if (!add) {
EditSession.initNewModel()
}
}
function loadBBModel(model) {
if (!model.meta || !model.meta.format) {
@ -138,7 +139,6 @@ function loadBBModel(model) {
} else {
Blockbench.entity_mode = false;
}
saveSettings()
Project.name = model.name;
if (model.geo_name) {
Project.geometry_name = model.geo_name;
@ -155,8 +155,7 @@ function loadBBModel(model) {
if (model.textures) {
model.textures.forEach(tex => {
var tex_copy = new Texture(tex).add(false);
tex_copy.uuid = tex.uuid;
var tex_copy = new Texture(tex, tex.uuid).add(false);
if (tex_copy.mode === 'link') {
tex_copy.fromPath(tex.path)
} else {
@ -166,7 +165,9 @@ function loadBBModel(model) {
}
if (model.cubes) {
model.cubes.forEach(function(cube) {
base_cube = new Cube(cube).init(false)
base_cube = new Cube()
if (cube.uuid) base_cube.uuid = cube.uuid;
base_cube.extend(cube).init(false)
for (var face in base_cube.faces) {
if (!model.meta.box_uv) {
var texture = textures[cube.faces[face].texture]
@ -182,15 +183,26 @@ function loadBBModel(model) {
}
if (model.outliner) {
parseGroups(model.outliner)
if (model.meta.bone_rig) {
Canvas.updateAllBones()
Canvas.updateAllPositions()
}
}
if (model.animations) {
model.animations.forEach(ani => {
var base_ani = new Animation(ani).add();
var base_ani = new Animation()
base_ani.uuid = ani.uuid;
base_ani.extend(ani).add();
})
}
if (model.display !== undefined) {
DisplayMode.loadJSON(model.display)
}
if (model.history) {
Undo.history = model.history.slice()
Undo.index = model.history_index;
}
updateSelection()
}
function loadBlockModel(model, filepath, add) {
if (!model.elements && !model.parent && !model.display && !model.textures) {
@ -209,7 +221,10 @@ function loadBlockModel(model, filepath, add) {
var previous_length = add ? elements.length : 0
var previous_texture_length = add ? textures.length : 0
var new_cubes = [];
var new_textures = [];
if (add) {
Undo.initEdit({cubes: new_cubes, outliner: true, textures: new_textures})
Prop.added_models++;
var import_group = new Group(pathToName(filepath, false))
}
@ -219,6 +234,7 @@ function loadBlockModel(model, filepath, add) {
DisplayMode.loadJSON(model.display)
}
var texture_ids = {}
var texture_paths = {}
if (model.textures) {
//Create Path Array to fetch textures
var path_arr = filepath.split(osfs)
@ -226,22 +242,23 @@ function loadBlockModel(model, filepath, add) {
path_arr.splice(-index)
var texture_arr = model.textures
var paths = {}
for (var tex in texture_arr) {
if (texture_arr.hasOwnProperty(tex)) {
if (tex != 'particle') {
var t = new Texture({id: tex}).fromJavaLink(texture_arr[tex], path_arr.slice()).add(false)
paths[texture_arr[tex]] = texture_ids[tex] = t
texture_paths[texture_arr[tex]] = texture_ids[tex] = t
new_textures.push(t);
}
}
}
if (texture_arr.particle) {
if (paths[texture_arr.particle]) {
paths[texture_arr.particle].enableParticle()
if (texture_paths[texture_arr.particle]) {
texture_paths[texture_arr.particle].enableParticle()
} else {
var t = new Texture({id: 'particle'}).fromJavaLink(texture_arr[tex], path_arr.slice()).add(false).enableParticle()
texture_ids.particle = t;
texture_paths[texture_arr[tex]] = texture_ids.particle = t;
new_textures.push(t);
}
}
//Get Rid Of ID overlapping
@ -278,10 +295,22 @@ function loadBlockModel(model, filepath, add) {
if (obj.faces[face].texture === '#missing') {
} else if (obj.faces[face].texture) {
var t = texture_ids[obj.faces[face].texture.replace(/^#/, '')]
if (t instanceof Texture) {
base_cube.faces[face].texture = t.uuid;
var id = obj.faces[face].texture.replace(/^#/, '')
var t = texture_ids[id]
if (t instanceof Texture === false) {
if (texture_paths[obj.faces[face].texture]) {
var t = texture_paths[obj.faces[face].texture]
if (t.id === 'particle') {
t.extend({id: id, name: '#'+id}).loadEmpty(3)
}
} else {
var t = new Texture({id: id, name: '#'+id}).add(false).loadEmpty(3)
texture_ids[id] = t
new_textures.push(t);
}
}
base_cube.faces[face].texture = t.uuid;
}
if (obj.faces[face].tintindex !== undefined) {
base_cube.faces[face].tint = true;
@ -294,7 +323,6 @@ function loadBlockModel(model, filepath, add) {
} else {
base_cube.autouv = 0;
}
elements.push(base_cube);
if (!add) {
TreeElements.push(base_cube)
base_cube.parent = 'root'
@ -302,6 +330,8 @@ function loadBlockModel(model, filepath, add) {
import_group.children.push(base_cube)
base_cube.parent = import_group
}
base_cube.init()
new_cubes.push(base_cube);
})
}
if (model.groups && model.groups.length > 0) {
@ -326,18 +356,17 @@ function loadBlockModel(model, filepath, add) {
from: [0, 0, 7.5],
to: [16, 16, 7.8],
faces: {
north: {uv: [16,0,0,16], texture: 'layer0'},
south: {uv: [16,0,16,0], texture: 'layer0'},
north: {uv: [16,0,0,16], texture: textures[0].uuid || null},
south: {uv: [0,0,16,16], texture: textures[0].uuid || null},
east: {uv: [0,0,0,0], texture: null},
west: {uv: [0,0,0,0], texture: null},
up: {uv: [0,0,0,0], texture: null},
up: {uv: [0,0,0,0], texture: null},
down: {uv: [0,0,0,0], texture: null},
},
autouv: 0,
export: false
})
elements.push(base_cube);
base_cube.addTo()
}).init()
new_cubes.push(base_cube);
} else if (!model.elements && model.parent) {
Blockbench.showMessageBox({
translateKey: 'child_model_only',
@ -345,6 +374,7 @@ function loadBlockModel(model, filepath, add) {
message: tl('message.child_model_only.message', [model.parent])
})
}
updateSelection()
//Set Parent
if (model.parent !== undefined) {
@ -354,8 +384,15 @@ function loadBlockModel(model, filepath, add) {
if (model.ambientocclusion === false) {
Project.ambientocclusion = false;
}
if (add) {
Undo.finishEdit('add block model')
}
}
function loadJPMModel(model) {
function loadJPMModel(model, add) {
var new_cubes = [];
if (add) {
Undo.initEdit({cubes: new_cubes, outliner: true})
}
function addSubmodel(submodel) {
if (submodel.boxes) {
submodel.boxes.forEach(function(box) {
@ -382,9 +419,8 @@ function loadJPMModel(model) {
down: {uv: box.uvDown},
},
rotation: submodel.rotate
})
elements.push(base_cube);
TreeElements.push(base_cube)
}).init()
new_cubes.push(base_cube);
}
})
}
@ -392,10 +428,13 @@ function loadJPMModel(model) {
submodel.submodels.forEach(addSubmodel)
}
}
if (add) {
Undo.finishEdit('add jpm model')
}
addSubmodel(model)
Canvas.updateAll()
}
function loadJEMModel(model) {
function loadJEMModel(model, add) {
entityMode.join()
if (model.textureSize) {
Project.texture_width = parseInt(model.textureSize[0])
@ -498,7 +537,6 @@ function loadEntityModelFile(data) {
if (pe_list && pe_list._data) {
pe_list._data.search_text = ''
}
saveSettings()
function rotateOriginCoord(pivot, y, z) {
return [
@ -751,6 +789,7 @@ function loadEntityModel(data) {
if (isApp && Project.parent) {
findEntityTexture(Project.parent)
}
EditSession.initNewModel()
}
var Extruder = {
drawImage: function(path) {
@ -978,22 +1017,26 @@ function buildBBModel(options) {
if (!cube.rotation.allEqual(0)) el.rotation = cube.rotation;
if (!cube.origin.allEqual(0)) el.origin = cube.origin;
if (!cube.uv_offset.allEqual(0)) el.uv_offset = cube.uv_offset;
if (!model.meta.box_uv) {
el.faces = {}
for (var face in cube.faces) {
el.faces[face] = cube.faces[face].getSaveCopy()
}
}
el.uuid = cube.uuid
model.cubes.push(el)
})
model.outliner = compileGroups()
model.outliner = compileGroups(true)
model.textures = [];
textures.forEach(tex => {
var t = tex.getUndoCopy();
delete t.selected;
if (options.bitmaps) {
t.source = 'data:image/png;base64,'+tex.getBase64()
t.mode = 'bitmap'
}
model.textures.push(t);
})
@ -1004,7 +1047,6 @@ function buildBBModel(options) {
})
}
if (!Blockbench.entity_mode && Object.keys(display).length >= 1) {
var new_display = {}
var entries = 0;
@ -1019,10 +1061,24 @@ function buildBBModel(options) {
model.display = new_display
}
}
if (options.history) {
model.history = [];
Undo.history.forEach(h => {
var e = {
before: omitKeys(h.before, ['aspects']),
post: omitKeys(h.post, ['aspects']),
action: h.action
}
model.history.push(e);
})
model.history_index = Undo.index;
}
if (options.raw) {
return model
return model;
} else {
return JSON.stringify(model)
return JSON.stringify(model);
}
}
function buildBlockModel(options) {
@ -1164,9 +1220,12 @@ function buildBlockModel(options) {
textures.forEach(function(t, i){
if (!textures_used.includes(t) && !isTexturesOnlyModel) return;
texturesObj[t.id] = t.javaTextureLink(options.backup)
var link = t.javaTextureLink()
if (t.id !== link.replace(/^#/, '')) {
texturesObj[t.id] = link
}
if (t.particle) {
texturesObj.particle = t.javaTextureLink(options.backup)
texturesObj.particle = link
}
if (t.mode === 'bitmap') {
hasUnsavedTextures = true
@ -1292,33 +1351,34 @@ function buildEntityModel(options) {
bone.material = g.material
}
//Cubes
if (g.children && g.children.length) {
bone.cubes = []
var i = 0;
while (i < g.children.length) {
var s = g.children[i]
if (s !== undefined && s.type === 'cube' && s.export !== false) {
var cube = new oneLiner()
cube.origin = s.from.slice()
cube.size = s.size()
cube.origin[0] = -(cube.origin[0] + cube.size[0])
cube.uv = s.uv_offset
if (s.inflate && typeof s.inflate === 'number') {
cube.inflate = s.inflate
}
if (s.mirror_uv === !bone.mirror) {
cube.mirror = s.mirror_uv
}
//Visible Bounds
var mesh = s.mesh
if (mesh) {
visible_box.expandByObject(mesh)
}
bone.cubes.push(cube)
cube_count++;
var cubes = []
var i = 0;
while (i < g.children.length) {
var s = g.children[i]
if (s !== undefined && s.type === 'cube' && s.export !== false) {
var cube = new oneLiner()
cube.origin = s.from.slice()
cube.size = s.size()
cube.origin[0] = -(cube.origin[0] + cube.size[0])
cube.uv = s.uv_offset
if (s.inflate && typeof s.inflate === 'number') {
cube.inflate = s.inflate
}
i++;
if (s.mirror_uv === !bone.mirror) {
cube.mirror = s.mirror_uv
}
//Visible Bounds
var mesh = s.mesh
if (mesh) {
visible_box.expandByObject(mesh)
}
cubes.push(cube)
cube_count++;
}
i++;
}
if (cubes.length) {
bone.cubes = cubes
}
bones.push(bone)
})
@ -1660,6 +1720,75 @@ function buildOBJModel(name) {
scene.position.set(-8,-8,-8)
return content;
}
function uploadSketchfabModel() {
var dialog = new Dialog({
id: 'sketchfab_uploader',
title: 'Upload Sketchfab Model',
width: 540,
form: {
token: {label: 'dialog.sketchfab_uploader.token', value: settings.sketchfab_token.value},
about_token: {type: 'text', text: 'dialog.sketchfab_uploader.about_token'},
name: {label: 'dialog.sketchfab_uploader.name'},
description: {label: 'dialog.sketchfab_uploader.description', type: 'textarea'},
tags: {label: 'dialog.sketchfab_uploader.tags', placeholder: 'Tag1 Tag2'},
},
onConfirm: function(formResult) {
if (formResult.token && !formResult.name) {
Blockbench.showQuickMessage('message.sketchfab.name_or_token', 1800)
return;
}
if (!formResult.tags.split(' ').includes('blockbench')) {
formResult.tags += ' blockbench';
}
var data = new FormData()
data.append('token', formResult.token)
data.append('name', formResult.name)
data.append('description', formResult.description)
data.append('tags', formResult.tags)
settings.sketchfab_token.value = formResult.token
var archive = new JSZip();
var model_data = buildOBJModel('model')
archive.file('model.obj', model_data.obj)
archive.file('model.mtl', model_data.mtl)
for (var key in model_data.images) {
var tex = model_data.images[key];
if (tex) {
archive.file(pathToName(tex.name) + '.png', tex.getBase64(), {base64: true});
}
}
archive.generateAsync({type: 'blob'}).then(blob => {
var file = new File([blob], 'model.zip', {type: 'application/x-zip-compressed'})
data.append('modelFile', file)
$.ajax({
url: 'https://api.sketchfab.com/v3/models',
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(response) {
Blockbench.showQuickMessage('message.sketchfab.success', 1500)
Blockbench.openLink('https://sketchfab.com/models/'+response.uid)
},
error: function(response) {
Blockbench.showQuickMessage('message.sketchfab.error', 1500)
console.error(response);
}
})
})
dialog.hide()
}
})
dialog.show()
}
function compileJSON(object, options) {
var output = ''
if (typeof options !== 'object') options = {}
@ -1770,6 +1899,22 @@ function autoParseJSON(data, feedback) {
return data;
}
function saveProjectSettings() {
if (Blockbench.entity_mode) {
main_uv.setGrid()
if (uv_dialog.editors) {
uv_dialog.editors.single.setGrid()
}
if (entityMode.old_res.x !== Project.texture_width || entityMode.old_res.y !== Project.texture_height) {
entityMode.setResolution()
Undo.finishEdit('changed resolution')
}
if (EditSession.active && EditSession.hosting) {
EditSession.sendAll('change_project_meta', JSON.stringify(Project));
}
}
}
BARS.defineActions(function() {
//New
new Action({
@ -1777,7 +1922,11 @@ BARS.defineActions(function() {
icon: 'insert_drive_file',
category: 'file',
keybind: new Keybind({key: 78, ctrl: true}),
click: function () {newProject()}
click: function () {
if (newProject()) {
EditSession.initNewModel()
}
}
})
new Action({
id: 'new_entity_model',
@ -1787,6 +1936,7 @@ BARS.defineActions(function() {
click: function () {
if (newProject(true)) {
showDialog('project_settings');
EditSession.initNewModel()
}
}
})
@ -1796,6 +1946,7 @@ BARS.defineActions(function() {
icon: 'assessment',
category: 'file',
keybind: new Keybind({key: 79, ctrl: true}),
condition: () => (!EditSession.active || EditSession.hosting),
click: function () {
Blockbench.import({
extensions: ['json', 'jem', 'jpm', 'bbmodel'],
@ -1976,7 +2127,6 @@ BARS.defineActions(function() {
var content = buildEntityModel()
}
archive.file((Project.name||'model')+'.json', content)
var texfolder = archive.folder('textures');
textures.forEach(tex => {
if (tex.mode === 'bitmap') {
texfolder.file(pathToName(tex.name) + '.png', tex.source.replace('data:image/png;base64,', ''), {base64: true});
@ -1995,4 +2145,12 @@ BARS.defineActions(function() {
})
}
})
new Action({
id: 'upload_sketchfab',
icon: 'fa-cube',
category: 'file',
click: function(ev) {
uploadSketchfabModel()
}
})
})

@ -253,7 +253,7 @@ $(document).keydown(function(e) {
if (e.ctrlKey === true && e.which == 73 && isApp) {
electron.getCurrentWindow().toggleDevTools()
used = true
} else if (e.which === 18 && Toolbox.selected.alt_tool && !Toolbox.original) {
} else if (e.which === 18 && Toolbox.selected.alt_tool && !Toolbox.original && !open_interface) {
//Alt Tool
var orig = Toolbox.selected;
var alt = BarItems[Toolbox.selected.alt_tool]

@ -32,10 +32,12 @@ const Language = {
ja: '\u65E5\u672C\u8A9E (Japanese)',//日本語
nl: 'Nederlands (Dutch)',
pl: 'Polski (Polish)',
pt: 'Portugu\u00EAs (Portuguese)',
ru: '\u0440\u0443\u0441\u0441\u043A\u0438\u0439 (Russian)',
sv: 'Svenska (Swedish)',
zh: '\u4e2d\u6587 (Chinese)',//中文
}
},
toString: () => Language.code
}
function getStringWidth(string, size) {
var a = $('<label style="position: absolute">'+string+'</label>')
@ -61,7 +63,7 @@ function loadLanguage() {
}
$.ajax({
dataType: "json",
url: 'lang/'+Language.code+'.json',
url: 'lang/'+Language+'.json',
//data: data,
//async: false,
success: function(data) {

@ -250,7 +250,7 @@ function previewVariableValue(name, time) {
return 1
} else if (name === 'false') {
return 0
} else if (name === 'global.anim_time' || name === 'time' || name === 'query.life_time' ) {
} else if (name === 'global.anim_time' || name === 'query.anim_time' || name === 'time' || name === 'query.life_time' ) {
return time
} else {
var inputs = $('#var_placeholder_area').val().split('\n')

@ -162,7 +162,7 @@ class BBPainter {
b: px[2],
a: px[3]/256
})
BarItems.brush_color.set(t)
ColorPanel.set(t)
})
}
useBrush(texture, x, y, uvTag, no_update) {
@ -174,7 +174,7 @@ class BBPainter {
var ctx = canvas.getContext('2d')
ctx.save()
var color = BarItems.brush_color.get().toRgb();//.toRgbString()
var color = ColorPanel.get().toRgb();//.toRgbString()
var size = BarItems.slider_brush_size.get();
var softness = BarItems.slider_brush_softness.get()/100;
var b_opacity = BarItems.slider_brush_opacity.get()/100;
@ -196,15 +196,15 @@ class BBPainter {
if (rect[t] > rect[t+2]) {
[rect[t], rect[t+2]] = [rect[t+2], rect[t]]
}
rect[t] = Math.floor(rect[t])
rect[t+2] = Math.ceil(rect[t+2])
rect[t] = Math.round(rect[t])
rect[t+2] = Math.round(rect[t+2])
}
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]]
ctx.rect(rect[0], rect[1], w, h)
if (tool === 'fill_tool') {
ctx.fillStyle = BarItems.brush_color.get().toRgbString()
ctx.fillStyle = ColorPanel.get().toRgbString()
var fill_mode = BarItems.fill_mode.get()
var cube = Painter.current.cube;
@ -462,61 +462,43 @@ class BBPainter {
})
}
addBitmapDialog() {
var lines = []
lines.push({label: 'dialog.create_texture.name', node: '<input class="dark_bordered half" type="text" id="bitmap_name">'})
lines.push({label: 'dialog.create_texture.folder', node: '<input class="dark_bordered half" type="text" id="bitmap_folder">'})
if (elements.length > 0) {
lines.push({label: 'dialog.create_texture.template', node: '<input type="checkbox" id="bitmap_doTemplate">'})
lines.push({label: 'dialog.create_texture.compress', node: '<input type="checkbox" id="bitmap_compressTemplate">'})
}
lines.push({widget: Painter.background_color})
lines.push({label: 'dialog.create_texture.resolution', node: '<input class="dark_bordered" style="width:72px" type="number" id="bitmap_resolution">'})
var dialog = new Dialog({
id: 'add_bitmap',
title: tl('dialog.create_texture.title'),
draggable: true,
lines: lines,
onConfirm: function() {
Painter.addBitmapFromDialog()
form: {
bitmap_name: {label: 'dialog.create_texture.name'},
bitmap_folder: {label: 'dialog.create_texture.folder'},
bitmap_doTemplate: {label: 'dialog.create_texture.template', type: 'checkbox', condition: elements.length},
bitmap_compressTemplate: {label: 'dialog.create_texture.compress', type: 'checkbox'},
bitmap_power: {label: 'dialog.create_texture.power', type: 'checkbox', value: true},
background_color: {type: 'color', colorpicker: Painter.background_color},
bitmap_resolution: {label: 'dialog.create_texture.resolution', type: 'number', value: 16},
},
onConfirm: function(results) {
Painter.addBitmap({
res: limitNumber(results.bitmap_resolution, 16, 2048),
color: results.background_color,
name: results.bitmap_name,
folder: results.bitmap_folder,
particle: 'auto',
entity_template: results.bitmap_doTemplate,
compress: results.bitmap_compressTemplate,
power: results.bitmap_power
})
dialog.hide()
}
})
dialog.show()
$('#bitmap_compressTemplate').parent().hide()
}).show()
$('#bitmap_compressTemplate, #bitmap_power').parent().hide()
$('.dialog#add_bitmap input#bitmap_doTemplate').click(function() {
var checked = $('.dialog#add_bitmap input#bitmap_doTemplate').is(':checked')
$('#bitmap_compressTemplate').parent()[ checked ? 'show' : 'hide' ]()
$('#bitmap_compressTemplate, #bitmap_power').parent()[ checked ? 'show' : 'hide' ]()
if (Painter.background_color.get().toHex8() === 'ffffffff') {
Painter.background_color.set('#00000000')
}
})
}
testSetup() {
Painter.addBitmap()
main_uv.setFace('up')
addCube().extend({to:[16,1,16]})
elements[0].faces.up.uv = [0,0,16,16]
textures[0].apply()
Canvas.updateSelected()
updateSelection()
}
addBitmapFromDialog() {
var color = Painter.background_color.get()
Painter.addBitmap({
res: limitNumber(parseInt($('.dialog#add_bitmap input#bitmap_resolution').val()), 16, 2048),
color: color,
name: $('.dialog#add_bitmap input#bitmap_name').val(),
folder: $('.dialog#add_bitmap input#bitmap_folder').val(),
particle: 'auto',
entity_template: $('.dialog#add_bitmap input#bitmap_doTemplate').is(':checked'),
compress: $('.dialog#add_bitmap input#bitmap_compressTemplate').is(':checked')
})
}
addBitmap(options, after) {
if (typeof options !== 'object') {
options = {}
@ -622,11 +604,15 @@ class BBPainter {
Blockbench.showMessage('No valid cubes', 'center')
return;
}
/*
TEMPLATE MENU
condensed
use old texture
*/
function getNextPower(num, min) {
var i = min ? min : 2
while (i < num && i < 4000) {
i *= 2
}
return i;
}
if (options.compress) {
var fill_map = {}
@ -726,20 +712,16 @@ class BBPainter {
extend_y += max_height
})
}
//Size
//function getNextPower(num, min) {
// var i = min ? min : 2
// while (i < num && i < 4000) {
// i *= 2
// }
// return i;
//}
var max_size = Math.max(extend_x, extend_y)
max_size = Math.ceil(max_size/16)*16
if (options.power) {
max_size = getNextPower(max_size, 16);
} else {
max_size = Math.ceil(max_size/16)*16;
}
if (background_color.getAlpha() != 0) {
background_color = background_color.toInteger()
background_color = background_color.toRgbString()
}
var canvas = document.createElement('canvas')
canvas.width = canvas.height = max_size*res_multiple;
@ -747,8 +729,9 @@ class BBPainter {
ctx.imageSmoothingEnabled = false;
function drawTemplateRectangle(border_color, color, coords) {
if (typeof background_color === 'number') {
function drawTemplateRectangle(border_color, color, face, coords) {
//if (!Blockbench.entity_mode && face && face.texture === null) return;
if (typeof background_color === 'string') {
border_color = background_color
color = undefined
}
@ -840,7 +823,7 @@ class BBPainter {
if (!t.obj.faces[face].texture ||
!drawTexture(t.obj.faces[face], d.place(t))
) {
drawTemplateRectangle(d.c1, d.c2, d.place(t))
drawTemplateRectangle(d.c1, d.c2, t.obj.faces[face], d.place(t))
}
}
obj.uv_offset[0] = t.posx
@ -880,7 +863,20 @@ class BBPainter {
}
}
}
var Painter = new BBPainter()
const Painter = new BBPainter()
const ColorPanel = {
set: function(color) {
var value = new tinycolor(color)
$('#main_colorpicker').spectrum('set', value.toHex8String())
$('#main_colorpicker_preview > div').css('background-color', value.toRgbString())
return this;
},
get: function() {
var value = $('#main_colorpicker').spectrum('get');
return value;
}
}
BARS.defineActions(function() {
@ -980,11 +976,6 @@ BARS.defineActions(function() {
}
})
new ColorPicker({
id: 'brush_color',
condition: () => (Toolbox && ['brush_tool', 'color_picker', 'fill_tool'].includes(Toolbox.selected.id)),
palette: true
})
new BarSelect({
id: 'brush_mode',
condition: () => Toolbox && (Toolbox.selected.id === 'brush_tool' || Toolbox.selected.id === 'eraser'),

@ -79,6 +79,9 @@ class Plugin {
}
download(first) {
var scope = this;
if (first) {
Blockbench.showQuickMessage(tl('message.install_plugin', [scope.title]), 1400)
}
if (!isApp) {
scope.install(first)
return this;
@ -249,14 +252,18 @@ function loadInstalledPlugins() {
})
}
if (Plugins.installed.length > 0) {
var loaded = []
Plugins.installed.forEach(function(id) {
if (id.substr(-3) === '.js') {
if (id && id.substr(-3) === '.js') {
//Dev Plugins
var plugin = new Plugin().loadFromFile({path: id}, true)
loaded.push(pathToName(id))
} else if (id) {
loaded.push(id)
}
})
console.log('Loaded '+Plugins.installed.length+' plugin'+pluralS(Plugins.installed.length))
console.log(`Loaded ${loaded.length} plugin${pluralS(loaded.length)}`, loaded)
}
Plugins.Vue = new Vue({

@ -649,6 +649,9 @@ class Preview {
}
}
fullscreen() {
if (quad_previews.current) {
quad_previews.current.controls.stopMovement()
}
quad_previews.current = this;
quad_previews.enabled = false;
$('#preview').empty()
@ -1356,14 +1359,20 @@ class CanvasController {
})
}
updateRenderSides() {
var side = Blockbench.entity_mode ? 2 : 0;
if (display_mode) {
if (['thirdperson_righthand', 'thirdperson_lefthand', 'head'].includes(display_slot)) {
side = 2;
}
}
textures.forEach(function(t) {
var mat = Canvas.materials[t.uuid]
if (mat) {
mat.side = (display_mode || Blockbench.entity_mode) ? 2 : 0
mat.side = side
}
})
emptyMaterials.forEach(function(mat) {
mat.side = (display_mode || Blockbench.entity_mode) ? 2 : 0
mat.side = side
})
}
//Selection updaters
@ -1667,8 +1676,6 @@ class CanvasController {
obj.faces[f.face].uv[2] = uv[2]
obj.faces[f.face].uv[3] = uv[3]
var do_cl = Math.random()<0.001
//Fight Bleeding
for (var si = 0; si < 2; si++) {
let margin = 16/(si?Project.texture_height:Project.texture_width)/16;
@ -1823,36 +1830,30 @@ BARS.defineActions(function() {
icon: 'local_movies',
category: 'view',
click: function () {
var lines = [
{label: 'dialog.create_gif.length', node: '<input class="dark_bordered half" type="number" value="10" step="0.25" id="gif_length">'},
{label: 'dialog.create_gif.fps', node: '<input class="dark_bordered half" type="number" value="10" id="gif_fps">'},
{label: 'dialog.create_gif.compression', node: '<input class="dark_bordered half" type="number" value="4" id="gif_quality">'},
]
if (Animator.open) {
lines.push({label: 'dialog.create_gif.play', node: '<input type="checkbox" id="gif_play_animation">'})
}
var dialog = new Dialog({
new Dialog({
id: 'create_gif',
title: tl('dialog.create_gif.title'),
draggable: true,
lines: lines,
onConfirm: function() {
var jq = $(dialog.object)
var length = parseFloat( jq.find('#gif_length').val() )
var fps = parseInt( jq.find('#gif_fps').val() )
var quality = parseInt( jq.find('#gif_quality').val() )
if (jq.find('#gif_play_animation').is(':checked')) {
form: {
length: {label: 'dialog.create_gif.length', type: 'number', value: 10, step: 0.25},
fps: {label: 'dialog.create_gif.fps', type: 'number', value: 10},
quality:{label: 'dialog.create_gif.compression', type: 'number', value: 4},
turn: {label: 'dialog.create_gif.turn', type: 'number', value: 0, min: -10, max: 10},
play: {label: 'dialog.create_gif.play', type: 'checkbox', condition: Animator.open},
},
onConfirm: function(formData) {
if (formData.play) {
Timeline.start()
}
Screencam.createGif({
length: limitNumber(length, 0.1, 240)*1000,
fps: limitNumber(fps, 0.5, 30),
quality: limitNumber(quality, 0, 30),
length: limitNumber(formData.length, 0.1, 240)*1000,
fps: limitNumber(formData.fps, 0.5, 30),
quality: limitNumber(formData.quality, 0, 30),
turnspeed: formData.turn,
}, Screencam.returnScreenshot)
dialog.hide()
this.hide()
}
})
dialog.show()
}).show()
}
})
new Action({

@ -14,12 +14,13 @@ function settingSetup() {
origin_size: {category: 'preview', value: 10, type: 'number'},
control_size: {category: 'preview', value: 10, type: 'number'},
//focal_length: {category: 'preview', value: 70, type: 'number'},
display_skin: {category: 'preview', value: false, type: 'click', condition: isApp, icon: 'icon-player', click: function() { changeDisplaySkin() }},
seethrough_outline: {category: 'preview', value: false},
brightness: {category: 'preview', value: 50, type: 'number'},
shading: {category: 'preview', value: true},
transparency: {category: 'preview', value: true},
outliner_colors: {category: 'preview', value: true},
texture_fps: {category: 'preview', value: 2, type: 'number'},
display_skin: {category: 'preview', value: false, type: 'click', condition: isApp, icon: 'icon-player', click: function() { changeDisplaySkin() }},
//Edit
undo_limit: {category: 'edit', value: 128, type: 'number'},
restricted_canvas: {category: 'edit', value: true},
@ -51,7 +52,8 @@ function settingSetup() {
minifiedout: {category: 'export', value: false},
export_groups: {category: 'export', value: true},
obj_textures: {category: 'export', value: true},
credit: {category: 'export', value: 'Made with Blockbench', type: 'text'}
sketchfab_token:{category: 'export', value: '', type: 'text'},
credit: {category: 'export', value: 'Made with Blockbench', type: 'text'},
}
if (localStorage.getItem('settings') != null) {
@ -260,19 +262,6 @@ function saveSettings(force_update) {
}
Blockbench.dispatchEvent('update_settings')
}
function saveProjectSettings() {
if (Blockbench.entity_mode) {
main_uv.setGrid()
if (uv_dialog.editors) {
uv_dialog.editors.single.setGrid()
}
if (entityMode.old_res.x !== Project.texture_width || entityMode.old_res.y !== Project.texture_height) {
entityMode.setResolution()
Undo.finishEdit('changed resolution')
}
}
hideDialog()
}
function toggleSetting(setting) {
if (settings[setting].value === true) {
settings[setting].value = false

@ -1,25 +1,28 @@
//Textures
class Texture {
constructor(data) {
this.path = ''
constructor(data, uuid) {
var scope = this;
//Info
this.id = '';
this.name = ''
this.folder = '';
this.namespace = '';
this.id = '';
this.source = ''
this.path = ''
this.particle = false
//meta
this.source = ''
this.selected = false
this.error = false;
this.ratio = 1
this.show_icon = true
this.average_color = {r:0, g:0, b:0}
this.dark_box = false
this.error = 0;
//Data
this.ratio = 1
this.img = 0;
this.res = 0;
this.mode = 'link' //link, bitmap (internally used)
this.saved = true
if (!isApp) this.mode = 'bitmap'
this.uuid = guid()
this.mode = isApp ? 'link' : 'bitmap';
this.uuid = uuid || guid()
if (typeof data === 'object') {
this.extend(data)
@ -39,16 +42,129 @@ class Texture {
i++;
} else {
this.id = i.toString();
return;
break;
}
}
}
//Setup Img/Mat
var img = this.img = new Image()
img.src = 'assets/missing.png'
var tex = new THREE.Texture(img)
img.tex = tex;
img.tex.magFilter = THREE.NearestFilter
img.tex.minFilter = THREE.NearestFilter
var mat = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: tex,
transparent: settings.transparency.value,
side: display_mode || Blockbench.entity_mode ? 2 : 0,
alphaTest: 0.2
});
Canvas.materials[this.uuid] = mat
this.img.onload = function() {
if (!this.src) return;
this.tex.needsUpdate = true;
scope.res = img.naturalWidth;
scope.ratio = img.naturalWidth / img.naturalHeight;
if (scope.isDefault) {
console.log('Successfully loaded '+scope.name+' from default pack')
}
var average_color = getAverageRGB(this)
scope.dark_box = (average_color.r + average_color.g + average_color.b) >= 383
//Width / Animation
if (img.naturalWidth !== img.naturalHeight && Blockbench.entity_mode === false) {
if (img.naturalHeight % img.naturalWidth !== 0) {
scope.error = 2;
Blockbench.showQuickMessage('message.square_textures')
}
}
//--------------------------------------------------------------------------
if (Blockbench.entity_mode && textures.indexOf(scope) === 0) {
if (!scope.keep_size) {
var size = {
pw: Project.texture_width,
ph: Project.texture_height,
nw: img.naturalWidth,
nh: img.naturalHeight
}
if (false && (scope.old_width != size.nw || scope.old_height != size.nh) && (size.pw != size.nw || size.ph != size.nh)) {
Blockbench.showMessageBox({
translateKey: 'update_res',
icon: 'photo_size_select_small',
buttons: [tl('message.update_res.update'), tl('dialog.cancel')],
confirm: 0,
cancel: 1
}, function(result) {
if (result === 0) {
var lockUV = ( // EG. Texture Optimierung > Modulo geht nicht in allen Bereichen auf
(size.pw%size.nw || size.ph%size.nh) &&
(size.nw%size.pw || size.nh%size.ph)
)
entityMode.setResolution(img.naturalWidth, img.naturalHeight, lockUV)
if (selected.length) {
main_uv.loadData()
main_uv.setGrid()
}
}
})
}
scope.old_width = img.naturalWidth
scope.old_height = img.naturalHeight
}
}
//--------------------------------------------------------------------------
if ($('.dialog#texture_edit:visible').length > 0 && scope.selected === true) {
scope.openMenu()
}
TextureAnimator.updateButton()
Canvas.updateAllFaces(scope)
if (typeof scope.load_callback === 'function') {
scope.load_callback(scope);
delete scope.load_callback;
}
}
this.img.onerror = function() {
if (isApp &&
!scope.isDefault &&
scope.mode !== 'bitmap' &&
scope.fromDefaultPack()
) {
return true;
} else {
scope.loadEmpty()
}
}
}
get frameCount() {
if (this.ratio !== 1) {
if (1/this.ratio % 1 === 0) {
return 1/this.ratio
}
}
getErrorMessage() {
switch (this.error) {
case 0: return ''; break;
case 1: return tl('texture.error.file'); break;
case 1: return tl('texture.error.invalid'); break;
case 2: return tl('texture.error.ratio'); break;
case 3: return tl('texture.error.parent'); break;
}
}
getUndoCopy(bitmap) {
var copy = {
path: this.path,
@ -80,126 +196,17 @@ class Texture {
Merge.boolean(this, data, 'saved')
if (this.mode === 'bitmap') {
Merge.string(this, data, 'source')
} else if (data.path) {
this.source = this.path + '?' + tex_version;
}
return this;
}
//Loading
load(isDefault, reloading, cb) {
var scope = this;
if (Painter.current.texture === this) {
Painter.current = {}
}
this.error = false;
this.show_icon = true
var img = this.img = new Image()
if (Canvas.materials[scope.uuid] !== undefined) {
Canvas.materials[scope.uuid].dispose()
}
function onerror() {
if (isApp &&
!(isDefault || scope.isDefault) &&
scope.mode !== 'bitmap' &&
scope.fromDefaultPack()
) {
return true;
} else {
scope.img.src = 'assets/missing.png'
scope.error = true;
scope.show_icon = false
console.log('Error loading '+scope.source)
}
}
if (isApp && this.mode === 'link' && !fs.existsSync(this.source.replace(/\?\d+$/, ''))) {
if (onerror()) {
return
}
} else {
img.src = this.source
img.onerror = onerror
}
var tex = new THREE.Texture(img)
img.tex = tex;
img.tex.magFilter = THREE.NearestFilter
img.tex.minFilter = THREE.NearestFilter
img.onload = function() {
this.tex.needsUpdate = true;
scope.res = img.naturalWidth;
scope.ratio = img.naturalWidth / img.naturalHeight;
if (isDefault) {
console.log('Successfully loaded '+scope.name+' from default pack')
}
scope.average_color = getAverageRGB(this)
scope.dark_box = (scope.average_color.r + scope.average_color.g + scope.average_color.b) >= 383
//Width / Animation
if (img.naturalWidth !== img.naturalHeight && Blockbench.entity_mode === false) {
if (img.naturalHeight % img.naturalWidth === 0) {
Canvas.updateAllUVs()
BARS.updateConditions()
} else {
scope.error = true;
Blockbench.showQuickMessage('message.square_textures')
}
}
if (Blockbench.entity_mode && textures.indexOf(scope) === 0) {
if (!scope.keep_size) {
var size = {
pw: Project.texture_width,
ph: Project.texture_height,
nw: img.naturalWidth,
nh: img.naturalHeight
}
if ((scope.old_width != size.nw || scope.old_height != size.nh) && (size.pw != size.nw || size.ph != size.nh)) {
Blockbench.showMessageBox({
translateKey: 'update_res',
icon: 'photo_size_select_small',
buttons: [tl('message.update_res.update'), tl('dialog.cancel')],
confirm: 0,
cancel: 1
}, function(result) {
if (result === 0) {
var lockUV = ( // EG. Texture Optimierung > Modulo geht nicht in allen Bereichen auf
(size.pw%size.nw || size.ph%size.nh) &&
(size.nw%size.pw || size.nh%size.ph)
)
entityMode.setResolution(img.naturalWidth, img.naturalHeight, lockUV)
if (selected.length) {
main_uv.loadData()
main_uv.setGrid()
}
}
})
}
scope.old_width = img.naturalWidth
scope.old_height = img.naturalHeight
}
}
if ($('.dialog#texture_edit:visible').length >= 1 && scope.selected === true) {
scope.openMenu()
}
TextureAnimator.updateButton()
Canvas.updateAllFaces(scope)
}
if (Canvas.materials[this.uuid]) {
Canvas.materials[this.uuid].map.dispose()
Canvas.materials[this.uuid].dispose()
delete Canvas.materials[this.uuid]
}
var mat = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: tex,
transparent: settings.transparency.value,
side: display_mode || Blockbench.entity_mode ? 2 : 0,
alphaTest: 0.2
});
Canvas.materials[this.uuid] = mat
load(cb) {
this.error = 0;
this.show_icon = true;
this.img.src = this.source;
this.load_callback = cb;
return this;
}
fromJavaLink(link, path_array) {
@ -283,11 +290,21 @@ class Texture {
this.startWatcher()
if (!isApp && Project.dataURLTextures) {
if (this.img && this.img.src) {
this.img.src = 'assets/missing.png'
}
this.error = true;
this.show_icon = false
this.loadEmpty()
} else if (EditSession.active) {
this.load(() => {
var before = {textures: {}}
before.textures[scope.uuid] = true;
this.edit()
var post = new Undo.save({textures: [this]})
EditSession.sendEdit({
before: before,
post: post,
action: 'loaded_texture',
save_history: false
})
})
} else {
this.load()
}
@ -305,7 +322,7 @@ class Texture {
if (Blockbench.entity_mode) {
var path = findEntityTexture(Project.parent, 'raw')
if (path) {
this.isDefault = true
this.isDefault = true;
path = settings.default_path.value + osfs + path
if (fs.existsSync(path + '.png')) {
@ -320,15 +337,23 @@ class Texture {
}
delete this.isDefault
}
} else {
var path = settings.default_path.value + osfs + this.folder.replace(/\//g, osfs) + osfs + this.name
} else if (this.name && this.name.includes('.')) {
var folder = this.folder.replace(/\//g, osfs);
var path = settings.default_path.value + osfs + (folder ? (folder+osfs) : '') + this.name
if (fs.existsSync(path)) {
this.isDefault = true;
this.fromPath(path)
return true;
}
}
}
}
loadEmpty(error_id) {
this.img.src = 'assets/missing.png'
this.error = error_id||1;
this.show_icon = false;
return this;
}
updateSource(dataUrl) {
this.source = dataUrl
this.img.src = dataUrl
@ -406,11 +431,9 @@ class Texture {
}
//this.source = this.path + '?' + tex_version;
this.source = this.source.replace(/\?\d+$/, '?' + tex_version)
this.load(undefined, true)
if (single) {
main_uv.loadData()
loadTextureDraggable()
}
this.load(undefined)
TickUpdates.main_uv = true;
TickUpdates.texture_list = true;
}
reloadTexture() {
this.refresh(true)
@ -476,7 +499,7 @@ class Texture {
return this;
}
add(undo) {
if (undo !== false) {
if (undo) {
Undo.initEdit({textures: []})
}
var scope = this
@ -503,7 +526,7 @@ class Texture {
}
})
}
if (undo === true) {
if (undo) {
Undo.finishEdit('add_texture', {textures: [this]})
}
return this;
@ -567,7 +590,11 @@ class Texture {
}
//Interface
openFolder() {
if (!isApp || !this.path) return;
if (!isApp || !this.path) return this;
if (!fs.existsSync(this.path)) {
Blockbench.showQuickMessage('texture.error.file')
return this;
}
shell.showItemInFolder(this.path)
return this;
}
@ -627,13 +654,8 @@ class Texture {
}
}
//Export
javaTextureLink(backup) {
if (backup) {
return this.source;
}
javaTextureLink() {
var link = this.name.replace(/\.png$/, '')
if (this.folder) {
link = this.folder + '/' + link
}
@ -690,7 +712,7 @@ class Texture {
}
return this;
}
toBitmap(cb) {
getBase64() {
var scope = this;
if (isApp && scope.mode === 'link') {
var canvas = document.createElement('canvas')
@ -698,22 +720,22 @@ class Texture {
canvas.height = scope.img.naturalHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(scope.img, 0, 0)
scope.mode = 'bitmap'
scope.saved = false
scope.source = canvas.toDataURL('image/png')
cb()
var dataUrl = canvas.toDataURL('image/png')
} else {
var dataUrl = scope.source
}
return dataUrl.replace('data:image/png;base64,', '')
}
edit(cb, options) {
var scope = this;
if (typeof options !== 'object') {
options = {}
}
if (!options) options = false;
if (scope.mode === 'link') {
scope.toBitmap(function() {
Painter.edit(scope, cb, options)
})
} else {
scope.source = 'data:image/png;base64,' + scope.getBase64()
scope.mode = 'bitmap'
scope.saved = false
}
if (cb) {
Painter.edit(scope, cb, options)
}
scope.saved = false;
@ -852,16 +874,26 @@ function saveTextures() {
}
function saveTextureMenu() {
hideDialog()
Undo.initEdit({textures})
var tex = textures.selected
tex.name = $('#texture_edit input#te_name').val()
tex.id = $('#texture_edit input#te_variable').val()
tex.folder = $('#texture_edit input#te_folder').val()
tex.namespace = $('#texture_edit input#te_namespace').val()
$('#texture_edit #change_file_button').unbind('click')
$('#texture_edit #file_upload').unbind('input')
Undo.finishEdit('texture_edit')
var name = $('#texture_edit input#te_name').val(),
id = $('#texture_edit input#te_variable').val(),
folder = $('#texture_edit input#te_folder').val(),
namespace = $('#texture_edit input#te_namespace').val();
if (!(
tex.name === name &&
tex.id === id &&
tex.folder === folder &&
tex.namespace === namespace)
) {
Undo.initEdit({textures})
tex.name = name;
tex.id = id;
tex.folder = folder;
tex.namespace = namespace;
Undo.finishEdit('texture_edit')
}
}
function loadTextureDraggable() {
Vue.nextTick(function() {
@ -894,9 +926,11 @@ function loadTextureDraggable() {
if (data.cube && data.face) {
var tex = textures.findInArray('uuid', ui.helper.attr('texid'));
var cubes_list = data.cube.selected ? selected : [data.cube];
Undo.initEdit({})
Undo.initEdit({cubes: cubes_list})
if (tex) {
data.cube.applyTexture(tex, [data.face])
cubes_list.forEach(cube => {
cube.applyTexture(tex, [data.face])
})
}
Undo.finishEdit('apply texture')
}

@ -287,12 +287,11 @@ function scaleAll(save, size) {
if (size === undefined) {
size = $('#model_scale_label').val()
}
var origin = [8, 8, 8]
if (Blockbench.entity_mode) {
origin = [0, 0, 0]
} else if (selected_group) {
origin = selected_group.origin
}
var origin = [
parseFloat($('#scaling_origin_x').val())||0,
parseFloat($('#scaling_origin_y').val())||0,
parseFloat($('#scaling_origin_z').val())||0,
]
var clip = false
selected.forEach(function(obj) {
obj.autouv = 0;
@ -735,6 +734,9 @@ BARS.defineActions(function() {
}
selected_group.origin[axis] += diff
Canvas.updatePositions()
if (Blockbench.entity_mode) {
Canvas.updateAllBones()
}
return;
}
selected.forEach(function(obj, i) {
@ -821,6 +823,11 @@ BARS.defineActions(function() {
}, 'group', true)
}
showDialog('scaling')
var v = Blockbench.entity_mode ? 0 : 8;
var origin = selected_group ? selected_group.origin : [v, 0, v];
$('#scaling_origin_x').val(origin[0])
$('#scaling_origin_y').val(origin[1])
$('#scaling_origin_z').val(origin[2])
scaleAll(false, 1)
}
})

@ -14,7 +14,7 @@
'<i v-if="node.children && node.children.length > 0" v-on:click="toggle(node)" class="fa icon-open-state" :class=\'{"fa-caret-right": !node.isOpen, "fa-caret-down": node.isOpen}\'></i>' +
'<i v-else class="outliner_opener_placeholder"></i>' +
//Main
'<i v-if="showIcon(node)" :class="nodeClass(node)"></i>' +
'<i :class="node.icon + (settings.outliner_colors.value ? \' ec_\'+node.color : \'\')"></i>' +
'<input type="text" class="cube_name" v-model="node.name" disabled>' +
'<a v-for="btn in node.buttons" class="ml5" href="javascript:" :title="btn.title" v-on:click.stop="btnClick(btn, node)" v-bind:class="{advanced_option: btn.advanced_option}">' +
'<i v-if="node.isIconEnabled(btn) === true" :class="btn.icon"></i>' +
@ -33,9 +33,6 @@
}
},
methods: {
showIcon: function (node) {
return node.icon || node.openedIcon || node.closedIcon;
},
nodeClass: function (node) {
if (node.isOpen) {
return node.openedIcon || node.icon;

@ -37,6 +37,9 @@ var Undo = {
Prop.project_saved = false;
}
Blockbench.dispatchEvent('finished_edit', {aspects})
if (EditSession.active) {
EditSession.sendEdit(entry)
}
},
cancelEdit: function() {
if (!Undo.current_save) return;
@ -44,7 +47,7 @@ var Undo = {
Undo.loadSave(Undo.current_save, new Undo.save(Undo.current_save.aspects))
delete Undo.current_save;
},
undo: function() {
undo: function(remote) {
if (Undo.history.length <= 0 || Undo.index < 1) return;
Prop.project_saved = false;
@ -52,10 +55,13 @@ var Undo = {
var entry = Undo.history[Undo.index]
Undo.loadSave(entry.before, entry.post)
if (EditSession.active && remote !== true) {
EditSession.sendAll('command', 'undo')
}
console.log('Undo: '+entry.action)
Blockbench.dispatchEvent('undo', {entry})
},
redo: function() {
redo: function(remote) {
if (Undo.history.length <= 0) return;
if (Undo.index >= Undo.history.length) {
return;
@ -65,9 +71,26 @@ var Undo = {
var entry = Undo.history[Undo.index-1]
Undo.loadSave(entry.post, entry.before)
if (EditSession.active && remote !== true) {
EditSession.sendAll('command', 'redo')
}
console.log('Redo: '+entry.action)
Blockbench.dispatchEvent('redo', {entry})
},
remoteEdit: function(entry) {
Undo.loadSave(entry.post, entry.before, 'session')
if (entry.save_history !== false) {
delete Undo.current_save;
Undo.history.push(entry)
if (Undo.history.length > settings.undo_limit.value) {
Undo.history.shift()
}
Undo.index = Undo.history.length
Prop.project_saved = false;
Blockbench.dispatchEvent('finished_edit', {remote: true})
}
},
getItemByUUID: function(list, uuid) {
if (!list || typeof list !== 'object' || !list.length) {return false;}
var i = 0;
@ -96,16 +119,15 @@ var Undo = {
if (aspects.cubes) {
this.cubes = {}
aspects.cubes.forEach(function(obj) {
var copy = new Cube(obj)
if (aspects.uv_only) {
var copy = new Cube(obj)
copy = {
uv_offset: copy.uv_offset,
faces: copy.faces,
}
} else {
var copy = new Cube(obj)
}
copy.uuid = obj.uuid
delete copy.parent;
scope.cubes[obj.uuid] = copy
})
}
@ -138,8 +160,11 @@ var Undo = {
}
}
if (aspects.animation) {
this.animation = aspects.animation ? aspects.animation.undoCopy() : null;
if (aspects.animations) {
this.animations = {}
aspects.animations.forEach(a => {
scope.animations[a.uuid] = a.undoCopy();
})
}
if (aspects.keyframes && Animator.selected && Animator.selected.getBoneAnimator()) {
this.keyframes = {
@ -162,7 +187,8 @@ var Undo = {
})
}
},
loadSave: function(save, reference) {
loadSave: function(save, reference, mode) {
var is_session = mode === 'session';
if (save.cubes) {
for (var uuid in save.cubes) {
if (save.cubes.hasOwnProperty(uuid)) {
@ -185,7 +211,7 @@ var Undo = {
if (reference.cubes.hasOwnProperty(uuid) && !save.cubes.hasOwnProperty(uuid)) {
var obj = elements.findInArray('uuid', uuid)
if (obj) {
obj.remove(false)
obj.remove()
}
}
}
@ -196,12 +222,23 @@ var Undo = {
if (save.outliner) {
selected_group = undefined
parseGroups(save.outliner)
if (is_session) {
function iterate(arr) {
arr.forEach((obj) => {
delete obj.isOpen;
if (obj.children) {
iterate(obj.children)
}
})
}
iterate(save.outliner)
}
if (Blockbench.entity_mode) {
Canvas.updateAllPositions()
}
}
if (save.selection_group) {
if (save.selection_group && !is_session) {
selected_group = undefined
var sel_group = TreeElements.findRecursive('uuid', save.selection_group)
if (sel_group) {
@ -209,7 +246,7 @@ var Undo = {
}
}
if (save.selection) {
if (save.selection && !is_session) {
selected.length = 0;
elements.forEach(function(obj) {
if (save.selection.includes(obj.uuid)) {
@ -221,6 +258,9 @@ var Undo = {
if (save.group) {
var group = TreeElements.findRecursive('uuid', save.group.uuid)
if (group) {
if (is_session) {
delete save.group.isOpen;
}
group.extend(save.group)
if (Blockbench.entity_mode) {
group.forEachChild(function(obj) {
@ -238,10 +278,17 @@ var Undo = {
if (reference.textures[uuid]) {
var tex = Undo.getItemByUUID(textures, uuid)
if (tex) {
var require_reload = tex.mode !== save.textures[uuid].mode;
tex.extend(save.textures[uuid]).updateMaterial()
if (require_reload || reference.textures[uuid] === true) {
tex.load()
} else {
tex.updateMaterial()
}
}
} else {
new Texture(save.textures[uuid]).load().add(false)
var tex = new Texture(save.textures[uuid], uuid)
tex.load().add(false)
}
}
for (var uuid in reference.textures) {
@ -265,81 +312,91 @@ var Undo = {
Project.texture_height = save.resolution.height
}
if (save.animation) {
if (save.animations) {
for (var uuid in save.animations) {
var animation = Animator.animations.findInArray('uuid', save.animation)
if (!animation) {
animation = new Animation()
}
Animation.extend(save.animation)
} else if (reference.animation) {
//remove
var animation = Animator.animations.findInArray('uuid', reference.animation.uuid)
if (animation.remove) {
animation.remove()
}
}
if (save.keyframes && Animator.selected) {
var animation = false;
if (Animator.selected.uuid !== save.keyframes.animation) {
animation = Animator.animations.findInArray('uuid', save.keyframes.animation)
if (animation.select) {
var animation = reference.animations[uuid] ? Undo.getItemByUUID(Animator.animations, uuid) : null;
if (!animation) {
animation = new Animation()
animation.uuid = uuid
}
animation.extend(save.animations[uuid]).add(false)
if (save.animations[uuid].selected) {
animation.select()
}
}
var bone = Animator.selected.getBoneAnimator();
if (!bone || bone.uuid !== save.keyframes.bone) {
for (var uuid in Animator.selected.bones) {
if (uuid === save.keyframes.bone) {
bone = Animator.selected.bones[uuid]
bone.select()
for (var uuid in reference.animations) {
if (!save.animations[uuid]) {
var animation = Undo.getItemByUUID(Animator.animations, uuid)
if (animation) {
animation.remove(false)
}
}
}
}
function getKeyframe(uuid) {
var i = 0;
while (i < Timeline.keyframes.length) {
if (Timeline.keyframes[i].uuid === uuid) {
return Timeline.keyframes[i];
}
i++;
if (save.keyframes) {
var animation = Animator.selected;
if (!animation || animation.uuid !== save.keyframes.animation) {
animation = Animator.animations.findInArray('uuid', save.keyframes.animation)
if (animation.select && Animator.open && is_session) {
animation.select()
}
}
var added = 0;
for (var uuid in save.keyframes) {
if (uuid.length === 36 && save.keyframes.hasOwnProperty(uuid)) {
var data = save.keyframes[uuid]
var kf = getKeyframe(uuid)
if (kf) {
kf.extend(data)
} else {
kf = new Keyframe(data)
kf.parent = bone;
kf.uuid = uuid;
Timeline.keyframes.push(kf)
added++;
if (animation) {
var bone = Animator.selected.getBoneAnimator();
if (!bone || bone.uuid !== save.keyframes.bone) {
for (var uuid in Animator.selected.bones) {
if (uuid === save.keyframes.bone) {
bone = Animator.selected.bones[uuid]
if (bone.select && Animator.open && is_session) {
bone.select()
}
}
}
}
}
if (bone) {
for (var uuid in reference.keyframes) {
if (uuid.length === 36 && reference.keyframes.hasOwnProperty(uuid) && !save.keyframes.hasOwnProperty(uuid)) {
var kf = getKeyframe(uuid)
if (kf) {
kf.remove()
function getKeyframe(uuid) {
var i = 0;
while (i < Timeline.keyframes.length) {
if (Timeline.keyframes[i].uuid === uuid) {
return Timeline.keyframes[i];
}
i++;
}
}
var added = 0;
for (var uuid in save.keyframes) {
if (uuid.length === 36 && save.keyframes.hasOwnProperty(uuid)) {
var data = save.keyframes[uuid]
var kf = getKeyframe(uuid)
if (kf) {
kf.extend(data)
} else {
kf = new Keyframe(data)
kf.parent = bone;
kf.uuid = uuid;
Timeline.keyframes.push(kf)
added++;
}
}
}
for (var uuid in reference.keyframes) {
if (uuid.length === 36 && reference.keyframes.hasOwnProperty(uuid) && !save.keyframes.hasOwnProperty(uuid)) {
var kf = getKeyframe(uuid)
if (kf) {
kf.remove()
}
}
}
if (added) {
Vue.nextTick(Timeline.update)
}
updateKeyframeSelection()
Animator.preview()
}
}
if (added) {
Vue.nextTick(Timeline.update)
}
updateKeyframeSelection()
Animator.preview()
}
if (save.display_slots) {

@ -275,6 +275,9 @@ Array.prototype.allEqual = function(s) {
}
return true;
}
Array.prototype.random = function() {
return this[Math.floor(Math.random()*this.length)]
}
//Object
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

315
js/uv.js

@ -67,6 +67,7 @@ class UVEditor {
constructor(id, headline, toolbar) {
this.face = 'north';
this.size = 320;
this.zoom = 1;
this.grid = 16;
this.id = id
this.autoGrid = true;
@ -95,12 +96,15 @@ class UVEditor {
uv_dialog.select(scope.id, event)
})
}
this.jquery.viewport = $('<div id="uv_viewport"></div>')
this.jquery.transform_info = $('<div class="uv_transform_info"></div>')
this.jquery.main.append(this.jquery.transform_info)
this.jquery.main.append(this.jquery.viewport)
this.jquery.frame = $('<div id="uv_frame" style="background-repeat: no-repeat;"><div id="uv_size"><div class="uv_size_handle"></div></div></div>')
this.jquery.size = this.jquery.frame.find('div#uv_size')
this.jquery.main.append(this.jquery.frame)
this.jquery.frame.append('<div class="uv_transform_info" title="Transform indicators"></div>')
this.jquery.viewport.append(this.jquery.frame)
this.jquery.frame.css('background-repeat', 'no-repeat')
this.jquery.transform_info = this.jquery.frame.find('.uv_transform_info')
if (Blockbench.browser === 'firefox') {
this.jquery.frame.css('image-rendering', '-moz-crisp-edges')
}
@ -222,13 +226,6 @@ class UVEditor {
}
this.jquery.size.mouseenter(function() {
scope.displayMappingOverlay()
})
this.jquery.size.mouseleave(function() {
console.trace('RM')
$(this).find('.uv_mapping_overlay').remove()
})
if (toolbar) {
this.jquery.bar = $(Toolbars.main_uv.node)
@ -237,7 +234,7 @@ class UVEditor {
this.jquery.bar = $('')
}
var dragging_not_clicking = false;
this.jquery.size.resizable({
handles: "all",
maxHeight: 320,
@ -247,34 +244,45 @@ class UVEditor {
Undo.initEdit({cubes: selected, uv_only: true})
},
resize: function(event, ui) {
//ui.size.width = ui.originalSize.width + (ui.size.width - ui.originalSize.width) / scope.zoom
//ui.size.height = ui.originalSize.height + (ui.size.height - ui.originalSize.height) / scope.zoom
/*
ui.size.width = ui.size.width - ui.size.width % (scope.size/scope.grid) + (scope.size/scope.grid)/2;
ui.size.height = ui.size.height - ui.size.height % (scope.size/scope.grid) + (scope.size/scope.grid)/2;
*/
//var size = main_uv.height / main_uv.grid * main_uv.zoom
scope.save()
scope.displaySliders()
},
stop: function(event, ui) {
dragging_not_clicking = true;
Undo.finishEdit('uv_change')
scope.disableAutoUV()
scope.updateDragHandle(ui.position)
},
grid: [20,20]
}
})
this.jquery.size.draggable({
containment: 'parent',
start: function(event, ui) {
Undo.initEdit({cubes: selected, uv_only: true})
},
drag: function( event, ui ) {
var snapTolerance = 200//$(this).draggable('option', 'snapTolerance');
var topRemainder = ui.position.top % (scope.size/scope.grid);
var leftRemainder = ui.position.left % (scope.size/scope.grid);
var p = ui.position;
var o = ui.originalPosition
p.left = o.left + (p.left - o.left)
p.top = o.top + (p.top - o.top)
p.left = limitNumber(p.left, 0, scope.inner_size-scope.jquery.size.width()+1)
p.top = limitNumber(p.top, 0, scope.inner_size-scope.jquery.size.height()+1)
if (topRemainder <= snapTolerance) {
ui.position.top = ui.position.top - topRemainder;
}
if (leftRemainder <= snapTolerance) {
ui.position.left = ui.position.left - leftRemainder;
}
p.left = p.left - p.left % (scope.inner_size/scope.grid);
p.top = p.top - p.top % (scope.inner_size/scope.grid);
scope.save()
scope.displaySliders()
},
@ -304,15 +312,63 @@ class UVEditor {
}
})
this.jquery.frame.contextmenu(function(event) {
this.jquery.size.mouseenter(function() {
scope.displayMappingOverlay()
})
this.jquery.size.mouseleave(function() {
$(this).find('.uv_mapping_overlay').remove()
})
this.jquery.frame.click(function(event) {
if (!dragging_not_clicking) {
scope.reverseSelect(event)
}
dragging_not_clicking = false;
})
this.jquery.viewport.contextmenu(function(event) {
scope.contextMenu()
})
this.jquery.frame.mousedown(function(event) {
if (Toolbox.selected.paintTool) {
this.jquery.viewport.mousedown(function(event) {
if (Toolbox.selected.paintTool && event.which === 1) {
scope.startBrush(event)
}
})
this.jquery.viewport.on('mousewheel', function(e) {
if (e.ctrlKey) {
var n = (event.deltaY < 0) ? 0.1 : -0.1;
n *= scope.zoom
var number = limitNumber(scope.zoom + n, 1.0, 4.0)
if (Math.abs(number - scope.zoom) > 0.001) {
this.scrollLeft += (scope.inner_size * n / 2) * (event.offsetX / scope.jquery.frame.width());
this.scrollTop += (scope.inner_size * n / 2) * (event.offsetY / scope.jquery.frame.height());
}
scope.setZoom(number)
event.preventDefault()
e.preventDefault()
return false;
}
})
var dMWCoords = {x: 0, y: 0}
function dragMouseWheel(e) {
e.currentTarget.scrollLeft -= (e.pageX - dMWCoords.x)
e.currentTarget.scrollTop -= (e.pageY - dMWCoords.y)
dMWCoords = {x: e.pageX, y: e.pageY}
}
function dragMouseWheelStop(e) {
scope.jquery.viewport.off('mousemove', dragMouseWheel)
document.removeEventListener('mouseup', dragMouseWheelStop)
}
scope.jquery.viewport.on('mousedown', function(e) {
if (e.which === 2) {
scope.jquery.viewport.on('mousemove', dragMouseWheel)
document.addEventListener('mouseup', dragMouseWheelStop)
dMWCoords = {x: e.pageX, y: e.pageY}
e.preventDefault();
return false;
}
})
this.setSize(this.size)
return this;
}
@ -331,7 +387,7 @@ class UVEditor {
getBrushCoordinates(event, tex) {
var scope = this;
var multiplier = (Blockbench.entity_mode && tex) ? tex.res/Project.texture_width : 1
var pixel_size = scope.size / tex.res
var pixel_size = scope.inner_size / tex.res
return {
x: Math.floor(event.offsetX/scope.getPixelSize()*multiplier),
y: Math.floor(event.offsetY/scope.getPixelSize()*multiplier)
@ -398,12 +454,18 @@ class UVEditor {
Painter.stopBrush()
}
//Get
get inner_size() {
return this.size*this.zoom;
}
get inner_height() {
return this.height*this.zoom;
}
getPixelSize() {
if (Blockbench.entity_mode) {
this.grid = Project.texture_width
return this.size/this.grid
return this.inner_size/this.grid
} else {
return this.size/ (
return this.inner_size/ (
(typeof this.texture === 'object' && this.texture.res)
? this.texture.res
: this.grid
@ -428,6 +490,42 @@ class UVEditor {
getTexture() {
return selected[0].faces[this.face].getTexture()
}
reverseSelect(event) {
var scope = this;
if (!event.target.classList.contains('uv_size_handle') && !event.target.id === 'uv_frame') {
return this;
}
var matches = [];
var face_match;
var u = event.offsetX / main_uv.inner_size * 16;
var v = event.offsetY / main_uv.inner_height * 16;
elements.forEach(cube => {
for (var face in cube.faces) {
var uv = cube.faces[face].uv
if (uv && Math.isBetween(u, uv[0], uv[2]) && Math.isBetween(v, uv[1], uv[3]) && cube.faces[face].getTexture() === scope.texture) {
matches.safePush(cube)
if (!face_match) {
face_match = face
}
break;
}
}
})
if (matches.length) {
if (!Blockbench.entity_mode) {
main_uv.setFace(face_match)
}
if (!event.ctrlKey && !event.shiftKey) {
selected.empty();
}
matches.forEach(s => {
selected.safePush(s)
})
updateSelection()
scope.displayMappingOverlay()
}
return this;
}
forCubes(cb) {
var i = 0;
while (i < selected.length) {
@ -436,37 +534,63 @@ class UVEditor {
}
}
//Set
setSize(size, cancel_load) {
setSize(input_size, cancel_load) {
var old_size = this.size;
this.size = size
this.jquery.frame.width(size)
if (uv_dialog.editors !== undefined && this === uv_dialog.editors.single) {
this.jquery.main.width(size)
}
var size = input_size - (input_size % 16);
this.size = size;
this.jquery.frame.width(this.inner_size);
this.jquery.viewport.width(size+8);
this.jquery.main.width(size+8);
if (Blockbench.entity_mode) {
this.height = size / (Project.texture_width/Project.texture_height)
this.jquery.frame.height(this.height)
this.jquery.frame.height(this.inner_height)
this.jquery.viewport.height(this.height+8)
$('.panel#textures').css('top', 133+(size / (Project.texture_width/Project.texture_height))+'px')
if (old_size !== size) {
this.displayAllMappingOverlays(true)
}
} else {
this.height = size
this.jquery.frame.height(size)
this.jquery.frame.height(this.inner_size)
this.jquery.viewport.height(size+8)
this.jquery.size.resizable('option', 'maxHeight', size)
this.jquery.size.resizable('option', 'maxWidth', size)
this.jquery.size.resizable('option', 'grid', [size/this.grid, size/this.grid])
this.jquery.size.resizable('option', 'maxHeight', this.inner_size)
this.jquery.size.resizable('option', 'maxWidth', this.inner_size)
this.jquery.size.resizable('option', 'grid', [this.inner_size/this.grid, this.inner_size/this.grid])
}
for (var id in this.sliders) {
this.sliders[id].setWidth(size/(Blockbench.entity_mode?2:4)-3)
this.sliders[id].setWidth(size/(Blockbench.entity_mode?2:4)-1)
}
if (!cancel_load) {
this.loadData()
}
return this;
}
setZoom(zoom) {
var zoomed_size = this.size * zoom;
var size = zoomed_size - (zoomed_size % 16);
this.zoom = size/this.size
this.jquery.frame.width(this.inner_size);
if (Blockbench.entity_mode) {
this.jquery.frame.height(this.inner_height)
this.displayAllMappingOverlays(true)
} else {
this.jquery.frame.height(this.inner_size)
this.jquery.size.resizable('option', 'maxHeight', this.inner_size)
this.jquery.size.resizable('option', 'maxWidth', this.inner_size)
this.jquery.size.resizable('option', 'grid', [this.inner_size/this.grid, this.inner_size/this.grid])
}
if (this.zoom > 1) {
this.jquery.viewport.css('overflow', 'scroll scroll')
} else {
this.jquery.viewport.css('overflow', 'hidden')
}
this.loadData()
return this;
}
setGrid(grid, load) {
if (Blockbench.entity_mode) {
this.autoGrid = false;
@ -504,19 +628,14 @@ class UVEditor {
return this;
}
setFrameColor(black) {
if (black) {
this.jquery.size.css('box-shadow', '0 0 6px black')
} else {
this.jquery.size.css('box-shadow', '0 0 6px white')
}
this.jquery.size.toggleClass('dark_frame', black === true)
}
setToMainSlot() {
var scope = this;
$('.panel#uv').append(this.jquery.main)
this.jquery.main.on('mousewheel', function() {
this.jquery.main.on('mousewheel', function(e) {
if (Blockbench.entity_mode) {
} else {
if (!Blockbench.entity_mode && !e.ctrlKey) {
var faceIDs = {'north': 0, 'south': 1, 'west': 2, 'east': 3, 'up': 4, 'down': 5}
var id = faceIDs[scope.face]
event.deltaY > 0 ? id++ : id--;
@ -524,6 +643,7 @@ class UVEditor {
if (id === -1) id = 5
$('input#'+getKeyByValue(faceIDs, id)+'_radio').prop("checked", true)
scope.loadSelectedFace()
e.preventDefault()
}
})
this.jquery.frame.on('dblclick', function() {
@ -571,20 +691,20 @@ class UVEditor {
selected.forEach(function(obj) {
obj.uv_offset = [
Math.round(scope.jquery.size.position().left / (scope.size/Project.texture_width) * 8) / 8,
Math.round(scope.jquery.size.position().top / (scope.size/Project.texture_width) * 8) / 8
Math.round(scope.jquery.size.position().left / (scope.inner_size/Project.texture_width) * 8) / 8,
Math.round(scope.jquery.size.position().top / (scope.inner_size/Project.texture_width) * 8) / 8
]
Canvas.updateUV(obj)
})
} else {
var trim = v => Math.round(v*1000+0.3)/1000;
var pixelSize = this.size/16
var pixelSize = this.inner_size/16
var left = trim( this.jquery.size.position().left / pixelSize);
var top = trim( this.jquery.size.position().top / pixelSize * (Project.texture_width/Project.texture_height));
var left2= Math.clamp(trim( (this.jquery.size.width()) / pixelSize + left), 0, 16);
var top2 = Math.clamp(trim( (this.jquery.size.height()) / pixelSize + top), 0, 16);
var left2= Math.clamp(trim( Math.round(this.jquery.size.width()) / pixelSize + left), 0, 16);
var top2 = Math.clamp(trim( Math.round(this.jquery.size.height()) / pixelSize + top), 0, 16);
var uvTag = this.getUVTag()
@ -595,11 +715,7 @@ class UVEditor {
top2 = [top, top = top2][0];
}
var uvArr = [left, top, left2, top2]
uvArr.forEach(function(s, i) {
if (s === 15.9) {
uvArr[i] = 16
}
})
selected.forEach(function(obj) {
obj.faces[scope.face].uv = uvArr.slice()
Canvas.updateUV(obj)
@ -623,7 +739,12 @@ class UVEditor {
displayTexture(face) {
var tex = face.getTexture()
if (!tex || typeof tex !== 'object' || tex.error) {
this.displayEmptyTexture()
this.jquery.frame.css('background-color', 'var(--color-back)').css('background-image', 'none')
this.texture = false;
this.setFrameColor()
if (this.autoGrid || Blockbench.entity_mode) {
this.setGrid(16, false)
}
} else {
this.setFrameColor(tex.dark_box)
var css = 'url("'+tex.source.split('\\').join('\\\\').replace(/ /g, '%20')+'")'
@ -634,11 +755,15 @@ class UVEditor {
this.jquery.frame.css('background-size', 'cover')
}
this.texture = tex;
tex.select()
if (this.autoGrid || Blockbench.entity_mode) {
this.setGrid(tex.res, false)
}
}
if (!tex || typeof tex !== 'object') {
unselectTextures()
} else {
tex.select()
}
if (Blockbench.entity_mode) {
this.setSize(this.size, true)
}
@ -658,14 +783,6 @@ class UVEditor {
this.jquery.transform_info.append('<b>'+ref.rotation+'</b>')
}
}
displayEmptyTexture() {
this.jquery.frame.css('background-color', 'var(--color-back)').css('background-image', 'none')
this.texture = false;
this.setFrameColor()
if (this.autoGrid) {
this.grid = 16
}
}
displayFrame() {
var scope = this;
if (Blockbench.entity_mode) {
@ -675,10 +792,10 @@ class UVEditor {
var width = (size_tag[0] + size_tag[2])*2
width = limitNumber(width, 0, Project.texture_width)
width = width/Project.texture_width*scope.size
width = width/Project.texture_width*scope.inner_size
var x = limitNumber(uvTag[0], 0, Project.texture_width)
x *= scope.size/Project.texture_width
x *= scope.inner_size/Project.texture_width
this.jquery.size.width(width)
this.jquery.size.css('left', x+'px')
@ -686,11 +803,11 @@ class UVEditor {
var height = size_tag[2] + size_tag[1]
height = limitNumber(height, 0, Project.texture_height)
height = height/Project.texture_height*scope.size
height = height/Project.texture_height*scope.inner_size
height *= Project.texture_height/Project.texture_width
var y = limitNumber(uvTag[1], 0, Project.texture_height)
y *= scope.size/Project.texture_height
y *= scope.inner_size/Project.texture_height
y *= Project.texture_height/Project.texture_width
this.jquery.size.height(height)
@ -698,7 +815,7 @@ class UVEditor {
} else {
var uvTag = this.getUVTag(selected[0])
var pixels = this.size/16
var pixels = this.inner_size/16
//X
var width = limitNumber(uvTag[2]-uvTag[0], -16, 16)
@ -729,7 +846,7 @@ class UVEditor {
var scope = this;
var sides = this.getMappingOverlay()
$(scope.jquery.size).find('.uv_mapping_overlay').remove()
$(scope.jquery.size).find('.mapping_overlay_cube').remove()
scope.jquery.size.append(sides)
return this;
@ -746,8 +863,8 @@ class UVEditor {
}
x *= pixels;
y *= pixels;
width = limitNumber(width *pixels + x, 0, scope.size) - x;
height = limitNumber(height*pixels + y, 0, scope.height)- y;
width = limitNumber(width *pixels + x, 0, scope.inner_size) - x;
height = limitNumber(height*pixels + y, 0, scope.inner_height)- y;
sides.append($(`<div class="uv_mapping_overlay"
style="left: ${x}px; top: ${y}px;
@ -774,7 +891,7 @@ class UVEditor {
elements.forEach(cube => {
var size = cube.size(undefined, true)
var hash = `${cube.uv_offset[0]}_${cube.uv_offset[1]}_${size[0]}_${size[1]}_${size[2]}`
var c = scope.jquery.frame.find(`.mapping_overlay_cube:not(.${cycle})[size_hash="${hash}"]`).first()
var c = scope.jquery.frame.find(`> .mapping_overlay_cube:not(.${cycle})[size_hash="${hash}"]`).first()
if (force_reload || !c.length) {
var sides = scope.getMappingOverlay(cube, true)
sides.addClass(cycle)
@ -819,7 +936,7 @@ class UVEditor {
}
contextMenu() {
var scope = this;
if (Blockbench.entity_mode) return;
//if (Blockbench.entity_mode) return;
this.reference_face = selected[0].faces[scope.face]
this.menu.open(event, this)
return this;
@ -1228,6 +1345,12 @@ class UVEditor {
}
}
UVEditor.prototype.menu = new Menu([
{name: 'menu.view.zoom', id: 'zoom', condition: isApp, icon: 'search', children: [
'zoom_in',
'zoom_out',
'zoom_reset'
]},
'_',
'copy',
'paste',
/*
@ -1239,7 +1362,7 @@ class UVEditor {
editor.paste(event)
Undo.finishEdit('uv_paste')
}},*/
{icon: 'photo_size_select_large', name: 'menu.uv.mapping', children: function(editor) { return [
{icon: 'photo_size_select_large', name: 'menu.uv.mapping', condition: () => !Blockbench.entity_mode, children: function(editor) { return [
{icon: editor.reference_face.enabled!==false ? 'check_box' : 'check_box_outline_blank', name: 'menu.uv.mapping.export', click: function(editor) {
Undo.initEdit({cubes: selected, uv_only: true})
editor.toggleUV(event)
@ -1295,13 +1418,14 @@ class UVEditor {
]}},
{
icon: (editor) => (editor.reference_face.tint ? 'check_box' : 'check_box_outline_blank'),
condition: () => !Blockbench.entity_mode,
name: 'menu.uv.tint', click: function(editor) {
Undo.initEdit({cubes: selected, uv_only: true})
editor.switchTint(selected[0].faces[editor.face].tint)
Undo.finishEdit('face_tint')
}
},
{icon: 'collections', name: 'menu.uv.texture', children: function() {
{icon: 'collections', name: 'menu.uv.texture', condition: () => !Blockbench.entity_mode, children: function() {
var arr = [
{icon: 'crop_square', name: 'menu.cube.texture.blank', click: function(editor, event) {
Undo.initEdit({cubes: selected})
@ -1351,9 +1475,8 @@ const uv_dialog = {
down: new UVEditor('down', true).appendTo('#uv_dialog_all')
}
var size = $(window).height() - 200
size = size - (size % 16)
uv_dialog.editors.single.setSize(size)
uv_dialog.editors.single.jquery.main.css('margin-left', 'auto').css('margin-right', 'auto').css('width', size+'px')
uv_dialog.editors.single.jquery.main.css('margin-left', 'auto').css('margin-right', 'auto')//.css('width', (size+10)+'px')
uv_dialog.editors.up.jquery.main.css('margin-left', '276px').css('clear', 'both')
uv_dialog.isSetup = true
@ -1476,7 +1599,6 @@ const uv_dialog = {
BarItems.uv_grid.set(uv_dialog.editors.single.gridSelectOption)
var max_size = $(window).height() - 200
max_size = max_size - (max_size % 16)
if (max_size < uv_dialog.editors.single.size ) {
uv_dialog.editors.single.setSize(max_size)
uv_dialog.editors.single.jquery.main.css('margin-left', 'auto').css('margin-right', 'auto').css('width', max_size+'px')
@ -1503,29 +1625,23 @@ const uv_dialog = {
y: obj.height()
}
if (uv_dialog.single) {
var menu_gap = Blockbench.entity_mode ? 66 : 130
var menu_gap = Blockbench.entity_mode ? 66 : 154
var editor_size = size.x
size.y = (size.y - menu_gap) * (Blockbench.entity_mode ? Project.texture_width/Project.texture_height : 1)
if (size.x > size.y) {
editor_size = size.y
}
editor_size = editor_size - (editor_size % 16)
uv_dialog.editors.single.setSize(editor_size)
} else {
var centerUp = false
if (size.x < size.y/1.2) {
//2 x 3 0.83 - 7.2
if (size.y*1.4 > size.x) {
var editor_size = limitNumber(size.x / 2 - 20, 80, $(window).height()/3-120)
editor_size = limitNumber(editor_size, 80, (size.y-64)/3-77)
} else {
var editor_size = size.y / 3 - 96 - 48
}
var editor_size = limitNumber(size.x / 2 - 35, 80, $(window).height()/3-120)
editor_size = limitNumber(editor_size, 80, (size.y-64)/3-120)
} else {
//4 x 2
var y_margin = 150
var editor_size = limitNumber(size.x/4-20, 16, size.y/2-y_margin)
var y_margin = 130
var editor_size = limitNumber(size.x/4-25, 16, size.y/2-y_margin)
centerUp = true
}
editor_size = editor_size - (editor_size % 16)
@ -1647,9 +1763,13 @@ BARS.defineActions(function() {
category: 'uv',
condition: () => !Blockbench.entity_mode && selected.length,
min: 0, max: 270, step: 90, width: 80,
onChange: function(slider) {
onBefore: () => {
Undo.initEdit({cubes: selected, uv_only: true})
},
onChange: function(slider) {
uv_dialog.forSelection('rotate')
},
onAfter: () => {
Undo.finishEdit('uv rotate')
}
})
@ -1658,6 +1778,7 @@ BARS.defineActions(function() {
category: 'uv',
condition: () => !Blockbench.entity_mode && selected.length,
width: 60,
value: 'auto',
options: {
auto: true,
'16': '16x16',

@ -11,6 +11,9 @@ $(document).ready(function() {
window.open(event.target.href, '_blank');
});
})
/*setInterval(function() {
Prop.zoom = Math.round(devicePixelRatio*100)
}, 500)*/
function tryLoadPOSTModel() {
if ($('#post_model').text() !== '') {

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Angelpunkt auf der Z Achse verschieben",
"action.brush_mode": "Pinselmodus",
"action.brush_mode.desc": "Modus des Pinsels",
"action.brush_color": "Farbe",
"action.brush_color.desc": "Farbe des Pinsels",
"action.slider_brush_size": "Radius",
"action.slider_brush_size.desc": "Radius des Pinsels in Pixeln",
"action.slider_brush_opacity": "Deckkraft",
@ -377,7 +375,7 @@
"action.export_optifine_full": "OptiFine JEM exportieren",
"action.export_optifine_full.desc": "Ein vollständiges Entitymodell für OptiFine exportieren",
"action.export_obj": "OBJ Modell exportieren",
"action.export_obj.desc": "Ein Wavefront OBJ Modell exportieren zur Weiterverwendung in anderen Programmen oder zum Hochladen auf Sketchfab",
"action.export_obj.desc": "Ein Wavefront OBJ Modell exportieren zum Rendern oder für Spiel-Engines",
"action.save": "Speichern",
"action.save.desc": "Das aktuelle Modell und die aktuellen Texturen speichern",
"action.settings_window": "Einstellungen...",
@ -586,7 +584,6 @@
"panel.outliner": "Outliner",
"panel.options": "Drehung",
"panel.options.angle": "Winkel",
"panel.options.origin": "Angelpunkt",
"uv_editor.title": "UV Bearbeiten",
"uv_editor.all_faces": "Alle",
"uv_editor.no_faces": "Keine",
@ -854,5 +851,53 @@
"action.export_bbmodel": "Blockbench-Projekt exportieren",
"action.export_bbmodel.desc": "Exportiere ein Blockbench Projekt mit allen Elementen, Texturen und Animationen",
"action.export_asset_archive": "ZIP-Ordner herunterladen",
"action.export_asset_archive.desc": "Lädt ein Archiv mit dem Modell und allen benötigten Texturen herunter"
"action.export_asset_archive.desc": "Lädt ein Archiv mit dem Modell und allen benötigten Texturen herunter",
"action.upload_sketchfab": "Teilen auf Sketchfab",
"message.sketchfab.name_or_token": "Bitte gebe deinen Sketchfab Schlüssel und einen Namen ein",
"dialog.sketchfab_uploader.title": "Modell auf Sketchfab hochladen",
"dialog.sketchfab_uploader.token": "API Schlüssel",
"dialog.sketchfab_uploader.about_token": "Der Schlüssel wird benötigt, um Blockbench mit deinem Sketchfab Account zu verbinden. Du findest ihn unter sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Name",
"dialog.sketchfab_uploader.description": "Beschreibung",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Schlüssel",
"settings.sketchfab_token.desc": "Schlüssel, der Blockbench erlaubt, Modelle auf Sketchfab hochzuladen",
"panel.color": "Farbauswahl",
"data.origin": "Angelpunkt",
"message.sketchfab.success": "Modell erfolgreich hochgeladen",
"message.sketchfab.error": "Upload auf Sketchfab fehlgeschlagen",
"settings.outliner_colors": "Outliner Farben",
"settings.outliner_colors.desc": "Elementfarben im Outliner anzeigen",
"action.upload_sketchfab.desc": "Modell auf Sketchfab hochladen",
"action.element_colors": "Elementfarben",
"action.element_colors.desc": "Elementfarben im Outliner anzeigen",
"texture.error.file": "Datei nicht gefunden",
"texture.error.invalid": "Datei fehlerhaft",
"texture.error.ratio": "Ungültiges Seitenverhältnis",
"texture.error.parent": "Textur durch Elternmodell",
"message.recover_backup.title": "Modell wiederherstellen",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -15,6 +15,7 @@
"data.cubes": "Cubes",
"data.group": "Group",
"data.texture": "Texture",
"data.origin": "Origin",
"data.plugin": "Plugin",
"data.preview": "Preview",
"data.toolbar": "Toolbar",
@ -72,6 +73,8 @@
"message.rotation_limit.message": "Rotations are limited by Minecraft to one axis and 22.5 degree increments. Rotating on a different axis will clear all rotations on the other axes. Disable the option \"Restricted Rotation\" if you are modeling for other purposes and need free rotations.",
"message.file_not_found.title": "File Not Found",
"message.file_not_found.message": "Blockbench could not find the requested file. Make sure it is saved locally and not in a cloud.",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.screenshot.title": "Screenshot",
"message.screenshot.message": "Screenshot captured.",
"message.screenshot.clipboard": "Clipboard",
@ -145,6 +148,7 @@
"message.load_plugin_app": "Do you want to allow this plugin to make changes to your PC? Only load plugins from people you trust.",
"message.load_plugin_web": "Do you want to load this plugin? Only load plugins from people you trust.",
"message.plugin_reload": "Reloaded %0 local plugins",
"message.install_plugin": "Installing the plugin %0",
"message.preset_no_info": "Preset does not contain information for this slot",
"message.restart_to_update": "Restart Blockbench to apply changes",
"message.save_file": "Saved as %0",
@ -154,6 +158,9 @@
"message.rename_animation": "Rename Animation",
"message.animation_update_var": "Animation Update Variable",
"message.untextured": "This surface does not have a texture",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"message.no_animation_selected": "You have to select an animation to do this",
"message.bone_material": "Change bone material",
@ -161,6 +168,9 @@
"message.duplicate_groups.title": "Bone Name Duplicate",
"message.duplicate_groups.message": "The name of this bone exists on multiple bones. This can cause problems.",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.project.title": "Project",
"dialog.project.name": "File Name",
"dialog.project.parent": "Parent Model",
@ -233,12 +243,14 @@
"dialog.create_texture.folder": "Folder",
"dialog.create_texture.template": "Template",
"dialog.create_texture.compress": "Compress Template",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_texture.resolution": "Resolution",
"dialog.create_gif.title": "Record GIF",
"dialog.create_gif.length": "Length (Seconds)",
"dialog.create_gif.fps": "FPS",
"dialog.create_gif.compression": "Compression Amount",
"dialog.create_gif.turn": "Turntable Speed",
"dialog.create_gif.play": "Start Animation",
"dialog.shift_uv.title": "Shift UV",
@ -258,6 +270,13 @@
"dialog.update.installed": "Installed Version",
"dialog.update.update": "Update",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"dialog.settings.settings": "Settings",
"dialog.settings.keybinds": "Keybindings",
"dialog.settings.layout": "Layout",
@ -340,6 +359,8 @@
"settings.transparency.desc": "Render transparent textures transparent",
"settings.texture_fps": "Animated Texture FPS",
"settings.texture_fps.desc": "Frames per second for animated textures",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"settings.base_grid": "Small Grid",
"settings.base_grid.desc": "Show small grid and axes",
@ -394,6 +415,8 @@
"settings.obj_textures.desc": "Export textures when exporting OBJ file",
"settings.credit": "Credit Comment",
"settings.credit.desc": "Add a credit comment to exported files",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"settings.default_path": "Default Path",
"settings.default_path.desc": "Folder from where Blockbench loads default textures",
"settings.image_editor": "Image Editor",
@ -455,8 +478,6 @@
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Color",
"action.fill_mode.cube": "Cube",
"action.brush_color": "Color",
"action.brush_color.desc": "Color of the brush",
"action.slider_brush_size": "Size",
"action.slider_brush_size.desc": "Radius of the brush in pixels",
"action.slider_brush_opacity": "Opacity",
@ -533,7 +554,9 @@
"action.export_optifine_full": "Export OptiFine JEM",
"action.export_optifine_full.desc": "Export a full OptiFine entity model",
"action.export_obj": "Export OBJ Model",
"action.export_obj.desc": "Export a Wavefront OBJ model for use in other programs or to upload to Sketchfab",
"action.export_obj.desc": "Export a Wavefront OBJ model for rendering or game engines",
"action.upload_sketchfab": "Sketchfab Upload",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.save": "Save",
"action.save.desc": "Save the current model and textures",
"action.settings_window": "Settings...",
@ -546,6 +569,8 @@
"action.donate.desc": "Donate to Blockbench",
"action.action_control": "Action Control",
"action.action_control.desc": "Search and execute any available action",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keybindings": "Reset Keybindings",
"action.reset_keybindings.desc": "Reset all keybindings to Blockbench's defaults",
@ -591,6 +616,8 @@
"action.sort_outliner.desc": "Sort the outliner alphabetically",
"action.local_move": "Move Relative",
"action.local_move.desc": "Move rotated elements on their own axes if possible",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"action.select_window": "Select...",
"action.select_window.desc": "Search and select cubes based on their properties",
"action.invert_selection": "Invert Selection",
@ -749,6 +776,8 @@
"action.slider_animation_length.desc": "Change the length of the selected animation",
"action.slider_keyframe_time": "Timecode",
"action.slider_keyframe_time.desc": "Change the timecode of the selected keyframes",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"action.select_all_keyframes": "Select All Keyframes",
"action.select_all_keyframes.desc": "Select all keyframes of the current bone",
"action.delete_keyframes": "Delete Keyframes",
@ -760,6 +789,7 @@
"action.next_keyframe": "Next Keyframe",
"action.next_keyframe.desc": "Jump to the next keyframe",
"timeline.rotation": "Rotation",
"timeline.position": "Position",
"timeline.scale": "Scale",
@ -859,9 +889,15 @@
"switches.mirror": "Mirror UV",
"switches.autouv": "Auto UV",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"panel.uv": "UV",
"panel.display": "Display",
"panel.textures": "Textures",
"panel.color": "Color",
"panel.outliner": "Outliner",
"panel.animations": "Animations",
"panel.keyframe": "Keyframe",
@ -906,6 +942,20 @@
"direction.top": "Top",
"direction.bottom": "Bottom",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session",
"display.slot.third_right": "Thirdperson Right",
"display.slot.third_left": "Thirdperson Left",
"display.slot.first_right": "Firstperson Right",

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Mover origen en el eje Z",
"action.brush_mode": "Modo Pincel",
"action.brush_mode.desc": "Modo del pincel",
"action.brush_color": "Color",
"action.brush_color.desc": "Color del pincel",
"action.slider_brush_size": "Tamaño",
"action.slider_brush_size.desc": "Radio del pincel en píxeles",
"action.slider_brush_opacity": "Opacidad",
@ -377,7 +375,7 @@
"action.export_optifine_full": "Exportar a OptiFine JEM",
"action.export_optifine_full.desc": "Exportar un modelo completo de entidad de OptiFine",
"action.export_obj": "Exportar Modelo OBJ",
"action.export_obj.desc": "Crear un modelo WaveFront OBJ para usar en otros programas o para subir a Sketchfab",
"action.export_obj.desc": "Crear un modelo WaveFront OBJ para usar en otros programas",
"action.save": "Guardar",
"action.save.desc": "Guardar el modelo actual y las texturas",
"action.settings_window": "Ajustes...",
@ -586,7 +584,6 @@
"panel.outliner": "Esquema",
"panel.options": "Rotación",
"panel.options.angle": "Ángulo",
"panel.options.origin": "Origen",
"uv_editor.title": "Editor de UV",
"uv_editor.all_faces": "Todo",
"uv_editor.no_faces": "Ninguno",
@ -849,10 +846,58 @@
"action.previous_keyframe.desc": "Salta al frame anterior",
"action.next_keyframe": "Frame Siguiente",
"action.next_keyframe.desc": "Salta al frame siguiente",
"message.outdated_client.title": "Outdated client",
"message.outdated_client.message": "Please update to the latest version of Blockbench to do this.",
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"message.outdated_client.title": "Programa desactualizado",
"message.outdated_client.message": "Por favor actualiza a la última versión de Blockbench para hacer esto.",
"action.export_bbmodel": "Exportar Proyecto de Blockbench",
"action.export_bbmodel.desc": "Exporta un proyecto de Blockbench con todos los cubos, texturas y animaciones",
"action.export_asset_archive": "Descargar Archivo",
"action.export_asset_archive.desc": "Descarga un archivo con el modelo y todas las texturas en él",
"action.upload_sketchfab": "Subir a Sketchfab",
"message.sketchfab.name_or_token": "Por favor, introduce tu token de Sketchfab y un nombre",
"dialog.sketchfab_uploader.title": "Subir Modelo de Sketchfab",
"dialog.sketchfab_uploader.token": "Token de API",
"dialog.sketchfab_uploader.about_token": "El token es usado para conectar Blockbench a tu cuenta de Sketchfab. Puedes encontrarlo en sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Nombre del Modelo",
"dialog.sketchfab_uploader.description": "Descripción",
"dialog.sketchfab_uploader.tags": "Etiquetas",
"settings.sketchfab_token": "Token de Sketchfab",
"settings.sketchfab_token.desc": "Token para autorizar a Blockbench a subir a tu cuenta de Sketchfab",
"panel.color": "Color",
"data.origin": "Origen",
"message.sketchfab.success": "Modelo subido con éxito",
"message.sketchfab.error": "La subida del modelo a Sketchfab ha fallado",
"settings.outliner_colors": "Colores del Borde",
"settings.outliner_colors.desc": "Muestra los colores de cubo en el borde",
"action.upload_sketchfab.desc": "Subir tu modelo a Sketchfab",
"action.element_colors": "Colores de Cubo",
"action.element_colors.desc": "Muestra los colores de cubo en el borde",
"texture.error.file": "Archivo no encontrado",
"texture.error.invalid": "Archivo inválido",
"texture.error.ratio": "Aspecto de ratio inválido",
"texture.error.parent": "Archivo de textura proveído por el modelo padre",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -198,12 +198,12 @@
"layout.font.headline": "Police de titre",
"about.version": "Version:",
"about.creator": "Créateur:",
"about.website": "Site internet:",
"about.website": "Traduction française : HookDonn_\nSite internet:",
"about.bugtracker": "Logiciel de suivi des bugs",
"about.electron": "Cette application est construite avec Electron, un cadre pour la création d'applications natives avec des technologies Web telles que Javascript, HTML et CSS.",
"about.vertex_snap": "Vertex Snapping est basé sur un plugin de SirBenet",
"about.icons": "Packs dicônes :",
"about.libraries": "Bibliothèques",
"about.libraries": "Bibliothèques :",
"settings.category.general": "Général",
"settings.category.preview": "Prévisualisation ",
"settings.category.grid": "Grille",
@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Déplacer lorigine sur laxe Z",
"action.brush_mode": "Mode",
"action.brush_mode.desc": "Mode peinture",
"action.brush_color": "Couleur",
"action.brush_color.desc": "Couleur du pinceau",
"action.slider_brush_size": "Taille",
"action.slider_brush_size.desc": "Rayon du pinceau en pixels",
"action.slider_brush_opacity": "Opacité",
@ -377,7 +375,7 @@
"action.export_optifine_full": "Exporter OptiFine JEM",
"action.export_optifine_full.desc": "Exporter un modèle complet d'entité OptiFine",
"action.export_obj": "Exporter un modèle OBJ",
"action.export_obj.desc": "Exporter un modèle OBJ Wavefront pour l'utiliser dans d'autres programmes ou pour le télécharger dans Sketchfab",
"action.export_obj.desc": "Exporter un modèle OBJ Wavefront pour l'utiliser dans d'autres programmes",
"action.save": "Sauvegarder",
"action.save.desc": "Enregistrer le modèle actuel et les textures",
"action.settings_window": "Réglages...",
@ -586,7 +584,6 @@
"panel.outliner": "Liste des blocs",
"panel.options": "Rotation",
"panel.options.angle": "Angle",
"panel.options.origin": "Origne",
"uv_editor.title": "Éditeur UV",
"uv_editor.all_faces": "Toutes",
"uv_editor.no_faces": "Aucun",
@ -819,40 +816,88 @@
"action.color_picker.desc": "Outil pour choisir la couleur des pixels sur votre texture",
"action.open_backup_folder": "Ouvrir le dossier de sauvegarde",
"action.open_backup_folder.desc": "Ouvre le dossier de sauvegarde de Blockbench",
"switches.mirror": "Mirror UV",
"language_name": "English",
"message.plugin_reload": "Reloaded %0 local plugins",
"settings.brightness": "Brightness",
"settings.brightness.desc": "Brightness of the preview. Default is 50",
"menu.preview.perspective.reset": "Reset Camera",
"action.fill_mode": "Fill Mode",
"action.fill_mode.desc": "Mode of the fill tool",
"switches.mirror": "Miroir UV",
"language_name": "Anglais",
"message.plugin_reload": "Recharger %0 plugins locaux",
"settings.brightness": "Luminosité",
"settings.brightness.desc": "Luminosité de l'aperçu. La valeur par défaut est 50",
"menu.preview.perspective.reset": "Réinitialiser la camera",
"action.fill_mode": "Mode de remplissage",
"action.fill_mode.desc": "Mode de l'outil de remplissage",
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Color",
"action.fill_mode.color": "Couleur",
"action.fill_mode.cube": "Cube",
"action.toggle_mirror_uv": "Mirror UV",
"action.toggle_mirror_uv.desc": "Toggle the UV mirroring on the X axis of the selected cubes.",
"action.toggle_uv_overlay": "Toggle UV Overlay",
"action.toggle_uv_overlay.desc": "When enabled, displays all UV mapping overlays above the texture.",
"menu.texture.blank": "Apply to Untextured Faces",
"dialog.scale.select_overflow": "Select Overflow",
"dialog.create_texture.compress": "Compress Template",
"action.action_control": "Action Control",
"action.action_control.desc": "Search and execute any available action",
"keybindings.recording": "Recording Keybinding",
"keybindings.press": "Press a key or key combination or click anywhere on the screen to record your keybinding.",
"action.pivot_tool": "Pivot Tool",
"action.pivot_tool.desc": "Tool to change the pivot point of cubes and bones",
"action.slider_animation_speed": "Playback Speed",
"action.slider_animation_speed.desc": "Playback speed of the timeline in percent",
"action.previous_keyframe": "Previous Keyframe",
"action.previous_keyframe.desc": "Jump to the previous keyframe",
"action.next_keyframe": "Next Keyframe",
"action.next_keyframe.desc": "Jump to the next keyframe",
"message.outdated_client.title": "Outdated client",
"message.outdated_client.message": "Please update to the latest version of Blockbench to do this.",
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.toggle_mirror_uv": "Miroir UV",
"action.toggle_mirror_uv.desc": "Changer la mise en miroir UV sur l'axe X des cubes sélectionnés.",
"action.toggle_uv_overlay": "Changer le recouvrement UV",
"action.toggle_uv_overlay.desc": "Quand c'est activé, affiche toutes les superpositions de cartographie UV au-dessus de la texture.",
"menu.texture.blank": "Appliquer aux faces non texturées",
"dialog.scale.select_overflow": "Sélectionnez débordement",
"dialog.create_texture.compress": "Compresser le modèle",
"action.action_control": "Contrôle d'action",
"action.action_control.desc": "Rechercher et exécuter toute action disponible",
"keybindings.recording": "Enregistrement des touches de clavier",
"keybindings.press": "Appuyez sur une touche ou une combinaison de touches ou cliquez n'importe où sur l'écran pour enregistrer votre raccourci clavier.",
"action.pivot_tool": "Outil de pivot",
"action.pivot_tool.desc": "Outil pour changer le point de pivot des cubes et des os",
"action.slider_animation_speed": "Vitesse de lecture",
"action.slider_animation_speed.desc": "Vitesse de lecture de la chronologie en pourcentage",
"action.previous_keyframe": "Image clé précédente",
"action.previous_keyframe.desc": "Aller à l'image clé précédente",
"action.next_keyframe": "Image clé suivante",
"action.next_keyframe.desc": "Passer à l'image clé suivante",
"message.outdated_client.title": "Client plus à jour",
"message.outdated_client.message": "Veuillez effectuer la mise à jour vers la dernière version de Blockbench.",
"action.export_bbmodel": "Projet d'exportation Blockbench",
"action.export_bbmodel.desc": "Exporter un projet Blockbench avec tous les cubes, textures et animations",
"action.export_asset_archive": "Télécharger une archive",
"action.export_asset_archive.desc": "Télécharger une archive avec le modèle et toutes les textures qu'elle contient",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Couleur\n",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "原点をZ軸上に移動する",
"action.brush_mode": "ブラシモード",
"action.brush_mode.desc": "ブラシモード",
"action.brush_color": "カラー",
"action.brush_color.desc": "ブラシのカラー",
"action.slider_brush_size": "サイズ",
"action.slider_brush_size.desc": "ブラシの半径",
"action.slider_brush_opacity": "不透明度",
@ -586,7 +584,6 @@
"panel.outliner": "Outliner",
"panel.options": "回転",
"panel.options.angle": "角度",
"panel.options.origin": "原点",
"uv_editor.title": "UVエディタ",
"uv_editor.all_faces": "すべて",
"uv_editor.no_faces": "なし",
@ -854,5 +851,53 @@
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Color",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Beweeg Oorsprong op de Z-as",
"action.brush_mode": "Mode",
"action.brush_mode.desc": "Modus van de kwast",
"action.brush_color": "Kleur",
"action.brush_color.desc": "Kleur van de kwast",
"action.slider_brush_size": "Grootte",
"action.slider_brush_size.desc": "Formaat van de kwast",
"action.slider_brush_opacity": "Doorzichtigheid",
@ -377,7 +375,7 @@
"action.export_optifine_full": "Exporteer OptiFine JEM",
"action.export_optifine_full.desc": "Exporteer een volledig OptiFine entity model",
"action.export_obj": "Exporteer OBJ Model",
"action.export_obj.desc": "Exporteer een Wavefront OBJ model om in andere programma's te gebuiken of om te uploaden naar Sketchfab",
"action.export_obj.desc": "Exporteer een Wavefront OBJ model om in andere programma's te gebuiken",
"action.save": "Opslaan",
"action.save.desc": "Het huidige model en texturen opslaan",
"action.settings_window": "Instellingen...",
@ -586,7 +584,6 @@
"panel.outliner": "Outliner",
"panel.options": "Rotatie",
"panel.options.angle": "Hoek",
"panel.options.origin": "Oorsprong",
"uv_editor.title": "UV Editor",
"uv_editor.all_faces": "Alle",
"uv_editor.no_faces": "Geen",
@ -728,15 +725,15 @@
"action.move_down": "Beweeg naar beneden",
"action.move_down.desc": "Beweeg de geselecteerde kubussen naar beneden ten opzichte van de huidige camerahoek",
"action.move_left": "Beweeg Naar Links",
"action.move_left.desc": "Move the selected cubes left relative to the current camera angle",
"action.move_left.desc": "Beweeg de geselecteerde kubussen naar links ten opzichte van de huidige camera hoek",
"action.move_right": "Beweeg Naar Rechts",
"action.move_right.desc": "Move the selected cubes right relative to the current camera angle",
"action.move_right.desc": "Beweeg de geselecteerde kubussen naar rechts ten opzichte van de huidige camera hoek",
"action.move_forth": "Beweeg naar voren",
"action.move_forth.desc": "Move the selected cubes forth relative to the current camera angle",
"action.move_forth.desc": "Beweeg de geselecteerde kubussen naar voren ten opzichte van de huidige camera hoek",
"action.move_back": "Beweeg naar achteren",
"action.move_back.desc": "Move the selected cubes back relative to the current camera angle",
"action.move_back.desc": "Beweeg de geselecteerde kubussen naar achteren ten opzichte van de huidige camera hoek",
"layout.color.wireframe": "Wireframe",
"layout.color.wireframe.desc": "Wireframe view lines",
"layout.color.wireframe.desc": "Wireframe bekijk lijnen",
"action.add_animation": "Voeg Animatie Toe",
"action.add_animation.desc": "Creëer een blanco animatie ",
"action.load_animation_file": "Import Animatie",
@ -760,7 +757,7 @@
"message.rename_animation": "Hernoem Animatie",
"message.animation_update_var": "Animatie Update Variabele",
"message.no_animation_selected": "Je moet een animatie selecteren om dit te doen",
"message.no_bone_selected": "You have to select a bone to do this",
"message.no_bone_selected": "Je moet een bot selecteren om dit te doen",
"message.duplicate_groups.title": "Bone Name Duplicate",
"message.duplicate_groups.message": "The name of this bone exists on multiple bones. This can cause problems.",
"action.select_all_keyframes": "Select All Keyframes",
@ -854,5 +851,53 @@
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Color",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Przesuń punkt obrotu na osi Z",
"action.brush_mode": "Tryb pędzla",
"action.brush_mode.desc": "Tryb pędzla",
"action.brush_color": "Kolor",
"action.brush_color.desc": "Kolor pędzla",
"action.slider_brush_size": "Wielkość",
"action.slider_brush_size.desc": "Promień pędzla w pikselach",
"action.slider_brush_opacity": "Nieprzezroczystość",
@ -586,7 +584,6 @@
"panel.outliner": "Outliner",
"panel.options": "Rotacja",
"panel.options.angle": "Kąt",
"panel.options.origin": "Punkt obrotu",
"uv_editor.title": "Edytor UV",
"uv_editor.all_faces": "Wszystkie",
"uv_editor.no_faces": "Żadne",
@ -853,6 +850,54 @@
"message.outdated_client.message": "Please update to the latest version of Blockbench to do this.",
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.export_asset_archive": "Pobier Archiv",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Color",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

903
lang/pt.json Normal file

@ -0,0 +1,903 @@
{
"dialog.ok": "Ok",
"dialog.cancel": "Cancelar",
"dialog.confirm": "Confirmar",
"dialog.close": "Fechar",
"dialog.import": "Importar",
"dialog.save": "Salvar",
"dialog.discard": "Descartar",
"dialog.dontshowagain": "Não mostrar novamente",
"data.cube": "Cubo",
"data.cubes": "Cubos",
"data.group": "Grupo",
"data.texture": "Textura",
"data.plugin": "Plugin",
"data.preview": "Pré-visualização",
"data.toolbar": "Barra de ferramentas",
"data.image": "Imagem",
"keys.ctrl": "Control",
"keys.shift": "Shift",
"keys.alt": "Alt",
"keys.meta": "Cmd",
"keys.delete": "Delete",
"keys.space": "Espaço",
"keys.leftclick": "Clique esquerdo",
"keys.middleclick": "Clique do meio",
"keys.rightclick": "Clique direito",
"keys.tab": "Tab",
"keys.backspace": "Backspace",
"keys.enter": "Enter",
"keys.escape": "Esc",
"keys.function": "F%0",
"keys.numpad": "Numpad %0",
"keys.caps": "Capslock",
"keys.menu": "Menu de contexto",
"keys.left": "Esquerda",
"keys.up": "Acima",
"keys.right": "Direita",
"keys.down": "Abaixo",
"keys.pageup": "Page Up",
"keys.pagedown": "Page Down",
"keys.plus": "Mais",
"keys.comma": "Vírgula",
"keys.point": "Ponto",
"keys.minus": "Menos",
"keys.cross": "Cruz",
"keys.end": "End",
"keys.pos1": "Pos 1",
"keys.printscreen": "Print Screen",
"keys.pause": "Pausa",
"message.rotation_limit.title": "Limites de rotação",
"message.rotation_limit.message": "As rotações são limitadas pelo Minecraft para um eixo e incrementos de 22.5 graus. Girar em um eixo diferente irá limpar todas as rotações nos outros eixos. Desative a opção \"Rotação Restrita\" se estiver modelando para outros propósitos e precisar de rotações livres.",
"message.file_not_found.title": "Arquivo não encontrado",
"message.file_not_found.message": "Blockbench não pode encontrar o arquivo solicitado. Verifique se ele está salvo localmente e não em uma núvem.",
"message.screenshot.title": "Captura de tela",
"message.screenshot.message": "Captura de tela capturada.",
"message.screenshot.clipboard": "Prancheta",
"message.screenshot.right_click": "Captura de tela - Botão direito do mouse para copiar",
"message.invalid_file.title": "Arquivo inválido",
"message.invalid_file.message": "Não foi possível abrir o arquivo json: %0",
"message.invalid_model.title": "Arquivo de modelo inválido",
"message.invalid_model.message": "Este arquivo não contém dados de modelo válidos.",
"message.child_model_only.title": "Modelo Filho Vazio\n",
"message.child_model_only.message": "Este arquivo é filho de %0 e não contém nenhum modelo",
"message.drag_background.title": "Posicionar fundo.",
"message.drag_background.message": "Arraste o fundo para mover sua posição. Segure a tecla shift e arraste para cima e para baixo para alterar seu tamanho.",
"message.unsaved_textures.title": "Texturas não salvas",
"message.unsaved_textures.message": "Seu modelo tem texturas não salvas. Certifique-se de salvá-las e colá-las em seu pacote de recursos na pasta correta.",
"message.model_clipping.title": "Modelo muito grande",
"message.model_clipping.message": "Seu modelo contém %0 cubos que são maiores do que o limite 3x3x3 permitido pelo Minecraft. Esse modelo não irá funcionar no Minecraft. Habilite a opção 'Limites restrita' para prevenir isso.",
"message.loose_texture.title": "Importar textura",
"message.loose_texture.message": "A textura importada não faz parte de um pacote de recursos. Minecraft apenas consegue carregar texturas dentro da pasta de texturas de um pacote de recursos carregado.",
"message.loose_texture.change": "Mudar caminho",
"message.update_res.title": "Resolução da textura",
"message.update_res.message": "Você gostaria de atualizar a resolução do projeto para a resolução dessa textura? Clique em 'Cancelar' se sua textura tem uma resolução maior que o normal.",
"message.update_res.update": "Atualizar",
"message.bedrock_overwrite_error.message": "Blockbench não pode combinar esse modelo com o arquivo antigo",
"message.bedrock_overwrite_error.backup_overwrite": "Criar backup e sobrescrever",
"message.bedrock_overwrite_error.overwrite": "Sobrescrever",
"message.close_warning.message": "Você deseja salvar o seu modelo?",
"message.close_warning.web": "O seu trabalho atual será perdido. Tem certeza que deseja sair?",
"message.default_textures.title": "Texturas padrão",
"message.default_textures.message": "Selecione a pasta \"textures\" do pacote de recursos padrão",
"message.default_textures.detail": "Extraia o pacote de texturas padrão do jar do Minecraft ou do Google e baixe-o. Em seguida, localize a pasta \"texturas\" e abra-a. O Blockbench lembrará esse local e tentará buscar texturas a partir dele se não puder encontrá-las no pacote de texturas atual.",
"message.default_textures.select": "Selecione a pasta das \"texturas\" padrão",
"message.default_textures.continue": "Continuar",
"message.default_textures.remove": "Remover",
"message.image_editor.title": "Selecione um editor de imagens",
"message.image_editor.file": "Selecionar Arquivo...",
"message.image_editor.exe": "Selecione o executável do editor de imagens",
"message.display_skin.title": "Exibir Skin",
"message.display_skin.message": "Selecione um arquivo de skin do seu computador ou digite um nome de jogador",
"message.display_skin.upload": "Carregar Skin",
"message.display_skin.name": "Nome de usuário",
"message.display_skin.reset": "Resetar",
"message.invalid_plugin": "Plugin inválido, veja o console",
"message.load_plugin_app": "Você quer permitir esse plugin de fazer alterações em seu computador? Apenas carregue plugins de pessoas que você confia.",
"message.load_plugin_web": "Você quer carregar este plugin? Apenas carregue plugins de pessoas que você confia.",
"message.preset_no_info": "A predefinição não contém informações para este espaço",
"message.restart_to_update": "Reinicie o Blockbench para aplicar as mudanças",
"message.save_file": "Salvo como %0",
"message.save_obj": "Salvo como modelo .obj",
"message.save_entity": "Salvo como modelo de entidade do \"bedrock\"",
"message.rename_cubes": "Renomear Cubos",
"dialog.project.title": "Projeto",
"dialog.project.name": "Nome do Arquivo",
"dialog.project.parent": "Modelo Pai",
"dialog.project.geoname": "Nome da Geometria do Mob",
"dialog.project.openparent": "Abrir Pai",
"dialog.project.ao": "Oclusão de ambiente",
"dialog.project.texture_size": "Tamanho da Textura",
"dialog.project.width": "Largura",
"dialog.project.height": "Altura",
"dialog.project.to_blockmodel": "Para Modelo de Bloco",
"dialog.project.to_entitymodel": "Para Modelo de Entidade",
"dialog.texture.title": "Textura",
"dialog.texture.name": "Nome",
"dialog.texture.variable": "Variável",
"dialog.texture.namespace": "Namespace",
"dialog.texture.folder": "Pasta",
"dialog.extrude.title": "Extrudar (expulsar) Imagem",
"dialog.extrude.mode": "Modo de Digitalização",
"dialog.extrude.mode.areas": "Areas",
"dialog.extrude.mode.lines": "Linhas",
"dialog.extrude.mode.columns": "Colunas",
"dialog.extrude.mode.pixels": "Pixels",
"dialog.extrude.opacity": "Opacidade Mínima",
"dialog.extrude.scan": "Digitalizar e Importar",
"dialog.display_preset.title": "Criar Predefinição",
"dialog.display_preset.message": "Selecione os slots que você deseja salvar",
"dialog.display_preset.create": "Criar",
"dialog.select.title": "Selecione",
"dialog.select.new": "Nova Seleção",
"dialog.select.group": "No Grupo Selecionado",
"dialog.select.name": "Nome Contém",
"dialog.select.random": "Aleatório",
"dialog.select.select": "Selecione",
"dialog.scale.title": "Modelo Escala",
"dialog.scale.axis": "Eixo",
"dialog.scale.scale": "Escala",
"dialog.scale.clipping": "Corte de modelo: seu modelo é muito grande para a tela",
"dialog.scale.confirm": "Escala",
"dialog.plugins.title": "Plug-ins",
"dialog.plugins.installed": "Instalado",
"dialog.plugins.available": "Disponível",
"dialog.plugins.install": "Instalar",
"dialog.plugins.uninstall": "Desinstalar",
"dialog.plugins.reload": "Recarregar",
"dialog.plugins.none_installed": "Nenhum plug-in instalado",
"dialog.plugins.none_available": "Nenhum plug-in disponível",
"dialog.plugins.outdated": "Requer uma versão mais nova do Blockbench",
"dialog.plugins.web_only": "Apenas para o aplicativo da web",
"dialog.plugins.app_only": "Apenas para o aplicativo do desktop",
"dialog.plugins.author": "por %0",
"dialog.plugins.show_less": "Mostrar Mais",
"dialog.entitylist.title": "Abrir Modelo de Entidade",
"dialog.entitylist.text": "Selecione o modelo que você deseja importar",
"dialog.entitylist.bones": "Ossos",
"dialog.entitylist.cubes": "Cubos",
"dialog.create_texture.title": "Criar Textura",
"dialog.create_texture.name": "Nome",
"dialog.create_texture.folder": "Pasta",
"dialog.create_texture.template": "Modelo",
"dialog.create_texture.resolution": "Resolução",
"dialog.input.title": "Entrada",
"dialog.update.title": "Atualizações",
"dialog.update.refresh": "Tente Novamente!",
"dialog.update.up_to_date": "Blockbench está atualizado!",
"dialog.update.connecting": "Conectando ao Servidor",
"dialog.settings.settings": "Configurações",
"dialog.settings.keybinds": "Combinações de teclas",
"dialog.settings.layout": "Layout",
"dialog.settings.about": "Sobre",
"layout.color.back": "Voltar",
"layout.color.back.desc": "Planos de fundo e campos de entrada",
"layout.color.dark": "Sombrio",
"layout.color.dark.desc": "Fundo de tela",
"layout.color.ui": "UI",
"layout.color.ui.desc": "Cor da interface principal",
"layout.color.bright_ui": "UI brilhante",
"layout.color.bright_ui.desc": "Menus contextuais e dicas de ferramentas",
"layout.color.button": "Botão",
"layout.color.button.desc": "Botões e interruptores",
"layout.color.selected": "Selecionado",
"layout.color.selected.desc": "Guias e objetos selecionados",
"layout.color.border": "Borda",
"layout.color.border.desc": "Borda de botões e entradas",
"layout.color.accent": "Acento",
"layout.color.accent.desc": "Polegar deslizante e outros detalhes",
"layout.color.grid": "Grade",
"layout.color.grid.desc": "Grade de visualização 3D",
"layout.color.text": "Texto",
"layout.color.text.desc": "Texto Normal",
"layout.color.light": "Luz",
"layout.color.light.desc": "Texto destacado",
"layout.color.accent_text": "Texto Acentuado",
"layout.color.accent_text.desc": "Texto em elementos brilhantes ou acentuados",
"layout.font.main": "Fonte Principal",
"layout.font.headline": "Fonte de título",
"about.version": "Versão:",
"about.creator": "Criador:",
"about.website": "Website:",
"about.bugtracker": "Rastreador de Problemas:",
"about.electron": "Este aplicativo é construído com o Electron, uma estrutura para criar aplicativos nativos com tecnologias da Web, como Javascript, HTML e CSS.",
"about.vertex_snap": "O Vertex Snapping é baseado em um plugin do SirBenet",
"about.icons": "Pacotes de Ícone",
"about.libraries": "Bibliotecas:",
"settings.category.general": "Geral",
"settings.category.preview": "Pré-Visualização",
"settings.category.grid": "Grade",
"settings.category.edit": "Editar",
"settings.category.snapping": "Snapping",
"settings.category.defaults": "Padrões",
"settings.category.dialogs": "Diálogos",
"settings.category.export": "Exportar",
"settings.language": "Idioma",
"settings.language.desc": "Idioma da Interface. Reinicie o Blockbench para aplicar as alterações",
"settings.show_actions": "Ações de Exibição",
"settings.show_actions.desc": "Exibir todas as ações na barra de status",
"settings.backup_interval": "Intervalo de Backup",
"settings.backup_interval.desc": "Intervalo dos backups automáticos em minutos",
"settings.origin_size": "Origem da Rotação",
"settings.origin_size.desc": "Tamanho da origem da rotação",
"settings.control_size": "Tamanho do controle do eixo",
"settings.control_size.desc": "Tamanho da ferramenta de controle de 3 eixos",
"settings.display_skin": "Exibir Skin",
"settings.display_skin.desc": "Skin usada para o modelo de player de referência de exibição",
"settings.shading": "Sombreamento",
"settings.shading.desc": "Ativar sombreamento",
"settings.transparency": "Transparência",
"settings.transparency.desc": "Renderizar Texturas Transparentes",
"settings.texture_fps": "Textura animada FPS",
"settings.texture_fps.desc": "Quadros por segundo para texturas animadas",
"settings.base_grid": "Grade Pequena",
"settings.base_grid.desc": "Mostrar pequena grade e eixos",
"settings.large_grid": "Grade Grande",
"settings.large_grid.desc": "Mostrar grade de blocos 3x3",
"settings.full_grid": "Grade Grande Completa",
"settings.full_grid.desc": "Mostrar grade precisa de 3x3",
"settings.large_box": "Caixa grande",
"settings.large_box.desc": "Mostrar limites de blocos 3x3",
"settings.display_grid": "Modo de exibição",
"settings.display_grid.desc": "Mostrar grade no modo de exibição",
"settings.undo_limit": "Limite de desfazer",
"settings.undo_limit.desc": "Número de etapas que você pode desfazer",
"settings.restricted_canvas": "Tela restrita",
"settings.restricted_canvas.desc": "Restringir a Tela a uma área de bloco de 3x3 para evitar modelos inválidos",
"settings.limited_rotation": "Rotação restrita",
"settings.limited_rotation.desc": "Restringir rotação aos valores válidos para modelos Minecraft",
"settings.local_move": "Mova-se nos Eixos Relativos",
"settings.local_move.desc": "Mover elementos rotacionados em seus próprios eixos, se possível",
"settings.canvas_unselect": "Clique em Tela Deselecionar",
"settings.canvas_unselect.desc": "Desmarca todos os elementos ao clicar no fundo da tela",
"settings.paint_side_restrict": "Restringir o pincel ao lado",
"settings.paint_side_restrict.desc": "Restringir os pincéis para pintar apenas no lado atual",
"settings.autouv": "Auto UV",
"settings.autouv.desc": "Ativar AutoUV por padrão",
"settings.create_rename": "Renomear Novo Cubo",
"settings.create_rename.desc": "Campo de nome de foco ao criar um novo elemento ou grupo",
"settings.edit_size": "Resolução de Grade",
"settings.edit_size.desc": "Resolução da grade em que os cubos se encaixam",
"settings.shift_size": "Resolução Shift",
"settings.shift_size.desc": "Resolução da grade enquanto mantém Shift pressionado",
"settings.ctrl_size": "Resolução de controle",
"settings.ctrl_size.desc": "Resolução da grade, mantendo o controle",
"settings.negative_size": "Tamanho negativo",
"settings.negative_size.desc": "Permitir que a ferramenta de redimensionamento use tamanhos negativos",
"settings.dialog_unsaved_textures": "Texturas não salvas",
"settings.dialog_unsaved_textures.desc": "Mostrar a caixa de diálogo \"Texturas não salvas\"",
"settings.dialog_larger_cubes": "Modelo muito grande",
"settings.dialog_larger_cubes.desc": "Mostrar a caixa de diálogo \"Modelo muito grande\"",
"settings.dialog_rotation_limit": "Limites de Rotação",
"settings.dialog_rotation_limit.desc": "Mostrar caixa de diálogo \"Limites de rotação\"",
"settings.minifiedout": "Exportação Minificada",
"settings.minifiedout.desc": "Escreva o arquivo JSON em uma linha",
"settings.export_groups": "Grupos de Exportação",
"settings.export_groups.desc": "Salvar grupos em arquivos blockmodel",
"settings.obj_textures": "Texturas de Exportação",
"settings.obj_textures.desc": "Exportar texturas ao exportar arquivo OBJ",
"settings.credit": "Comentário de crédito",
"settings.credit.desc": "Adicionar um comentário de crédito aos arquivos exportados",
"settings.default_path": "Caminho Padrão",
"settings.default_path.desc": "Pasta de onde o Blockbench carrega texturas padrão",
"settings.image_editor": "Editor de Imagem",
"settings.image_editor.desc": "Editor de imagens padrão para editar texturas com",
"category.navigate": "Navegação",
"category.tools": "Ferramentas",
"category.file": "Arquivo",
"category.blockbench": "Blockbench",
"category.edit": "Editar",
"category.transform": "Transformar",
"category.filter": "Filtro",
"category.view": "Visão",
"category.display": "Configurações Padrão",
"category.textures": "Texturas",
"category.misc": "Diversos",
"keybind.preview_select": "Selecione",
"keybind.preview_rotate": "Visão de Rotação",
"keybind.preview_drag": "Visão de Arrastar",
"keybind.confirm": "Confirme",
"keybind.cancel": "Cancelar",
"action.slider_pos_x": "Mover X",
"action.slider_pos_x.desc": "Move cubos no eixo X",
"action.slider_pos_y": "Mover Y",
"action.slider_pos_y.desc": "Move cubos no eixo Y",
"action.slider_pos_z": "Mover Z",
"action.slider_pos_z.desc": "Move cubos no eixo Z",
"action.slider_size_x": "Tamanho X",
"action.slider_size_x.desc": "Redimensionar cubos no eixo X",
"action.slider_size_y": "Tamanho Y",
"action.slider_size_y.desc": "Redimensionar cubos no eixo Y",
"action.slider_size_z": "Tamanho Z",
"action.slider_size_z.desc": "Redimensionar cubos no eixo Z",
"action.slider_inflate": "Aumentar",
"action.slider_inflate.desc": "Aumentar cubos em todas as direções sem alterar o UV.",
"action.slider_rotation_x": "Rodar X",
"action.slider_rotation_x.desc": "Rotaciona cubos no eixo X",
"action.slider_rotation_y": "Rodar Y",
"action.slider_rotation_y.desc": "Rotaciona cubos no eixo X",
"action.slider_rotation_z": "Rodar Z",
"action.slider_rotation_z.desc": "Rotaciona cubos no eixo X",
"action.slider_origin_x": "Origem X",
"action.slider_origin_x.desc": "Move a origem no eixo X",
"action.slider_origin_y": "Origem Y",
"action.slider_origin_y.desc": "Move a origem no eixo X",
"action.slider_origin_z": "Origem Z",
"action.slider_origin_z.desc": "Move a origem no eixo X",
"action.brush_mode": "Modo de Pincel",
"action.brush_mode.desc": "Modo do Pincel",
"action.slider_brush_size": "Tamanho",
"action.slider_brush_size.desc": "Raio do pincel em pixels",
"action.slider_brush_opacity": "Opacidade",
"action.slider_brush_opacity.desc": "Opacidade do pincel em porcentagem",
"action.slider_brush_softness": "Suavidade",
"action.slider_brush_softness.desc": "Suavidade do pincel em porcentagem",
"action.background_color": "Cor de fundo",
"action.background_color.desc": "Cor de fundo da textura criada",
"action.uv_slider_pos_x": "Move Horizontal",
"action.uv_slider_pos_x.desc": "Mova a seleção UV de todos os cubos selecionados horizontalmente",
"action.uv_slider_pos_y": "Move Vertical",
"action.uv_slider_pos_y.desc": "Mova a seleção UV de todos os cubos selecionados verticalmente",
"action.uv_slider_size_x": "Escala Horizontal",
"action.uv_slider_size_x.desc": "Escale a seleção UV de todos os cubos selecionados horizontalmente",
"action.uv_slider_size_y": "Escala Vertical",
"action.uv_slider_size_y.desc": "Escale a seleção UV de todos os cubos selecionados verticalmente",
"action.vertex_snap_mode": "Modo Snap",
"action.vertex_snap_mode.desc": "Selecione se o Vertex Snap mover elementos para a posição selecionada ou redimensioná-los",
"action.move_tool": "Mover",
"action.move_tool.desc": "Ferramenta para selecionar e mover elementos",
"action.resize_tool": "Redimensionar",
"action.resize_tool.desc": "Ferramenta para selecionar e redimensionar elementos",
"action.brush_tool": "Pincel",
"action.brush_tool.desc": "Ferramenta para pintar texturas de bitmap em superfícies ou o editor de UV",
"action.vertex_snap_tool": "Vertex Snap",
"action.vertex_snap_tool.desc": "Mova um cubo para outro cubo conectando dois vértices (origens)",
"action.swap_tools": "Ferramentas de Troca",
"action.swap_tools.desc": "Alternar entre a ferramenta de movimentação e redimensionamento",
"action.project_window": "Projeto...",
"action.project_window.desc": "Abre a janela do projeto, onde você pode editar os metadados do seu modelo",
"action.new_block_model": "Novo Modelo",
"action.new_block_model.desc": "Cria um novo modelo de bloco / item",
"action.new_entity_model": "Novo Modelo de Entidade",
"action.new_entity_model.desc": "Cria um novo modelo de entidade de base",
"action.open_model": "Abrir Modelo",
"action.open_model.desc": "Abre o arquivo do modelo do seu computador.",
"action.add_model": "Adicionar modelo",
"action.add_model.desc": "Adicionar um modelo de um arquivo ao modelo atual",
"action.extrude_texture": "Textura extrudada",
"action.extrude_texture.desc": "Gere um modelo esticando uma textura",
"action.export_blockmodel": "Exportar Blockmodel",
"action.export_blockmodel.desc": "Exportar um bloco ou modelo de item do Minecraft",
"action.export_entity": "Exportar a Entidade Bedrock",
"action.export_entity.desc": "dicione o modelo atual como uma entidade a um arquivo mobs.json",
"action.export_optifine_part": "Exportar OptiFine JPM",
"action.export_optifine_part.desc": "Exportar um modelo de parte da entidade para o OptiFine",
"action.export_optifine_full": "Exportar OptiFine JEM",
"action.export_optifine_full.desc": "Exportar um modelo de entidade completo do OptiFine",
"action.export_obj": "Exportar Modelo OBJ",
"action.export_obj.desc": "Exportar um modelo Wavefront OBJ para uso em outros programas ou fazer upload para o Sketchfab",
"action.save": "Salve",
"action.save.desc": "Salve o modelo e as texturas atuais",
"action.settings_window": "Configurações",
"action.settings_window.desc": "Abre o diálogo de configurações do Blockbench.",
"action.plugins_window": "Plugins...",
"action.plugins_window.desc": "Abre a janela da loja de plugins",
"action.update_window": "Atualizações...",
"action.update_window.desc": "Busca por atualizações do Blockbench",
"action.donate": "Doar",
"action.donate.desc": "Doar para o Blockbench",
"action.reset_keybindings": "Redefinir atalhos de teclado",
"action.reset_keybindings.desc": "Redefinir todas as combinações de teclas com os padrões do Blockbench",
"action.import_layout": "Importar Layout",
"action.import_layout.desc": "Importa um arquivo de layout",
"action.export_layout": "Exportar Layout",
"action.export_layout.desc": "Cria um arquivo de layout baseado nas configurações atuais",
"action.reset_layout": "Redefinir Layout",
"action.reset_layout.desc": "Redefine para layout padrão do Blockbench",
"action.load_plugin": "Carregar Plugin de um Arquivo",
"action.load_plugin.desc": "Carrega um plugin importando o arquivo de origem.",
"action.reload_plugins": "Recarregar Plugins",
"action.reload_plugins.desc": "Recarrega todos os plugins de desenvolvimento.",
"action.uv_dialog": "Janela UV",
"action.uv_dialog.desc": "Abre o diálogo UV para ver todas as faces próximas umas das outras",
"action.uv_dialog_full": "Vista Completa",
"action.uv_dialog_full.desc": "Abra o diálogo UV para editar uma face em tela cheia",
"action.undo": "Desfazer",
"action.undo.desc": "Anula a última alteração",
"action.redo": "Refazer",
"action.redo.desc": "Reverte o último desfazer",
"action.copy": "Copiar",
"action.copy.desc": "Copie a seleção selecionada, face ou configurações de exibição",
"action.paste": "Colar",
"action.paste.desc": "Cole a seleção selecionada, face ou configurações de exibição",
"action.cut": "Cortar",
"action.cut.desc": "Corta a seleção selecionada, face ou configurações de exibição",
"action.add_cube": "Adicionar Cubo",
"action.add_cube.desc": "Adiciona um novo cubo",
"action.add_group": "Adicionar Grupo",
"action.add_group.desc": "Adiciona um novo grupo ou osso",
"action.outliner_toggle": "Alternar Mais Opções",
"action.outliner_toggle.desc": "Alterna opções para mais opções no delineador",
"action.duplicate": "Duplicar",
"action.duplicate.desc": "Duplica os cubos ou grupos selecionados",
"action.delete": "Deletar",
"action.delete.desc": "Exclui os cubos ou grupos selecionados",
"action.sort_outliner": "Ordenar Outliner",
"action.sort_outliner.desc": "Ordenar o delineador em ordem alfabética",
"action.local_move": "Mover Relativo",
"action.local_move.desc": "Mover elementos rotacionados em seus próprios eixos, se possível",
"action.select_window": "Selecionar...",
"action.select_window.desc": "Pesquisa e seleciona cubos com base em suas propriedades",
"action.invert_selection": "Seleção invertida",
"action.invert_selection.desc": "Inverte a seleção atual de cubos",
"action.select_all": "Selecionar todos",
"action.select_all.desc": "Seleciona todos os cubos",
"action.collapse_groups": "Recolher grupos",
"action.collapse_groups.desc": "Recolhe todos os grupos",
"action.scale": "Escala...",
"action.scale.desc": "Escala os cubos selecionados",
"action.rotate_x_cw": "Rodar CW",
"action.rotate_x_cw.desc": "Rodar os cubos selecionados a 90 ° no eixo X",
"action.rotate_x_ccw": "Rodar Counter-CW",
"action.rotate_x_ccw.desc": "Gira os cubos selecionados -90 ° no eixo X",
"action.rotate_y_cw": "Rodar CW",
"action.rotate_y_cw.desc": "Rodar os cubos selecionados a 90 ° no eixo Y",
"action.rotate_y_ccw": "Rodar Counter-CW",
"action.rotate_y_ccw.desc": "Gira os cubos selecionados -90 ° no eixo Y",
"action.rotate_z_cw": "Rodar CW",
"action.rotate_z_cw.desc": "Roda os cubos selecionados a 90 ° no eixo Z",
"action.rotate_z_ccw": "Rodar Counter-CW",
"action.rotate_z_ccw.desc": "Roda os cubos selecionados -90 ° no eixo Z",
"action.flip_x": "Girar X",
"action.flip_x.desc": "Gira os cubos selecionados no eixo X",
"action.flip_y": "Girar Y",
"action.flip_y.desc": "Gira os cubos selecionados no eixo Y",
"action.flip_z": "Girar Z",
"action.flip_z.desc": "Gira os cubos selecionados no eixo Z",
"action.center_x": "Centralizar X",
"action.center_x.desc": "Centraliza os cubos selecionados no eixo X",
"action.center_y": "Centralizar Y",
"action.center_y.desc": "Centraliza os cubos selecionados no eixo Y",
"action.center_z": "Centralizar Z",
"action.center_z.desc": "Centraliza os cubos selecionados no eixo Z",
"action.center_all": "Centralizar Todos",
"action.center_all.desc": "Centraliza os cubos selecionados",
"action.toggle_visibility": "Alternar visibilidade",
"action.toggle_visibility.desc": "Alterna a visibilidade dos cubos selecionados.",
"action.toggle_export": "Alternar exportação",
"action.toggle_export.desc": "Alterna as configurações de exportação dos cubos selecionados.",
"action.toggle_autouv": "Alternar Auto UV",
"action.toggle_autouv.desc": "Alternas as configurações de auto UV dos cubos selecionados.",
"action.toggle_shade": "Alternar sombreamento",
"action.toggle_shade.desc": "Alterna o sombreamento dos cubos selecionados.",
"action.rename": "Renomear",
"action.rename.desc": "Muda o nome dos cubos selecionados.",
"action.add_display_preset": "Nova predefinição",
"action.add_display_preset.desc": "Adicionar uma nova predefinição de configuração de exibição.",
"action.fullscreen": "Tela Cheia",
"action.fullscreen.desc": "Alterna para o modo de Tela Cheia.",
"action.zoom_in": "Mais Zoom",
"action.zoom_in.desc": "Aumentar o zoom para ampliar a interface.",
"action.zoom_out": "Reduzir o Zoom",
"action.zoom_out.desc": "Reduzir o Zoom para reduzir a interface.",
"action.zoom_reset": "Resetar Zoom",
"action.zoom_reset.desc": "Reseta o Zoom para padrão 100%.",
"action.reset_interface": "Resetar Interface",
"action.reset_interface.desc": "Reseta o tamanho e as posições da Interface (GUI)",
"action.toggle_wireframe": "Alterar Wireframe",
"action.toggle_wireframe.desc": "Altera o wireframe para o modo padrão.",
"action.screenshot_model": "Capturar Modelo",
"action.screenshot_model.desc": "Tira uma captura de tela recortada do modelo do ângulo atual",
"action.screenshot_app": "Capturar Aplicativo",
"action.screenshot_app.desc": "Tira uma captura de tela de toda a aplicação",
"action.toggle_quad_view": "Alternar vista quad",
"action.toggle_quad_view.desc": "Alternar o modo de 4 pontos de vista",
"action.import_texture": "Importar Textura",
"action.import_texture.desc": "Importa uma ou mais texturas do seu arquivo do sistema",
"action.create_texture": "Criar Textura",
"action.create_texture.desc": "Cria uma textura ou modelo de textura em branco",
"action.reload_textures": "Recarregar Texturas",
"action.reload_textures.desc": "Recarrega todas as texturas",
"action.save_textures": "Salvar Texturas",
"action.save_textures.desc": "Salva as texturas não salvas",
"action.animated_textures": "Iniciar Texturas Animadas",
"action.animated_textures.desc": "Inicia e Pausa a pré visualização das texturas animadas",
"action.origin_to_geometry": "Origem para Geometria",
"action.origin_to_geometry.desc": "Definir a origem para o centro da geometria",
"action.rescale_toggle": "Escala de alternância",
"action.rescale_toggle.desc": "Rescalar cubos com base em sua rotação atual",
"action.bone_reset_toggle": "Resetar Osso",
"action.bone_reset_toggle.desc": "Impedir que o osso exiba cubos do modelo pai",
"action.reload": "Recarregar o Blockbench",
"action.reload.desc": "Recarrega o Blockbench. Isso removerá todos os progressos não salvos",
"menu.file": "Arquivo",
"menu.edit": "Editar",
"menu.transform": "Transformar",
"menu.filter": "Filtro",
"menu.display": "Padrão",
"menu.view": "Visão",
"menu.file.new": "Novo",
"menu.file.recent": "Recente",
"menu.file.import": "Importar",
"menu.file.export": "Exportar",
"menu.transform.rotate": "Rodar",
"menu.transform.flip": "Girar",
"menu.transform.center": "Centralizar",
"menu.transform.properties": "Propiedades",
"menu.display.preset": "Aplicar predefinição",
"menu.display.preset_all": "Aplica Predefinição em toda a parte",
"menu.display.remove_preset": "Remover Predefinição",
"menu.view.zoom": "Zoom",
"menu.view.background": "Fundo",
"menu.view.screenshot": "Captura de Tela",
"menu.cube.duplicate": "Duplicar",
"menu.cube.color": "Criador de Cor",
"menu.cube.texture": "Textura",
"menu.cube.texture.transparent": "Transparente",
"menu.cube.texture.blank": "Em branco",
"menu.group.duplicate": "Duplicar",
"menu.group.sort": "Ordenar",
"menu.group.resolve": "Resolver",
"menu.texture.face": "Aplicar a Face",
"menu.texture.cube": "Aplicar aos Cubes",
"menu.texture.file": "Arquivo",
"menu.texture.refresh": "Atualizar",
"menu.texture.change": "Mudar Arquivo",
"menu.texture.folder": "Abrir na Pasta",
"menu.texture.edit": "Editar",
"menu.texture.export": "Salvar como",
"menu.texture.save": "Salvar",
"menu.texture.properties": "Propriedades...",
"menu.preview.background": "Fundo",
"menu.preview.background.load": "Carregar",
"menu.preview.background.position": "Posição",
"menu.preview.background.lock": "Travar a câmera",
"menu.preview.background.remove": "Remover",
"menu.preview.screenshot": "Captura de Tela",
"menu.preview.perspective": "Perspectiva",
"menu.preview.perspective.normal": "Normal",
"menu.preview.quadview": "Visão Quad",
"menu.preview.fullview": "Visão Completa",
"menu.preview.stop_drag": "Pare o posicionamento em segundo plano",
"menu.uv.mapping": "Mapeamento UV",
"menu.uv.mapping.export": "Exportar",
"menu.uv.mapping.rotation": "Rotação",
"menu.uv.mapping.mirror_x": "Espelhar X",
"menu.uv.mapping.mirror_y": "Espelhar Y",
"menu.uv.tint": "Tom",
"menu.uv.texture": "Textura",
"cube.color.light_blue": "Azul claro",
"cube.color.yellow": "Amarelo",
"cube.color.orange": "Laranja",
"cube.color.red": "Vermelho",
"cube.color.purple": "Roxo",
"cube.color.blue": "Azul",
"cube.color.green": "Verde",
"cube.color.lime": "Lime",
"switches.visibility": "Visibilidade",
"switches.export": "Exportar",
"switches.shading": "Sombra",
"switches.autouv": "Auto UV",
"panel.uv": "UV",
"panel.display": "Padrão",
"panel.textures": "Texturas",
"panel.outliner": "Delineador",
"panel.options": "Rotação",
"panel.options.angle": "Ângulo",
"uv_editor.title": "Editor de UV",
"uv_editor.all_faces": "Todas",
"uv_editor.no_faces": "Nenhum",
"face.north": "Norte",
"face.south": "Sul",
"face.west": "Oeste",
"face.east": "Leste",
"face.up": "Cima",
"face.down": "Baixo",
"direction.north": "Norte",
"direction.south": "Sul",
"direction.west": "Oeste",
"direction.east": "Leste",
"direction.top": "Topo",
"direction.bottom": "Baixo",
"display.slot.third_right": "Direito da terceira pessoa",
"display.slot.third_left": "Esquerdo da terceira pessoa",
"display.slot.first_right": "Direito da primeira pessoa",
"display.slot.first_left": "Esquerdo da primeira pessoa",
"display.slot.head": "Cabeça",
"display.slot.ground": "Ground",
"display.slot.frame": "Quadro",
"display.slot.gui": "GUI (Interface)",
"display.rotation": "Rotação",
"display.translation": "Translação",
"display.scale": "Escala",
"display.slot": "Slot",
"display.reference": "Modelo Referência",
"display.presetname": "Nome",
"display.reference.player": "Player",
"display.reference.zombie": "Zumbi",
"display.reference.armor_stand": "Suporte de armadura",
"display.reference.baby_zombie": "Zumbi Bebê",
"display.reference.armor_stand_small": "Suporte de Armadura pequeno",
"display.reference.monitor": "Normal",
"display.reference.bow": "Arco",
"display.reference.block": "Bloco",
"display.reference.frame": "Moldura",
"display.reference.inventory_nine": "3x3",
"display.reference.inventory_full": "Inventário",
"display.reference.hud": "HUD",
"display.preset.blank_name": "Por favor insira um nome",
"display.preset.item": "Item Padrão",
"display.preset.block": "Bloco Padrão",
"display.preset.handheld": "Arma Padrão",
"display.preset.rod": "Vara Padrão",
"dialog.continue": "Continuar",
"message.square_textures": "Texturas têm que ser quadradas",
"message.unsaved_texture.title": "Textura não salva",
"message.unsaved_texture.message": "Todas as alterações não salvas serão perdidas.Você quer continuar?",
"dialog.update.no_connection": "Sem conexão com a Internet",
"dialog.update.latest": "Última versão",
"dialog.update.installed": "Versão Instalada",
"dialog.update.update": "Atualização",
"action.brush_mode.brush": "Modo Volta",
"action.brush_mode.noise": "Modo Nariz",
"action.vertex_snap_mode.move": "Mover",
"action.vertex_snap_mode.scale": "Escala",
"action.open_model_folder": "Abrir pasta de Modelo",
"action.open_model_folder.desc": "Abre a pasta onde o modelo está contido",
"action.change_textures_folder": "Mudar local da textura",
"action.change_textures_folder.desc": "Alterar a pasta em que todas as texturas são salvas",
"menu.texture.particle": "Usar para partículas",
"message.update_notification.title": "Uma atualização está disponível",
"message.update_notification.message": "A versão \"%0\" do Blockbench está disponível.\nVocê deseja instalar agora?",
"message.update_notification.install": "Instalar",
"message.update_notification.later": "Mais tarde",
"message.untextured": "A superfície não tem textura",
"dialog.toolbar_edit.title": "Customizar Barra de Ferramentas",
"dialog.shift_uv.title": "Shift UV",
"dialog.shift_uv.message": "Digite o número que você deseja multiplicar as coordenadas de deslocamento UV por. Expressões matemáticas são permitidas. Prefira um \"+\" se você quiser adicionar esse número.",
"dialog.shift_uv.horizontal": "Horizontal",
"dialog.shift_uv.vertical": "Vertical",
"keybindings.reset": "Resetar",
"keybindings.clear": "Vazio",
"action.cube_counter": "Contador de cubo",
"action.uv_rotation": "Rotação UV",
"action.uv_rotation.desc": "Rotação da face UV",
"action.uv_grid": "Grade UV",
"action.uv_grid.desc": "A resolução da grade na qual o seletor UV se encaixa",
"action.uv_grid.auto": "Auto",
"action.uv_grid.none": "Nenhum",
"action.uv_maximize": "Maximizar UV",
"action.uv_maximize.desc": "Define o UV para este face para a textura total",
"action.uv_auto": "Auto UV",
"action.uv_auto.desc": "Define o tamanho de UV dessa face para o tamanho real da face",
"action.uv_rel_auto": "Rel. Auto UV",
"action.uv_rel_auto.desc": "Define o UV dessa face para a posição e tamanho da face real",
"action.uv_mirror_x": "Espelhar UV X",
"action.uv_mirror_x.desc": "Espelha o UV da face no eixo X",
"action.uv_mirror_y": "Espelhar UV Y",
"action.uv_mirror_y.desc": "Espelha o UV da face no eixo Y",
"action.uv_transparent": "Face Transparente",
"action.uv_transparent.desc": "Faz a face atual, transparente",
"action.uv_reset": "Resetar Face",
"action.uv_reset.desc": "Reseta a Face atual",
"action.cullface": "Face Cobrida",
"action.cullface.desc": "Desativa a renderização para essa face se o lado selecionado do modelo for coberto",
"action.auto_cullface": "Auto Face Cobrida",
"action.auto_cullface.desc": "Define o rosto para este rosto para si",
"action.face_tint": "Tom",
"action.face_tint.desc": "Ativa a opção de tom para a face atual",
"action.uv_shift": "Shift UV",
"action.uv_shift.desc": "Desloca todas as regiões UV por uma quantidade fixa ou expressão matemática",
"menu.toolbar.edit": "Customizar",
"menu.toolbar.reset": "Resetar",
"uv_editor.rotated": "Rotacionado",
"uv_editor.auto_cull": "Face Cobrida para si mesmo",
"uv_editor.copied": "Face Copiada",
"uv_editor.pasted": "Face Colada",
"uv_editor.copied_x": "Copiada %0 Faces ",
"uv_editor.reset": "Resetar Face",
"uv_editor.maximized": "Maximizado",
"uv_editor.autouv": "Tamanho Automático",
"uv_editor.mirrored": "Espelhado",
"uv_editor.to_all": "Aplicado para todas as Faces",
"uv_editor.transparent": "Feito Transparente",
"uv_editor.cullface_on": "Face Cobrida Ligada",
"uv_editor.cullface_off": "Face Cobrida Desligada",
"uv_editor.tint_on": "Tom Ligado",
"uv_editor.tint_off": "Tom Desligado",
"action.uv_apply_all": "Aplicar para todas as Faces",
"action.uv_apply_all.desc": "Aplica as configurações na face atual e para todas as faces",
"message.convert_mode.title": "Converter Modelo",
"message.convert_mode.message": "Você tem certeza que deseja converter esse modelo para %0? Você não poderá desfazer isso.",
"message.convert_mode.block": "um modelo de entidade",
"message.convert_mode.entity": "um modelo de bloco",
"message.convert_mode.convert": "Converter",
"message.image_editor_missing.title": "Editor de Imagem Padrão",
"message.image_editor_missing.message": "Selecione um arquivo executável do seu Editor de Imagem",
"message.image_editor_missing.detail": "O Blockbench não conseguiu encontrar um editor de imagens no seu computador. Selecione o arquivo executável do seu editor de imagens preferido.",
"action.update_autouv": "Atualizar Auto UV",
"action.update_autouv.desc": "Atualiza o auto UV mapeando os cubos selecionados",
"category.uv": "UV",
"status_bar.saved": "O Modelo foi salvo",
"status_bar.unsaved": "Existem alterações não salvas!",
"action.move_up": "Move para cima",
"action.move_up.desc": "Mova os cubos selecionados para cima em relação ao ângulo atual da câmera",
"action.move_down": "Mover para baixo",
"action.move_down.desc": "Mova os cubos selecionados para baixo em relação ao ângulo atual da câmera",
"action.move_left": "Mover para esquerda",
"action.move_left.desc": "Mova os cubos selecionados para a esquerda em relação ao ângulo atual da câmera",
"action.move_right": "Mover para direita",
"action.move_right.desc": "Mova os cubos selecionados para a direita em relação ao ângulo atual da câmera",
"action.move_forth": "Ir para Frente",
"action.move_forth.desc": "Mova os cubos selecionados para a frente em relação ao ângulo atual da câmera",
"action.move_back": "Ir para Trás",
"action.move_back.desc": "Mova os cubos selecionados para a trás em relação ao ângulo atual da câmera",
"layout.color.wireframe": "Wireframe",
"layout.color.wireframe.desc": "Linhas de visualização de wireframe",
"action.add_animation": "Adicionar Animação",
"action.add_animation.desc": "Criar uma animação em branco",
"action.load_animation_file": "Importar Animações",
"action.load_animation_file.desc": "Importar um arquivo de animação",
"action.play_animation": "Iniciar Animação",
"action.play_animation.desc": "Pré Visualização de animação",
"action.export_animation_file": "Exportar Animação",
"action.export_animation_file.desc": "Exporta um arquivo json com as animações atuais",
"action.slider_keyframe_time": "Tempo de Código",
"action.slider_keyframe_time.desc": "Alterar o timecode dos quadros-chave selecionados",
"timeline.rotation": "Rotação",
"timeline.position": "Posição",
"timeline.scale": "Escala",
"menu.timeline.add": "Adicionar quadro-chave",
"menu.keyframe.quaternion": "Quartenion",
"panel.animations": "Anim",
"panel.keyframe": "Quadro - Chave",
"panel.keyframe.type": "Quadro - Chave (%0)",
"generic.delete": "Deletar",
"generic.rename": "Renomear",
"message.rename_animation": "Renomear Animação",
"message.animation_update_var": "Atualização de Variável de Animação",
"message.no_animation_selected": "Você tem que selecionar uma animação para fazer isso",
"message.no_bone_selected": "Você tem que selecionar um osso para fazer isso",
"message.duplicate_groups.title": "Nome do Osso Duplicado",
"message.duplicate_groups.message": "O nome deste osso existe em vários ossos. Isso pode causar problemas.",
"action.select_all_keyframes": "Selecionar todos os Quadros-Chave",
"action.select_all_keyframes.desc": "Seleciona todos os Quadros-Chave do osso atual",
"action.delete_keyframes": "Deletar Quadros-Chave",
"action.delete_keyframes.desc": "Deleta todos os Quadros-Chave selecionados",
"menu.animation": "Animação",
"menu.animation.loop": "Loop",
"menu.animation.override": "Sobrepor",
"menu.animation.anim_time_update": "Atualizar Variável",
"message.display_skin_model.title": "Modelo da Skin",
"message.display_skin_model.message": "Seleciona o tipo de modelo da sua skin",
"message.display_skin_model.classic": "Clássico",
"message.display_skin_model.slim": "Magro/Fino",
"message.bone_material": "Mudar o material do osso",
"action.slider_animation_length": "Duração da animação",
"action.slider_animation_length.desc": "Muda a duração da animação selecionada",
"menu.group.material": "Definir Material",
"action.camera_reset": "Resetar Câmera",
"action.camera_reset.desc": "Redefinir a visualização atual para o ângulo de câmera padrão",
"panel.variable_placeholders": "Espaços reservados variáveis",
"panel.variable_placeholders.info": "Listar as variáveis que você deseja visualizar via nome = valor",
"status_bar.vertex_distance": "Distância: %0",
"dialog.create_gif.title": "Gravar GIF",
"dialog.create_gif.length": "Duração (Segundos)",
"dialog.create_gif.fps": "FPS",
"dialog.create_gif.compression": "Quantidade de Compressão",
"dialog.create_gif.play": "Iniciar Animação",
"category.animation": "Animação",
"action.record_model_gif": "Gravar GIF",
"action.record_model_gif.desc": "Grava um GIF animado do modelo do ângulo atual",
"display.mirror": "Espelhar",
"data.separator": "Separador",
"message.set_background_position.title": "Posição de Fundo",
"menu.preview.background.set_position": "Definir Posição",
"dialog.toolbar_edit.hidden": "Esconder",
"action.export_class_entity": "Exportar Entidade Java",
"action.export_class_entity.desc": "Exporta o modelo da entidade como Java class",
"settings.seethrough_outline": "Esboços de raio X",
"settings.seethrough_outline.desc": "Mostrar contornos através de objetos",
"mode.edit": "Editar",
"mode.paint": "Pintar",
"mode.display": "Padrão",
"mode.animate": "Animar ",
"status_bar.recording_gif": "Gravando GIF",
"status_bar.processing_gif": "Processando GIF ",
"settings.backup_retain": "Backup reter a duração",
"settings.backup_retain.desc": "Defina por quanto tempo o Blockbench mantém backups antigos em dias",
"action.rotate_tool": "Rodar",
"action.rotate_tool.desc": "Ferramenta para selecionar e rodar elementos",
"action.fill_tool": "Lata de Tinda",
"action.fill_tool.desc": "Lata de Tinta para preencher faces inteiras com uma cor",
"action.eraser": "Borracha",
"action.eraser.desc": "Ferramenta Borracha para substituir cores em uma textura com transparência",
"action.color_picker": "Seletor de cores",
"action.color_picker.desc": "Ferramenta para escolher a cor dos pixels na sua textura",
"action.open_backup_folder": "Abrir pasta de Backup",
"action.open_backup_folder.desc": "Abre a pasta de Backup do Blockbench",
"switches.mirror": "Espelhar UV",
"language_name": "Inglês",
"message.plugin_reload": "Recarregado %0 plug-ins locais",
"settings.brightness": "Brilho",
"settings.brightness.desc": "Brilho da pré visualização. Padrão é 50",
"menu.preview.perspective.reset": "Resetar Câmera",
"action.fill_mode": "Modo de preenchimento",
"action.fill_mode.desc": "Modo da ferramenta de preenchimento",
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Cor",
"action.fill_mode.cube": "Cubo",
"action.toggle_mirror_uv": "Espelhar UV",
"action.toggle_mirror_uv.desc": "Alterne o espelhamento UV no eixo X dos cubos selecionados.",
"action.toggle_uv_overlay": "Alternar sobreposição de UV",
"action.toggle_uv_overlay.desc": "Quando ativado, exibe todas as sobreposições de mapeamento UV acima da textura.",
"menu.texture.blank": "Aplicar a faces não texturizadas",
"dialog.scale.select_overflow": "Selecione Overflow",
"dialog.create_texture.compress": "Comprimir Modelo",
"action.action_control": "Controle de ação",
"action.action_control.desc": "Pesquise e execute qualquer ação disponível",
"keybindings.recording": "Gravando de Teclas de Gravação",
"keybindings.press": "Pressione uma tecla ou combinação de teclas ou clique em qualquer lugar da tela para gravar sua combinação de teclas.",
"action.pivot_tool": "Ferramenta Pivô",
"action.pivot_tool.desc": "Ferramenta para alterar o ponto de articulação de cubos e ossos",
"action.slider_animation_speed": "Velocidade de reprodução",
"action.slider_animation_speed.desc": "Velocidade de reprodução da linha do tempo em porcentagem",
"action.previous_keyframe": "Quadro-Anterior anterior",
"action.previous_keyframe.desc": "Pula para o Quadro-Chave Anterior",
"action.next_keyframe": "Próximo Quadro-Chave",
"action.next_keyframe.desc": "Pula para o próximo Quadro-Chave",
"message.outdated_client.title": "Cliente desatualizado",
"message.outdated_client.message": "Por favor atualize o Blockbench para a última versão para fazer isso.",
"action.export_bbmodel": "Exportar Projeto Blockbench",
"action.export_bbmodel.desc": "Exporta um Projeto do Blockbench com todos os cubos, texturas e animações",
"action.export_asset_archive": "Baixar Arquivo",
"action.export_asset_archive.desc": "Baixa um arquivo com o modelo e todas as texturas nele",
"action.upload_sketchfab": "Upload do Sketchfab",
"message.sketchfab.name_or_token": "Por favor insira seu token e nome do Sketchfab",
"dialog.sketchfab_uploader.title": "Upload do modelo Sketchfab",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "O token é usado para conectar o Blockbench à sua conta do Sketchfab. Você pode encontrá-lo em sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Nome do Modelo",
"dialog.sketchfab_uploader.description": "Descrição",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token para autorizar o Blockbench a fazer upload para sua conta do Sketchfab",
"panel.color": "Cor",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Смещение центра поворота по оси Z",
"action.brush_mode": "Режим кисти",
"action.brush_mode.desc": "Режим кисти",
"action.brush_color": "Цвет",
"action.brush_color.desc": "Цвет кисти",
"action.slider_brush_size": "Размер",
"action.slider_brush_size.desc": "Размер кисти в пикселях",
"action.slider_brush_opacity": "Непрозрачность",
@ -586,7 +584,6 @@
"panel.outliner": "Элементы",
"panel.options": "Поворот",
"panel.options.angle": "Угол",
"panel.options.origin": "Центр поворота",
"uv_editor.title": "Редактор UV",
"uv_editor.all_faces": "Все",
"uv_editor.no_faces": "Нет",
@ -823,10 +820,10 @@
"language_name": "Английский",
"message.plugin_reload": "Перезагружено %0 локальных плагинов",
"settings.brightness": "Яркость",
"settings.brightness.desc": "Brightness of the preview. Default is 50",
"menu.preview.perspective.reset": "Reset Camera",
"settings.brightness.desc": "Яркость дисплея. 50 по умолчанию",
"menu.preview.perspective.reset": "Сбросить камеру",
"action.fill_mode": "Режим заполнения",
"action.fill_mode.desc": "Mode of the fill tool",
"action.fill_mode.desc": "Режим инструмента заполнения",
"action.fill_mode.face": "Грань",
"action.fill_mode.color": "Цвет",
"action.fill_mode.cube": "Куб",
@ -854,5 +851,53 @@
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Тэги",
"settings.sketchfab_token": "Ключ Скетчфаб",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Цвет",
"data.origin": "Центр поворота",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Цвета кубов",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "Файл не найден",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "Flytta ursprunget på Z axeln",
"action.brush_mode": "Penselläge",
"action.brush_mode.desc": "Penselns läge",
"action.brush_color": "Färg",
"action.brush_color.desc": "Färg på penseln",
"action.slider_brush_size": "Storlek",
"action.slider_brush_size.desc": "Penselns radie i pixlar",
"action.slider_brush_opacity": "Opacitet",
@ -586,7 +584,6 @@
"panel.outliner": "Konturen",
"panel.options": "Rotation",
"panel.options.angle": "Vinkel",
"panel.options.origin": "Ursprung",
"uv_editor.title": "UV redigerare",
"uv_editor.all_faces": "Alla",
"uv_editor.no_faces": "Ingen",
@ -854,5 +851,53 @@
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Color",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
"message.install_plugin": "Installing the plugin %0",
"message.invalid_session.title": "Invalid Session Token",
"message.invalid_session.message": "The session you are trying to join has expired or the token provided is invalid.",
"dialog.create_texture.power": "Power-of-2 Size",
"dialog.create_gif.turn": "Turntable Speed",
"action.edit_session": "Edit Session...",
"action.edit_session.desc": "Connect to an edit session to collaborate with other users",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"panel.options.origin": "Origin",
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
"edit_session.join": "Join Session",
"edit_session.create": "Create Session",
"edit_session.quit": "Quit Session",
"edit_session.joined": "User %0 joined the session",
"edit_session.left": "User %0 left the session",
"edit_session.quit_session": "Left current session",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
"edit_session.connected": "Connected to a session"
}

@ -326,8 +326,6 @@
"action.slider_origin_z.desc": "以 Z 轴移动原点",
"action.brush_mode": "笔刷模式",
"action.brush_mode.desc": "笔刷的模式",
"action.brush_color": "颜色",
"action.brush_color.desc": "笔刷的颜色",
"action.slider_brush_size": "尺寸",
"action.slider_brush_size.desc": "笔刷的半径(以像素为单位)",
"action.slider_brush_opacity": "不透明度",
@ -420,7 +418,7 @@
"action.add_group.desc": "添加一个新的组",
"action.outliner_toggle": "切换更多选项",
"action.outliner_toggle.desc": "切换开关以在outliner中添加更多选项",
"action.duplicate": "复制",
"action.duplicate": "生成副本",
"action.duplicate.desc": "复制选定的方块或组",
"action.delete": "删除",
"action.delete.desc": "删除选定的方块或组",
@ -532,12 +530,12 @@
"menu.view.zoom": "缩放",
"menu.view.background": "背景",
"menu.view.screenshot": "截图",
"menu.cube.duplicate": "复制",
"menu.cube.duplicate": "生成副本",
"menu.cube.color": "标记颜色",
"menu.cube.texture": "材质纹理",
"menu.cube.texture.transparent": "透明度",
"menu.cube.texture.blank": "空白",
"menu.group.duplicate": "复制",
"menu.group.duplicate": "生成副本",
"menu.group.sort": "排序",
"menu.group.resolve": "解析",
"menu.texture.face": "应用到面",
@ -586,7 +584,6 @@
"panel.outliner": "大纲",
"panel.options": "旋转",
"panel.options.angle": "角度",
"panel.options.origin": "旋转原点",
"uv_editor.title": "UV 编辑器",
"uv_editor.all_faces": "全部",
"uv_editor.no_faces": "无",
@ -839,20 +836,43 @@
"dialog.create_texture.compress": "压缩模板",
"action.action_control": "动作控制",
"action.action_control.desc": "搜索并且执行任何可用的操作",
"keybindings.recording": "Recording Keybinding",
"keybindings.press": "Press a key or key combination or click anywhere on the screen to record your keybinding.",
"action.pivot_tool": "Pivot Tool",
"action.pivot_tool.desc": "Tool to change the pivot point of cubes and bones",
"action.slider_animation_speed": "Playback Speed",
"action.slider_animation_speed.desc": "Playback speed of the timeline in percent",
"action.previous_keyframe": "Previous Keyframe",
"action.previous_keyframe.desc": "Jump to the previous keyframe",
"action.next_keyframe": "Next Keyframe",
"action.next_keyframe.desc": "Jump to the next keyframe",
"message.outdated_client.title": "Outdated client",
"message.outdated_client.message": "Please update to the latest version of Blockbench to do this.",
"action.export_bbmodel": "Export Blockbench Project",
"action.export_bbmodel.desc": "Export a Blockbench project with all cubes, textures and animations",
"action.export_asset_archive": "Download Archive",
"action.export_asset_archive.desc": "Download an archive with the model and all textures in it"
"keybindings.recording": "录制按键绑定",
"keybindings.press": "输入按键或输入组合按键或单击屏幕上的任意位置以记录键绑定",
"action.pivot_tool": "枢轴工具",
"action.pivot_tool.desc": "用于更改立方体和骨骼的轴心点的工具",
"action.slider_animation_speed": "播放速度",
"action.slider_animation_speed.desc": "时间线的播放速度以百分比表示",
"action.previous_keyframe": "上一个关键帧",
"action.previous_keyframe.desc": "跳转到上一个关键帧",
"action.next_keyframe": "下一个关键帧",
"action.next_keyframe.desc": "跳转到下一个关键帧",
"message.outdated_client.title": "Blockbench已经过期(请更新)",
"message.outdated_client.message": "请更新到Blockbench的最新版本来执行此操作",
"action.export_bbmodel": "导出Blockbench项目",
"action.export_bbmodel.desc": "导出包含所有立方体纹理和动画的Blockbench项目",
"action.export_asset_archive": "下载存档",
"action.export_asset_archive.desc": "下载包含模型及其中所有纹理的存档",
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
"dialog.sketchfab_uploader.token": "API Token",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on sketchfab.com/settings/password",
"dialog.sketchfab_uploader.name": "Model Name",
"dialog.sketchfab_uploader.description": "Description",
"dialog.sketchfab_uploader.tags": "Tags",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
"panel.color": "Color",
"data.origin": "Origin",
"message.sketchfab.success": "Uploaded model successfully",
"message.sketchfab.error": "Failed to upload model to Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
"action.upload_sketchfab.desc": "Upload your model to Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"texture.error.file": "File not found",
"texture.error.invalid": "Invalid file",
"texture.error.ratio": "Invalid aspect ratio",
"texture.error.parent": "Texture file provided by parent model"
}

1
lib/peer.min.js vendored Normal file

File diff suppressed because one or more lines are too long

@ -500,6 +500,7 @@
}
function addColorToSelectionPalette(color) {
if (showSelectionPalette) {
var rgb = tinycolor(color).toRgbString();
if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
@ -559,7 +560,7 @@
}
function dragStart() {
if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0 || flat) {
reflow();
}
isDragging = true;

@ -4,7 +4,7 @@ const url = require('url')
let orig_win;
function createWindow() {
function createWindow(second_instance) {
if (app.requestSingleInstanceLock && !app.requestSingleInstanceLock()) {
return;
}
@ -66,12 +66,15 @@ function createWindow() {
win.on('closed', () => {
win = null
})
//win.webContents.openDevTools()
if (second_instance === true) {
win.webContents.second_instance = true
}
}
app.on('second-instance', function (event, argv, cwd) {
process.argv = argv
createWindow()
createWindow(true)
})
app.commandLine.appendSwitch('ignore-gpu-blacklist')

@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Minecraft Block Model Editor",
"version": "2.5.1",
"version": "2.6.0",
"license": "MIT",
"author": {
"name": "JannisX11",