From 46e5f39312c730f6666f6ccde70700928e82aceb Mon Sep 17 00:00:00 2001 From: JannisX11 <jannis4236@gmail.com> Date: Sun, 7 Apr 2019 18:53:33 +0200 Subject: [PATCH] v2.6.0 --- css/spectrum.css | 48 +- css/style.css | 1613 ++++++++++++++++++++------------------- index.html | 95 ++- index.php | 98 ++- js/TransformControls.js | 7 +- js/actions.js | 225 +++--- js/animations.js | 139 +++- js/api.js | 211 +++-- js/app.js | 94 ++- js/blockbench.js | 66 +- js/display.js | 4 +- js/edit_sessions.js | 239 ++++++ js/element.js | 258 +++++-- js/interface.js | 85 ++- js/io.js | 292 +++++-- js/keyboard.js | 2 +- js/language.js | 6 +- js/molang.js | 2 +- js/painter.js | 135 ++-- js/plugin_loader.js | 11 +- js/preview.js | 53 +- js/settings.js | 19 +- js/textures.js | 378 ++++----- js/transform.js | 19 +- js/tree.vue.js | 5 +- js/undo.js | 199 +++-- js/util.js | 3 + js/uv.js | 315 +++++--- js/web.js | 3 + lang/de.json | 55 +- lang/en.json | 56 +- lang/es.json | 65 +- lang/fr.json | 125 ++- lang/ja.json | 53 +- lang/nl.json | 67 +- lang/pl.json | 55 +- lang/pt.json | 903 ++++++++++++++++++++++ lang/ru.json | 59 +- lang/sv.json | 53 +- lang/zh.json | 64 +- lib/peer.min.js | 1 + lib/spectrum.js | 3 +- main.js | 9 +- package.json | 2 +- 44 files changed, 4325 insertions(+), 1869 deletions(-) create mode 100644 js/edit_sessions.js create mode 100644 lang/pt.json create mode 100644 lib/peer.min.js diff --git a/css/spectrum.css b/css/spectrum.css index 9e9e8662..08f2c983 100644 --- a/css/spectrum.css +++ b/css/spectrum.css @@ -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; diff --git a/css/style.css b/css/style.css index 640a0b8c..daf27fe5 100644 --- a/css/style.css +++ b/css/style.css @@ -7,12 +7,14 @@ outline: none; outline-color: rgba(0, 0, 0, 0); } + /*Webkit*/ a:-webkit-any-link { color: inherit; text-decoration: underline; } - - + a:-webkit-any-link:hover { + color: var(--color-light); + } ::-webkit-scrollbar { width: 6px; height: 6px; @@ -21,6 +23,9 @@ ::-webkit-scrollbar-track { background: var(--color-ui); } + ::-webkit-scrollbar-corner { + background: var(--color-ui); + } ::-webkit-scrollbar-thumb { background: var(--color-selected); @@ -29,6 +34,12 @@ ::-webkit-scrollbar-thumb:hover { background: var(--color-accent); } + /*Mozilla*/ + * { + scrollbar-width: thin; + scrollbar-color: var(--color-selected) var(--color-back); + } + ::selection { background: var(--color-accent); border-radius: 4px; @@ -50,18 +61,18 @@ local('Montserrat'), url(../font/Montserrat-Regular.ttf) format('truetype'); } + @font-face { + font-family: 'icomoon'; + src: url('../font/icomoon.eot?t564px'); + src: url('../font/icomoon.eot?t564px#iefix') format('embedded-opentype'), + url('../font/icomoon.ttf?t564px') format('truetype'), + url('../font/icomoon.woff?t564px') format('woff'), + url('../font/icomoon.svg?t564px#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + } -@font-face { - font-family: 'icomoon'; - src: url('../font/icomoon.eot?t564px'); - src: url('../font/icomoon.eot?t564px#iefix') format('embedded-opentype'), - url('../font/icomoon.ttf?t564px') format('truetype'), - url('../font/icomoon.woff?t564px') format('woff'), - url('../font/icomoon.svg?t564px#icomoon') format('svg'); - font-weight: normal; - font-style: normal; -} - +/*Icons*/ [class^="icon-"]:not(.fa), [class*=" icon-"]:not(.fa) { /* use !important to prevent issues with browser extensions that change fonts */ font-family: 'icomoon' !important; @@ -80,119 +91,115 @@ } .icon-blockbench_file:before { - content: "\e900"; + content: "\e900"; } .icon-vertexsnap:before { - content: "\e901"; + content: "\e901"; } .icon-create_bitmap:before { - content: "\e902"; + content: "\e902"; } .icon-objects:before { - content: "\e903"; + content: "\e903"; } .icon-bow:before { - content: "\e904"; + content: "\e904"; } .icon-bb_interface:before { - content: "\e905"; + content: "\e905"; } .icon-blockbench:before { - content: "\e906"; + content: "\e906"; } .icon-x11:before { - content: "\e907"; + content: "\e907"; } .icon-baby_zombie:before { - content: "\e908"; + content: "\e908"; } .icon-armor_stand:before { - content: "\e909"; + content: "\e909"; } .icon-armor_stand_small:before { - content: "\e90a"; + content: "\e90a"; } .icon-ground:before { - content: "\e90b"; + content: "\e90b"; } .icon-hud:before { - content: "\e90c"; + content: "\e90c"; } .icon-inventory_full:before { - content: "\e90d"; + content: "\e90d"; } .icon-inventory_nine:before { - content: "\e90e"; + content: "\e90e"; } .icon-inventory_single:before { - content: "\e90f"; + content: "\e90f"; } .icon-player_head:before { - content: "\e910"; + content: "\e910"; } .icon-zombie:before { - content: "\e911"; + content: "\e911"; } .icon-blockbench_inverted:before { - content: "\e912"; + content: "\e912"; } .icon-optifine_file:before { - content: "\e913"; + content: "\e913"; } .icon-saved:before { - content: "\e914"; + content: "\e914"; } .icon-player:before { - content: "\e915"; + content: "\e915"; } .icon-mirror_x:before { - content: "\e916"; + content: "\e916"; } .icon-mirror_y:before { - content: "\e917"; + content: "\e917"; } .icon-mirror_z:before { - content: "\e918"; + content: "\e918"; } - - - - - .material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; - } - i.fa_big { - font-size: 16pt; - height: 30px; - width: 24px; - text-align: center; - vertical-align: text-top; - } - .dialog .message_box_icon { - font-size: 40pt; - float: left; - padding-right: 8px; - min-width: 61px; - max-height: 54px; - max-width: 62px; - } - .dialog img.message_box_icon { - min-width: auto; - } + .material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + max-width: 24px; + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + i.fa_big { + font-size: 16pt; + height: 30px; + width: 24px; + text-align: center; + vertical-align: text-top; + } + .dialog .message_box_icon { + font-size: 40pt; + float: left; + padding-right: 8px; + min-width: 61px; + max-height: 54px; + max-width: 62px; + } + .dialog img.message_box_icon { + min-width: auto; + } /*Vars*/ body { @@ -215,15 +222,13 @@ height: 100%; overflow-y: hidden; } - - body { height: 100%; width: 100%; position: fixed; font-family: segoe ui, Helvetiva Neue, arial, sans-serif; - font-size: 18px; + font-size: 17px; font-weight: lighter; color: var(--color-text); @@ -231,6 +236,9 @@ background-color: var(--color-dark); image-rendering: pixelated; } + div { + cursor: default; + } a { text-decoration: none; cursor: default; @@ -243,90 +251,32 @@ b { font-weight: bolder; } - + li { + list-style: none; + cursor: default; + } + hr { + border-top: 1px solid var(--color-dark); + } h2 { font-weight: lighter; margin: 0; font-family: inherit; } h3 { + display: inline-block; font-size: 1.26em; padding-top: 6px; padding-bottom: 4px; font-family: inherit; + font-weight: inherit; + margin-left: 16px; + min-width: 10px; + height: 32px; } h4 { font-family: inherit; } - li { - list-style: none; - cursor: default; - } - div { - cursor: default; - } - div.tool.wide { - width: 79px; - padding-top: 0px; - } - .hidden, .tooltip_shift, .custom_select ul, .mobile_only, .m_disp { - display: none; - } - div.selection_only { - visibility: hidden; - } - a.open-in-browser { - cursor: pointer; - } - .f_left { - float: left; - } - .f_right { - float: right !important; - } - .i_b { - display: inline-block; - } - label.inline_label { - padding-left: 8px; - padding-right: 8px; - padding-top: 2px; - } - .progress_bar { - background-color: var(--color-back); - height: 20px; - width: 488px; - margin-left: 12px; - margin-bottom: 24px; - margin-top: 12px; - } - .progress_bar_inner { - background-color: var(--color-accent); - height: 100%; - width: 0px; - } - .accent_color { - color: var(--color-accent); - font-weight: normal; - } - .slash { - color: var(--color-light); - padding-left: 3px; - padding-right: 3px; - font-weight: normal; - display: inline-block; - } - -/*Axis Colors*/ - .color_x { - color: #d50a0a; - } - .color_y { - color: #23d400; - } - .color_z { - color: #0894ed; - } /*Inputs*/ input { @@ -350,6 +300,13 @@ vertical-align: middle; cursor: default; outline: none; + height: 38px; + min-width: 100px; + width: auto; + color: var(--color-text); + padding-right: 16px; + padding-left: 16px; + font-weight: normal; } button:hover { background: var(--color-accent); @@ -361,7 +318,15 @@ outline: none; outline-color: var(--color-accent); border: none; - background-color: var(--color-back); + background-color: var(--color-button); + height: 32px; + padding-top: 2px; + padding-left: 10px; + padding-right: 10px; + color: var(--color-text); + } + select:hover{ + color: var(--color-light); } select option { -webkit-appearance: none; @@ -381,10 +346,6 @@ resize: none; outline: none; } - .code { - font-family: consolas, monospace; - font-size: 16px; - } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button, { -webkit-appeareance: none; } @@ -486,6 +447,297 @@ background-color: var(--color-selected); } +/*Defaults*/ + div.tool.wide { + width: 79px; + padding-top: 0px; + } + .hidden, .tooltip_shift, .custom_select ul, .mobile_only, .m_disp { + display: none; + } + div.selection_only { + visibility: hidden; + } + a.open-in-browser { + cursor: pointer; + } + .f_left { + float: left; + } + .f_right { + float: right !important; + } + .i_b { + display: inline-block; + } + label.inline_label { + padding-left: 8px; + padding-right: 8px; + padding-top: 2px; + } + .progress_bar { + background-color: var(--color-back); + height: 20px; + margin-top: 12px; + } + .progress_bar_inner { + background-color: var(--color-accent); + height: 100%; + width: 0px; + } + .accent_color { + color: var(--color-accent); + font-weight: normal; + } + .slash { + color: var(--color-light); + padding-left: 3px; + padding-right: 3px; + font-weight: normal; + display: inline-block; + } + .code { + font-family: consolas, monospace; + font-size: 16px; + } + .small_text { + font-size: 0.94em; + } + .color_x { + color: #d50a0a; + } + .color_y { + color: #23d400; + } + .color_z { + color: #0894ed; + } + .button { + display: inline-block; + width: 30px; + text-align: center; + cursor: default; + } + .dark_bordered { + height: 32px; + padding-left: 4px; + background-color: var(--color-back); + border: 1px solid var(--color-border); + } + .input_wide { + width: 100%; + padding: 8px; + height: 40px; + padding-bottom: 5px; + } + +/*General*/ + canvas.preview { + background-repeat: no-repeat; + } + .text_padding { + margin-left: 5px; + margin-right: 5px; + } + .toolbar_label { + margin-top: 4px; + } + .bar { + height: 32px; + margin-top: 2px; + } + .bar > * { + float: left; + } + .list { + background-color: var(--color-back); + height: calc(100% - 86px); + width: calc(100% - 2px); + overflow-y: scroll; + flex-grow: 1; + } + body.animation_mode #cubes_list { + height: calc(100% - 50px); + } + body.animation_mode #cubes_list .outliner_object.cube { + display: none; + } + #quick_message_box { + position: fixed; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + margin-top: 420px; + z-index: 100; + min-width: 100px; + max-width: 200px; + background-color: var(--color-bright_ui); + color: var(--color-text_acc); + box-shadow: 0 0 2px black; + text-align: center; + cursor: default; + pointer-events: none; + } + .uv_message_box { + position: absolute; + margin-left: auto; + margin-right: auto; + z-index: 101; + min-width: 100px; + max-width: 200px; + background-color: var(--color-ui); + color: var(--color-light); + box-shadow: 0 0 2px black; + text-align: center; + cursor: default; + top: 40px; + left: 60px; + } + #selection_box { + position: absolute; + display: block; + border: 1px solid var(--color-accent); + background-color: rgba(40,50,60,0.5); + pointer-events: none; + } + + .spinning { + -webkit-animation: spin 2s linear infinite; + -moz-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + } + @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } + @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } + @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } + +/*Dialog*/ + #blackout { + display: none; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + background-color: #000; + opacity: 0.5; + z-index: 17; + } + #dialog_close_button { + position: absolute; + z-index: 102; + right: 5px; + top: 5px; + height: 28px; + } + #dialog_close_button:hover { + color: var(--color-light); + } + .dialog:not(.draggable) #dialog_close_button { + top: 8px; + right: -34px; + } + + .dialog { + display: none; + position: fixed; + width: 540px; + top: 0; + background-color: var(--color-ui); + border: 1px solid var(--color-border); + z-index: 18; + box-shadow: 0 0px 40px #000; + } + .dialog:not(.draggable) { + left: 0; + right: 0; + top:0; + margin-right: auto; + margin-left: auto; + } + .dialog_bar { + min-height: 32px; + margin-top: 4px; + margin-bottom: 4px; + height: auto; + } + .dialog_bar.narrow { + min-height: 30px; + } + .dialog_bar.button_bar { + text-align: right; + } + .dialog_bar button.large { + margin-bottom: 0; + margin-top: 16px; + } + .dialog .tab { + float: left; + width: 25%; + height: 34px; + text-align: center; + padding-top: 4px; + background: var(--color-dark); + vertical-align: middle; + cursor: default; + } + .dialog#settings .tab:firstChild { + margin-left: 0; + width: 25%; + } + .dialog .tab:hover { + color: var(--color-light); + } + .dialog .tab.open { + background: transparent; + border-top: none; + transition: border-top 200 ease-in; + } + .dialog .tab_content { + height: calc(100% - 90px); + width: 100%; + padding-bottom: 16px; + } + #settings_tab_bar { + margin: -24px; + margin-bottom: 0; + } + .dialog p { + margin-top: 8px; + margin-bottom: 8px; + } + .dialog h3 { + margin-left: 16px; + } + .dialog.draggable h2 { + padding-left: 26px; + padding-top: 2px; + margin-top: -24px; + margin-left: -24px; + margin-right: -24px; + background: var(--color-button); + height: 32px; + margin-bottom: 20px; + font-size: 1.12em; + } + .dialog_handle { + cursor: pointer; + overflow: hidden; + } + .dialog_bar label.in_toolbar { + padding-left: 0; + } + .dialog p.multiline_text { + margin-top: 0; + margin-bottom: 20px; + margin-left: 12px; + margin-right: 12px; + font-size: 0.86em; + user-select: text; + } + /*Menu Bar*/ ul#menu_bar { height: 32px; @@ -496,6 +748,7 @@ padding-top: 2px; padding-bottom: 0; float: left; + height: 100%; } li.menu_bar_point.opened { color: var(--color-light); @@ -506,8 +759,8 @@ } header div#title { width: auto; - padding-right: 16px; - padding-left: 10px; + padding-right: 8px; + padding-left: 8px; font-size: 1.2em; font-weight: normal; font-family: montserrat, arial, sans-serif; @@ -515,6 +768,7 @@ color: var(--color-light); vertical-align: top; min-width: 62px; + padding-top: 2px; } header div#title i { display: none; @@ -576,11 +830,123 @@ width: 16px; margin-left: -5px; } + .tool { + height: 32px; + width: 40px; + margin-left: 1px; + margin-right: 1px; + background: transparent; + display: inline-block; + text-align: center; + vertical-align: middle; + cursor: default; + float: left; + color: var(--color-text); + } + .tool i { + display: inline-block; + margin-top: 4px; + } + + .tool.sel { + border-bottom: 4px solid var(--color-accent); + } + + .tool.widget { + width: auto; + padding: 0; + } + .tool.widget.bar_text { + padding: 3px; + } + + .tool:hover { + color: var(--color-light); + } + .tool.head_right { + margin-top: -29px; + height: 24px; + float: right; + } + .tool.right_tool { + position: relative; + } + .toolbar_wrapper.tools > .toolbar { + margin-left: 20px; + margin-right: 20px; + } + + .placeholder { + width: 20px; + height: 10px; + float: left; + } + .toolbar_separator { + width: 2px; + height: 24px; + float: left; + background-color: var(--color-border); + margin: 4px; + margin-bottom: 0; + } + .text_button:hover { + color: var(--color-light); + } + select.tool { + -webkit-appearance: none; + width: 112px; + padding-left: 5px; + background-color: var(--color-back); + border: 1px solid var(--color-border); + } + .bar_select { + position: relative; + } + .bar_select:hover { + color: var(--color-light); + } + .bar_select select { + padding-right: 24px; + } + .bar_select::before { + content: "\23F7"; + display: block; + position: absolute; + height: 12px; + width: 16px; + pointer-events: none; + right: 6px; + top: 3px; + } + .half { + width: calc(50% - 4px); + } + .tooltip { + position: absolute; + height: 30px; + padding-left: 5px; + padding-right: 5px; + padding-top: 2px; + color: var(--color-text_acc); + margin-top: 32px; + display: none; + background: var(--color-bright_ui); + white-space: nowrap; + z-index: 125; + box-shadow: 0 0.4px 3.5px rgba(0, 0, 0, 0.6); + } + .tool:hover > .tooltip:not(:hover) { + display: block; + } + .tooltip_shift { + display: inline; + display: none; + } /*Layout*/ body { display: grid; - grid-template-columns: 328px auto 300px; + grid-template-columns: 338px auto 300px; grid-template-rows: 32px calc(100% - 58px) 26px; grid-template-areas: "header header header" @@ -692,179 +1058,6 @@ pointer-events: none; } -/*General*/ - hr { - border-top: 1px solid var(--color-dark); - } - .input_wide { - width: 100%; - padding: 5px; - background-color: var(--color-back); - } - canvas.preview { - background-repeat: no-repeat; - } - .bar { - height: 32px; - margin-top: 2px; - } - .bar > * { - float: left; - } - .text_padding { - margin-left: 5px; - margin-right: 5px; - } - .toolbar_label { - margin-top: 4px; - } - - .tool { - height: 32px; - width: 40px; - margin-left: 1px; - margin-right: 1px; - background: transparent; - display: inline-block; - text-align: center; - vertical-align: middle; - cursor: default; - float: left; - color: var(--color-text); - } - .tool i { - display: inline-block; - margin-top: 4px; - } - - .tool.sel { - border-bottom: 4px solid var(--color-accent); - } - - .tool.widget { - width: auto; - padding: 0; - } - .tool.widget.bar_text { - padding: 3px; - } - - .tool:hover { - color: var(--color-light); - } - .tool.head_right { - margin-top: -29px; - height: 24px; - float: right; - } - .tool.right_tool { - position: relative; - } - .toolbar_wrapper.tools > .toolbar { - margin-left: 20px; - margin-right: 20px; - } - - .placeholder { - width: 20px; - height: 10px; - float: left; - } - .toolbar_separator { - width: 2px; - height: 24px; - float: left; - background-color: var(--color-border); - margin: 4px; - margin-bottom: 0; - } - .text_button:hover { - color: var(--color-light); - } - - select.tool { - -webkit-appearance: none; - width: 112px; - padding-left: 5px; - background-color: var(--color-back); - border: 1px solid var(--color-border); - } - - .half { - width: calc(50% - 4px); - } - - .tooltip { - position: absolute; - height: 30px; - padding-left: 5px; - padding-right: 5px; - padding-top: 2px; - color: var(--color-text_acc); - margin-top: 32px; - display: none; - background: var(--color-bright_ui); - white-space: nowrap; - z-index: 125; - box-shadow: 0 0.4px 3.5px rgba(0, 0, 0, 0.6); - } - - .tool:hover > .tooltip:not(:hover) { - display: block; - } - .tooltip_shift { - display: inline; - display: none; - } - - .button { - display: inline-block; - width: 30px; - text-align: center; - cursor: default; - } - - button.large { - padding: 8px; - margin-bottom: 8px; - margin-left: 8px; - height: 40px; - min-width: 160px; - width: auto; - color: var(--color-text); - } - - .list { - background-color: var(--color-back); - height: calc(100% - 86px); - width: calc(100% - 2px); - overflow-y: scroll; - flex-grow: 1; - } - body.animation_mode #cubes_list { - height: calc(100% - 50px); - } - body.animation_mode #cubes_list .outliner_object.cube { - display: none; - } - - h3 { - font-weight: inherit; - margin-left: 16px; - min-width: 10px; - height: 32px; - display: inline-block; - } - - .spinning { - -webkit-animation: spin 2s linear infinite; - -moz-animation: spin 2s linear infinite; - animation: spin 2s linear infinite; - } - @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } - @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } - @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } - /*Displaytabs*/ .tabs_small input[type="radio"]:checked+label { @@ -906,6 +1099,9 @@ #cubes_list > div.vue-tree > ul > li > *:not(ul) { display: none; } + #cubes_list > div > ul > li > ul > li:last-child { + margin-bottom: 180px; + } .outliner_node .outliner_object i, .outliner_object i[class^="icon-"] { text-align: center; width: 18px; @@ -992,6 +1188,30 @@ font-size: 13pt; float: right; } + .outliner_object i.ec_0 { + color: #A2EBFF; /*light_blue*/ + } + .outliner_object i.ec_1 { + color: #FFF899; /*yellow*/ + } + .outliner_object i.ec_2 { + color: #E8BD7B; /*orange*/ + } + .outliner_object i.ec_3 { + color: #FFA7A4; /*red*/ + } + .outliner_object i.ec_4 { + color: #C5A6E8; /*purple*/ + } + .outliner_object i.ec_5 { + color: #A6C8FF; /*blue*/ + } + .outliner_object i.ec_6 { + color: #7BFFA3; /*green*/ + } + .outliner_object i.ec_7 { + color: #BDFFA6; /*lime*/ + } body > .outliner_object { width: 180px; padding-left: 0 !important; @@ -1032,11 +1252,11 @@ left: 20px; border: 1px solid var(--color-border); } + #options .bar .nslide, #options .bar .tool.wide { + width: 83px; + } - - - -/*Context*/ +/*(Context-)Menu*/ .contextMenu { position: absolute; display: block; @@ -1106,9 +1326,10 @@ background-color: var(--color-text); } -/*Action Select*/ +/*Action Control*/ #action_selector { position: absolute; + z-index: 24; right: 0; left: 0; margin-left: auto; @@ -1131,34 +1352,43 @@ right: 6px; top: 9px; } - #action_selector > ul { + #action_selector > div { + background-color: var(--color-ui); + color: var(--color-text); + width: 340px; + margin-left: 8px; + box-shadow: 0 0 5px black; + } + #action_selector > div > ul { background-color: var(--color-bright_ui); color: var(--color-text_acc); min-height: 20px; - width: 300px; - margin-left: 8px; - box-shadow: 0 0 5px black; + width: 340px; max-height: 400px; overflow-y: auto; overflow-x: hidden; } - #action_selector > ul > li { + #action_selector > div > div { + background-color: var(--color-ui); + color: var(--color-text); + height: auto; + padding: 5px; + font-size: 0.94em + } + #action_selector ul > li { padding: 4px; + overflow: hidden; } - #action_selector > ul > li.selected { + #action_selector ul > li.selected { background-color: var(--color-accent); + color: var(--color-text_acc); } - #action_selector > ul > li div.icon_wrapper { + #action_selector ul > li div.icon_wrapper { display: inline-block; height: 26px; vertical-align: text-top; } - - - - - #bar_item_list { max-height: 400px; margin-bottom: 20px; @@ -1262,7 +1492,6 @@ text-shadow: 0 0 5px #000; } - /*Animations*/ .panel#animations #animations_list { min-height: 126px; @@ -1294,6 +1523,20 @@ .animation > .animation_name { width: calc(100% - 28px); } + .panel#keyframe .tabs_small label { + font-size: 1em; + height: 32px; + width: 25%; + } + .panel#keyframe .bar label { + margin-top: 3px; + margin-left: 8px; + width: 32px; + text-align: center; + } + .panel#keyframe .bar input.dark_bordered { + width: calc(100% - 45px); + } /*Timeline*/ #timeline { @@ -1419,22 +1662,6 @@ margin-top: 28px; border-right: 1px solid var(--color-border); } -/*Keyframe Panel*/ - .panel#keyframe .tabs_small label { - font-size: 1em; - height: 32px; - width: 25%; - } - .panel#keyframe .bar label { - margin-top: 3px; - margin-left: 8px; - width: 32px; - text-align: center; - } - .panel#keyframe .bar input.dark_bordered { - width: calc(100% - 45px); - } - /*UV*/ #uv_dialog { @@ -1475,11 +1702,18 @@ float: right; } + #uv_viewport { + height: 320px; + width: 320px; + background-size: 320px; + position: relative; + overflow: hidden; + } #uv_frame { height: 320px; width: 320px; - margin-left: 3px; - margin-top: 3px; + margin: 4px; + margin-bottom: 0; background-size: 320px; background-repeat: no-repeat; position: relative; @@ -1500,13 +1734,19 @@ #uv_dialog h2.dialog_handle.entity_mode_only { margin: 0; } - #uv_size { height: 320px; width: 320px; cursor: move; box-sizing: border-box; z-index: 1; + box-shadow: 0 0 0 1.5px var(--color-text); + } + #uv_frame:hover #uv_size { + box-shadow: 0 0 0 1.5px white; + } + #uv_frame:hover #uv_size.dark_frame { + box-shadow: 0 0 0 1.5px black; } .uv_size_handle { position: absolute; @@ -1519,11 +1759,12 @@ .uv_transform_info { position: absolute; display: block; - right: 0; - bottom: -3px; + right: 8px; + top: 8px; font-size: 0.8em; cursor: default; pointer-events: none; + z-index: 5; } #uv .bar.next_to_title { margin-top: -32px; @@ -1532,145 +1773,43 @@ } #uv .bar.next_to_title .tool { } - -/*Options*/ - #cube_axis { - width: 58px; - position: relative; - z-index: 5; - } - #options .bar .nslide { - width: 83px; - } - #options .bar .tool.wide { - width: 83px; - } - .tool .tooltip.clip_right { - right: 0; - } - -/*Dialog*/ - #blackout { - display: none; + #uv_size .ui-resizable-se:before, + #uv_size .ui-resizable-sw:before, + #uv_size .ui-resizable-ne:before, + #uv_size .ui-resizable-nw:before { + content: ""; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - background-color: #000; - opacity: 0.5; - z-index: 17; + height: 8px; + width: 8px; + background-color: var(--color-text); } - #dialog_close_button { - position: absolute; - z-index: 102; - right: 5px; - top: 5px; - height: 28px; + #uv_frame:hover #uv_size .ui-resizable-handle:before { + background-color: white; } - #dialog_close_button:hover { - color: var(--color-light); + #uv_frame:hover #uv_size.dark_frame .ui-resizable-handle:before { + background-color: black; } - .dialog:not(.draggable) #dialog_close_button { - top: 8px; - right: -34px; + #uv_frame:hover #uv_size .ui-resizable-handle:hover:before { + background-color: var(--color-accent); + } + #uv_size .ui-resizable-se:before { + bottom: 1px; + right: 1px; + } + #uv_size .ui-resizable-sw:before { + bottom: 1px; + left: 1px; + } + #uv_size .ui-resizable-ne:before { + top: 1px; + right: 1px; + } + #uv_size .ui-resizable-nw:before { + top: 1px; + left: 1px; } - .dialog { - display: none; - position: fixed; - width: 540px; - top: 0; - background-color: var(--color-ui); - border: 1px solid var(--color-border); - z-index: 18; - box-shadow: 0 0px 40px #000; - } - .dialog:not(.draggable) { - left: 0; - right: 0; - top:0; - margin-right: auto; - margin-left: auto; - } - .dialog_bar { - min-height: 50px; - height: auto; - } - .dialog_bar.narrow { - min-height: 30px; - } - .dialog_bar.button_bar { - text-align: right; - } - .dialog .tab { - float: left; - width: calc(25% - 2px); - height: 34px; - text-align: center; - padding-top: 4px; - margin-left: 2px; - background: var(--color-dark); - vertical-align: middle; - cursor: default; - } - .dialog#settings .tab:firstChild { - margin-left: 0; - width: 25%; - } - .dialog .tab:hover { - color: var(--color-light); - } - .dialog .tab.open { - background: transparent; - border-top: none; - transition: border-top 200 ease-in; - } - .dialog .tab_content { - height: calc(100% - 90px); - width: 100%; - padding-bottom: 16px; - } - #settings_tab_bar { - margin: -24px; - margin-bottom: 0; - } - .dialog p { - margin-top: 16px; - } - .dialog h3 { - margin-left: 16px; - } - .dialog.draggable h2 { - padding-left: 26px; - margin-top: -24px; - margin-left: -24px; - margin-right: -24px; - background: var(--color-button); - height: 32px; - margin-bottom: 20px; - font-size: 1.12em; - } - .dialog_handle { - cursor: pointer; - overflow: hidden; - } - .dialog_bar label.in_toolbar { - padding-left: 0; - } - - #keybindlist { - max-height: 600px; - margin-bottom: 20px; - overflow-y: scroll; - } - #keybindlist > li { - width: 100%; - min-height: 34px; - padding-left: 6px; - } +/*Settings Dialog*/ #settings h3 > i { margin-top: 5px; float: left; @@ -1678,42 +1817,9 @@ #settings h3:hover { color: var(--color-light); } - #keybindlist .tool { - height: 32px; - width: 25px; - float: right; - } - #keybindlist li > div.keybindslot { - width: calc(48% - 32px); - padding: 2px; - padding-left: 8px; - height: 30px; - background-color: var(--color-button); - border: 1px solid var(--color-border); - font-size: 0.84em; - overflow: hidden; - white-space: nowrap; - display: inline-block; - } - #keybindlist li > div.keybindslot:hover { - background-color: var(--color-selected); - color: var(--color-light); - } - #keybindlist li > div:first-child { - background: transparent; - width: calc(52% - 28px); - text-align: right; - padding: 2px; - padding-left: 8px; - vertical-align: top; - display: inline-block; - } - #keybindlist > li > div:focus { - color: transparent; - text-shadow: 0 0 0 var(--color-light); - background-color: var(--color-dark); - } + + /*Settings*/ #settings li h3 { margin: 0; padding: 0; @@ -1740,7 +1846,7 @@ #settingslist li > .setting_label { display: inline-block; margin-left: 8px; - width: 420px; + width: 416px; } #settingslist .setting_name { font-size: 1.1em; @@ -1762,10 +1868,13 @@ padding: 10px; margin-left: 5px; } - #settingslist select { + #settingslist div.bar_select { margin: 8px; width: 96%; } + #settingslist div.bar_select select { + width: 100%; + } #settingslist li li i { font-size: 27pt; width: 34px; @@ -1775,11 +1884,97 @@ color: var(--color-light); } + /*Keybinds*/ + #keybindlist .tool { + height: 32px; + width: 25px; + float: right; + } + #keybindlist li > div.keybindslot { + width: calc(48% - 32px); + padding: 2px; + padding-left: 8px; + height: 30px; + background-color: var(--color-button); + border: 1px solid var(--color-border); + font-size: 0.84em; + overflow: hidden; + white-space: nowrap; + display: inline-block; + } + #keybindlist li > div.keybindslot:hover { + background-color: var(--color-selected); + color: var(--color-light); + } + #keybindlist li > div:first-child { + background: transparent; + width: calc(52% - 28px); + text-align: right; + padding: 2px; + padding-left: 8px; + vertical-align: top; + display: inline-block; + } + #keybindlist > li > div:focus { + color: transparent; + text-shadow: 0 0 0 var(--color-light); + background-color: var(--color-dark); + } + #keybindlist { + max-height: 600px; + margin-bottom: 20px; + overflow-y: scroll; + } + #keybindlist > li { + width: 100%; + min-height: 34px; + padding-left: 6px; + } + /*Colors*/ + div#color_wrapper { + columns: 2; + margin-bottom: 20px; + } + .color_field { + min-height: 64px; + width: 100% + } + .color_field .desc { + width: 172px; + display: inline-block; + } + .color_field p { + margin: 0; + font-size: 0.8em; + } + .color_field h4 { + margin: 0; + font-size: 1.2em; + } + input.color_input { + -webkit-appearance: none; + display: none; + } + label.color_input { + -webkit-appearance: none; + appearance: none; + outline: none; + height: 36px; + width: 46px; + margin: 4px; + margin-top: 10px; + display: inline-block; + vertical-align: top; + border: 1px solid var(--color-border); + } + /*About*/ #credits a:hover { text-decoration: underline; } + +/*Specific Dialogs*/ .dialog#updater .dialog_bar.narrow { padding-top: 3px; } @@ -1789,14 +1984,6 @@ margin-top: -64px; width: 240px; } - .dialog p.multiline_text { - margin-top: 0; - margin-bottom: 20px; - margin-left: 12px; - margin-right: 12px; - font-size: 0.86em; - user-select: text; - } .dialog#texture_edit p.multiline_text { width: 344px; min-height: 51px; @@ -1823,8 +2010,103 @@ width: 73px; border: none; } + /*Scale*/ + #model_scale_range { + width: calc(100% - 50px); + float: left; + height: 31px; + padding-top: 3px; + } + #model_scale_label { + width: 50px; + padding-top: 3px; + text-align: center; + float: left; + } + #scaling_clipping_warning { + color: #ff384b; + } + .borderless { + margin: 0; + padding: 0; + border: none; + } -/*Keybind recording*/ + /*Extrusion*/ + #image_extruder label { + float: left; + margin-right: 8px; + padding-top: 5px; + } + #scan_tolerance { + width: 200px; + } + #scan_tolerance_label { + margin-left: 8px; + } + #extrusion_canvas { + margin-left: 12px; + border-bottom: 1px solid #000; + border-right: 1px solid #000; + } + button.large:first-child { + margin-left: 0; + } + + /*Selection Creator*/ + input[type=range].dark_bordered { + height: 32px; + padding-top: 3px; + padding-left: 0; + } + select.dark_bordered { + color: var(--color-text); + padding: 6px; + padding-top: 2px; + height: 32px; + } + label.name_space_left { + float: left; + min-width: 155px; + margin-left: 1px; + margin-right: 8px; + } + label.in_toolbar { + text-align: left; + color: var(--color-text); + padding: 8px; + padding-top: 3px; + padding-bottom: 0; + } + + /*PE Import Dialog*/ + #pe_list { + max-height: 600px; + margin-bottom: 20px; + overflow-y: scroll; + } + #pe_list li > * { + margin: 0; + margin-left: 12px; + cursor: default; + overflow-x: hidden; + } + #pe_list li.selected { + background-color: var(--color-selected); + } + #pe_list li > .pe_icon { + height: 48px; + width: 48px; + float: left; + margin: 4px; + margin-right: 8px; + margin-bottom: 0; + } + #pe_list li > h4 > span { + font-size: 0.7; + } + +/*Keybind Recording*/ #overlay_message_box { height: 100%; width: 100%; @@ -1844,36 +2126,11 @@ } #overlay_message_box h3 i { vertical-align: sub; - padding: 8px; + margin: 8px; font-size: 1.2em; } /*Display*/ - .mode_tab { - display: block; - float: right; - height: 32px; - padding-right: 12px; - padding-left: 12px; - margin-left: 4px; - padding-top: 3px; - font-size: 1.1em; - font-weight: normal; - cursor: pointer; - background-color: var(--color-back); - border: 1px solid var(--color-border); - } - .mode_tab.open { - background-color: var(--color-selected); - border-bottom: none; - cursor: default; - } - .mode_tab:not(.open):hover { - color: var(--color-light); - } - #display_settings { - - } #display_bar .tool, #display_ref_bar > div { width: calc(100% / 8 - 2px); max-width: 52px; @@ -1927,179 +2184,6 @@ margin-right: auto; } - -/*Scale*/ - #model_scale_range { - width: calc(100% - 50px); - float: left; - height: 31px; - padding-top: 3px; - } - #model_scale_label { - width: 50px; - padding-top: 3px; - text-align: center; - float: left; - } - #scaling_clipping_warning { - color: #ff384b; - } - .borderless { - margin: 0; - padding: 0; - border: none; - } - -/*Extrusion*/ - #image_extruder label { - float: left; - margin-right: 8px; - padding-top: 5px; - } - #scan_tolerance { - width: 200px; - } - #scan_tolerance_label { - margin-left: 8px; - } - #extrusion_canvas { - margin-left: 12px; - border-bottom: 1px solid #000; - border-right: 1px solid #000; - } - button.large:first-child { - margin-left: 0; - } - - /*Selection Creator*/ - .dark_bordered { - height: 32px; - padding-left: 4px; - background-color: var(--color-back); - border: 1px solid var(--color-border); - } - input[type=range].dark_bordered { - height: 32px; - padding-top: 3px; - padding-left: 0; - } - select.dark_bordered { - color: var(--color-text); - padding: 6px; - padding-top: 2px; - height: 32px; - } - label.name_space_left { - float: left; - min-width: 155px; - margin-left: 1px; - margin-right: 8px; - } - label.in_toolbar { - text-align: left; - color: var(--color-text); - padding: 8px; - padding-top: 3px; - padding-bottom: 0; - } - -/*Layout Menu*/ - div#color_wrapper { - columns: 2; - margin-bottom: 20px; - } - .color_field { - min-height: 64px; - width: 100% - } - .color_field .desc { - width: 172px; - display: inline-block; - } - .color_field p { - margin: 0; - font-size: 0.8em; - } - .color_field h4 { - margin: 0; - font-size: 1.2em; - } - input.color_input { - -webkit-appearance: none; - display: none; - } - label.color_input { - -webkit-appearance: none; - appearance: none; - outline: none; - height: 36px; - width: 46px; - margin: 4px; - margin-top: 10px; - display: inline-block; - vertical-align: top; - border: 1px solid var(--color-border); - } - - #quick_message_box { - position: fixed; - left: 0; - right: 0; - margin-left: auto; - margin-right: auto; - margin-top: 420px; - z-index: 100; - min-width: 100px; - max-width: 200px; - background-color: var(--color-ui); - color: var(--color-light); - box-shadow: 0 0 2px black; - text-align: center; - cursor: default; - pointer-events: none; - } - .uv_message_box { - position: absolute; - margin-left: auto; - margin-right: auto; - z-index: 101; - min-width: 100px; - max-width: 200px; - background-color: var(--color-ui); - color: var(--color-light); - box-shadow: 0 0 2px black; - text-align: center; - cursor: default; - top: 40px; - left: 60px; - } - -/*PE Import Dialog*/ - #pe_list { - max-height: 600px; - margin-bottom: 20px; - overflow-y: scroll; - } - #pe_list li > * { - margin: 0; - margin-left: 12px; - cursor: default; - overflow-x: hidden; - } - #pe_list li.selected { - background-color: var(--color-selected); - } - #pe_list li > .pe_icon { - height: 48px; - width: 48px; - float: left; - margin: 4px; - margin-right: 8px; - } - #pe_list li > h4 > span { - font-size: 0.7; - } - /*Plugin Menu*/ .bar.next_to_title { display: inline-block; @@ -2233,8 +2317,7 @@ margin-top: 6px; } -/* Toolbar Dialog */ - +/*Toolbar Dialog*/ #bar_item_list { max-height: 400px; margin-bottom: 20px; @@ -2272,9 +2355,7 @@ border: 1px solid var(--color-border); } - /*Status Bar*/ - #status_bar { position: relative; padding: 2px; @@ -2300,33 +2381,3 @@ bottom: 0; left: 0; } - -/*Entity Mode*/ - - a.ml5 i.shade_mirror_on:before { - content: "\f005"; - } - a.ml5 i.shade_mirror_off:before { - content: "\f006"; - } - body.entity_mode a.ml5 i.shade_mirror_on:before { - content: "\f005"; - } - body.entity_mode a.ml5 i.shade_mirror_off:before { - content: "\f005"; - } - - - #cubes_list > div > ul > li > ul > li:last-child { - margin-bottom: 180px; - } - - - -#selection_box { - position: absolute; - display: block; - border: 1px solid var(--color-accent); - background-color: rgba(40,50,60,0.5); - pointer-events: none; -} \ No newline at end of file diff --git a/index.html b/index.html index 6f705665..4788093a 100644 --- a/index.html +++ b/index.html @@ -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> diff --git a/index.php b/index.php index b5964a3d..74140e07 100644 --- a/index.php +++ b/index.php @@ -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> diff --git a/js/TransformControls.js b/js/TransformControls.js index 779d031f..c8891eee 100644 --- a/js/TransformControls.js +++ b/js/TransformControls.js @@ -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') diff --git a/js/actions.js b/js/actions.js index 45b38526..f3a9ec4e 100644 --- a/js/actions.js +++ b/js/actions.js @@ -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 diff --git a/js/animations.js b/js/animations.js index 5dc54b5d..08bf4fca 100644 --- a/js/animations.js +++ b/js/animations.js @@ -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() } }) diff --git a/js/api.js b/js/api.js index 421d182f..c65ff6b6 100644 --- a/js/api.js +++ b/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') diff --git a/js/app.js b/js/app.js index 6a81b732..22ca7b6b 100644 --- a/js/app.js +++ b/js/app.js @@ -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(); diff --git a/js/blockbench.js b/js/blockbench.js index 6134e296..aba47e7a 100644 --- a/js/blockbench.js +++ b/js/blockbench.js @@ -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') + } } } diff --git a/js/display.js b/js/display.js index f46d7c5d..6dd0baf5 100644 --- a/js/display.js +++ b/js/display.js @@ -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 diff --git a/js/edit_sessions.js b/js/edit_sessions.js new file mode 100644 index 00000000..b16da7e0 --- /dev/null +++ b/js/edit_sessions.js @@ -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 + }) +}) \ No newline at end of file diff --git a/js/element.js b/js/element.js index 6f6ae920..55a61f7a 100644 --- a/js/element.js +++ b/js/element.js @@ -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 + }) + } + }) }) diff --git a/js/interface.js b/js/interface.js index a6df59de..224e2457 100644 --- a/js/interface.js +++ b/js/interface.js @@ -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 diff --git a/js/io.js b/js/io.js index 1d3cba23..1b553a68 100644 --- a/js/io.js +++ b/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() + } + }) }) diff --git a/js/keyboard.js b/js/keyboard.js index 44889709..b2673f13 100644 --- a/js/keyboard.js +++ b/js/keyboard.js @@ -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] diff --git a/js/language.js b/js/language.js index cd1d39b8..7ffa52cc 100644 --- a/js/language.js +++ b/js/language.js @@ -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) { diff --git a/js/molang.js b/js/molang.js index 00fd46fa..f0eabdad 100644 --- a/js/molang.js +++ b/js/molang.js @@ -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') diff --git a/js/painter.js b/js/painter.js index 779fc2d3..95304f0d 100644 --- a/js/painter.js +++ b/js/painter.js @@ -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'), diff --git a/js/plugin_loader.js b/js/plugin_loader.js index d79b0202..ebcb7baa 100644 --- a/js/plugin_loader.js +++ b/js/plugin_loader.js @@ -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({ diff --git a/js/preview.js b/js/preview.js index d3d2d834..5390050f 100644 --- a/js/preview.js +++ b/js/preview.js @@ -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({ diff --git a/js/settings.js b/js/settings.js index 17d0b77a..aaffb0aa 100644 --- a/js/settings.js +++ b/js/settings.js @@ -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 diff --git a/js/textures.js b/js/textures.js index 7e6eae60..568b85c4 100644 --- a/js/textures.js +++ b/js/textures.js @@ -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') } diff --git a/js/transform.js b/js/transform.js index 3c49eab5..15157d25 100644 --- a/js/transform.js +++ b/js/transform.js @@ -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) } }) diff --git a/js/tree.vue.js b/js/tree.vue.js index 002b96e0..7eb9d6e3 100644 --- a/js/tree.vue.js +++ b/js/tree.vue.js @@ -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; diff --git a/js/undo.js b/js/undo.js index c248b71b..315b7cae 100644 --- a/js/undo.js +++ b/js/undo.js @@ -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) { diff --git a/js/util.js b/js/util.js index 7e1f6fe2..b3d43d82 100644 --- a/js/util.js +++ b/js/util.js @@ -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}); diff --git a/js/uv.js b/js/uv.js index c7496df9..ef4a4273 100644 --- a/js/uv.js +++ b/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', diff --git a/js/web.js b/js/web.js index 3f9c66e7..c655802d 100644 --- a/js/web.js +++ b/js/web.js @@ -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() !== '') { diff --git a/lang/de.json b/lang/de.json index 41846cf9..e07f7d63 100644 --- a/lang/de.json +++ b/lang/de.json @@ -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" } \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index 224ab4f4..0d93f962 100644 --- a/lang/en.json +++ b/lang/en.json @@ -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", diff --git a/lang/es.json b/lang/es.json index 0ff9d077..ba2f379a 100644 --- a/lang/es.json +++ b/lang/es.json @@ -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" } \ No newline at end of file diff --git a/lang/fr.json b/lang/fr.json index 8239bd70..efe3a2ed 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -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 d’icô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 l’origine sur l’axe 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" } \ No newline at end of file diff --git a/lang/ja.json b/lang/ja.json index cd5e2fb1..9f6c0420 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -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" } \ No newline at end of file diff --git a/lang/nl.json b/lang/nl.json index a2e03686..4523d409 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -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" } \ No newline at end of file diff --git a/lang/pl.json b/lang/pl.json index a95320a7..be62063c 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -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" } \ No newline at end of file diff --git a/lang/pt.json b/lang/pt.json new file mode 100644 index 00000000..df22b9c0 --- /dev/null +++ b/lang/pt.json @@ -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" +} \ No newline at end of file diff --git a/lang/ru.json b/lang/ru.json index b85decd0..a662fa40 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -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" } \ No newline at end of file diff --git a/lang/sv.json b/lang/sv.json index 212d1df6..80c5e376 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -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" } \ No newline at end of file diff --git a/lang/zh.json b/lang/zh.json index bca3656f..c5a62d0e 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -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" } \ No newline at end of file diff --git a/lib/peer.min.js b/lib/peer.min.js new file mode 100644 index 00000000..03365756 --- /dev/null +++ b/lib/peer.min.js @@ -0,0 +1 @@ +!function o(s,a,u){function c(t,e){if(!a[t]){if(!s[t]){var i="function"==typeof require&&require;if(!e&&i)return i(t,!0);if(p)return p(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var r=a[t]={exports:{}};s[t][0].call(r.exports,function(e){return c(s[t][1][e]||e)},r,r.exports,o,s,a,u)}return a[t].exports}for(var p="function"==typeof require&&require,e=0;e<u.length;e++)c(u[e]);return c}({1:[function(e,t,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0}),i.RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription,i.RTCPeerConnection=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection,i.RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate},{}],2:[function(e,t,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0});var a=e("./util"),n=e("eventemitter3"),r=e("./negotiator"),o=e("reliable");function s(e,t,i){if(!(this instanceof s))return new s(e,t,i);n.EventEmitter.call(this),this.options=a.util.extend({serialization:"binary",reliable:!1},i),this.open=!1,this.type="data",this.peer=e,this.provider=t,this.id=this.options.connectionId||s._idPrefix+a.util.randomToken(),this.label=this.options.label||this.id,this.metadata=this.options.metadata,this.serialization=this.options.serialization,this.reliable=this.options.reliable,this._buffer=[],this._buffering=!1,this.bufferSize=0,this._chunkedData={},this.options._payload&&(this._peerBrowser=this.options._payload.browser),r.Negotiator.startConnection(this,this.options._payload||{originator:!0})}i.DataConnection=s,a.util.inherits(s,n.EventEmitter),s._idPrefix="dc_",s.prototype.initialize=function(e){this._dc=this.dataChannel=e,this._configureDataChannel()},s.prototype._configureDataChannel=function(){var t=this;a.util.supports.sctp&&(this._dc.binaryType="arraybuffer"),this._dc.onopen=function(){a.util.log("Data channel connection success"),t.open=!0,t.emit("open")},!a.util.supports.sctp&&this.reliable&&(this._reliable=new o.Reliable(this._dc,a.util.debug)),this._reliable?this._reliable.onmessage=function(e){t.emit("data",e)}:this._dc.onmessage=function(e){t._handleDataMessage(e)},this._dc.onclose=function(e){a.util.log("DataChannel closed for:",t.peer),t.close()}},s.prototype._handleDataMessage=function(e){var t=this,i=e.data,n=i.constructor;if("binary"===this.serialization||"binary-utf8"===this.serialization){if(n===Blob)return void a.util.blobToArrayBuffer(i,function(e){i=a.util.unpack(e),t.emit("data",i)});if(n===ArrayBuffer)i=a.util.unpack(i);else if(n===String){var r=a.util.binaryStringToArrayBuffer(i);i=a.util.unpack(r)}}else"json"===this.serialization&&(i=JSON.parse(i));if(i.__peerData){var o=i.__peerData,s=this._chunkedData[o]||{data:[],count:0,total:i.total};return s.data[i.n]=i.data,s.count+=1,s.total===s.count&&(delete this._chunkedData[o],i=new Blob(s.data),this._handleDataMessage({data:i})),void(this._chunkedData[o]=s)}this.emit("data",i)},s.prototype.close=function(){this.open&&(this.open=!1,r.Negotiator.cleanup(this),this.emit("close"))},s.prototype.send=function(e,t){if(this.open)if(this._reliable)this._reliable.send(e);else{var i=this;if("json"===this.serialization)this._bufferedSend(JSON.stringify(e));else if("binary"===this.serialization||"binary-utf8"===this.serialization){var n=a.util.pack(e);if((a.util.chunkedBrowsers[this._peerBrowser]||a.util.chunkedBrowsers[a.util.browser])&&!t&&n.size>a.util.chunkedMTU)return void this._sendChunks(n);a.util.supports.sctp?a.util.supports.binaryBlob?this._bufferedSend(n):a.util.blobToArrayBuffer(n,function(e){i._bufferedSend(e)}):a.util.blobToBinaryString(n,function(e){i._bufferedSend(e)})}else this._bufferedSend(e)}else this.emit("error",new Error("Connection is not open. You should listen for the `open` event before sending messages."))},s.prototype._bufferedSend=function(e){!this._buffering&&this._trySend(e)||(this._buffer.push(e),this.bufferSize=this._buffer.length)},s.prototype._trySend=function(e){try{this._dc.send(e)}catch(e){this._buffering=!0;var t=this;return setTimeout(function(){t._buffering=!1,t._tryBuffer()},100),!1}return!0},s.prototype._tryBuffer=function(){if(0!==this._buffer.length){var e=this._buffer[0];this._trySend(e)&&(this._buffer.shift(),this.bufferSize=this._buffer.length,this._tryBuffer())}},s.prototype._sendChunks=function(e){for(var t=a.util.chunk(e),i=0,n=t.length;i<n;i+=1){e=t[i];this.send(e,!0)}},s.prototype.handleMessage=function(e){var t=e.payload;switch(e.type){case"ANSWER":this._peerBrowser=t.browser,r.Negotiator.handleSDP(e.type,this,t.sdp);break;case"CANDIDATE":r.Negotiator.handleCandidate(this,t.candidate);break;default:a.util.warn("Unrecognized message type:",e.type,"from peer:",this.peer)}}},{"./negotiator":5,"./util":8,eventemitter3:9,reliable:12}],3:[function(e,t,i){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(i,"__esModule",{value:!0});var r=e("./util"),o=e("./adapter"),s=e("./socket"),a=e("./mediaconnection"),u=e("./dataconnection"),c=e("./peer"),p=e("./negotiator"),l=n(e("js-binarypack"));window.Socket=s.Socket,window.MediaConnection=a.MediaConnection,window.DataConnection=u.DataConnection,window.Peer=c.Peer,window.RTCPeerConnection=o.RTCPeerConnection,window.RTCSessionDescription=o.RTCSessionDescription,window.RTCIceCandidate=o.RTCIceCandidate,window.Negotiator=p.Negotiator,window.util=r.util,window.BinaryPack=l.default},{"./adapter":1,"./dataconnection":2,"./mediaconnection":4,"./negotiator":5,"./peer":6,"./socket":7,"./util":8,"js-binarypack":10}],4:[function(e,t,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0});var r=e("./util"),n=e("eventemitter3"),o=e("./negotiator");function s(e,t,i){if(!(this instanceof s))return new s(e,t,i);n.EventEmitter.call(this),this.options=r.util.extend({},i),this.open=!1,this.type="media",this.peer=e,this.provider=t,this.metadata=this.options.metadata,this.localStream=this.options._stream,this.id=this.options.connectionId||s._idPrefix+r.util.randomToken(),this.localStream&&o.Negotiator.startConnection(this,{_stream:this.localStream,originator:!0})}i.MediaConnection=s,r.util.inherits(s,n.EventEmitter),s._idPrefix="mc_",s.prototype.addStream=function(e){r.util.log("Receiving stream",e),this.remoteStream=e,this.emit("stream",e)},s.prototype.handleMessage=function(e){var t=e.payload;switch(e.type){case"ANSWER":o.Negotiator.handleSDP(e.type,this,t.sdp),this.open=!0;break;case"CANDIDATE":o.Negotiator.handleCandidate(this,t.candidate);break;default:r.util.warn("Unrecognized message type:",e.type,"from peer:",this.peer)}},s.prototype.answer=function(e){if(this.localStream)r.util.warn("Local stream already exists on this MediaConnection. Are you answering a call twice?");else{this.options._payload._stream=e,this.localStream=e,o.Negotiator.startConnection(this,this.options._payload);for(var t=this.provider._getMessages(this.id),i=0,n=t.length;i<n;i+=1)this.handleMessage(t[i]);this.open=!0}},s.prototype.close=function(){this.open&&(this.open=!1,o.Negotiator.cleanup(this),this.emit("close"))}},{"./negotiator":5,"./util":8,eventemitter3:9}],5:[function(e,t,o){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var s=e("./util"),r=e("./adapter");o.Negotiator={pcs:{data:{},media:{}},queue:[]},o.Negotiator._idPrefix="pc_",o.Negotiator.startConnection=function(e,t){var i=o.Negotiator._getPeerConnection(e,t);if(e.pc=e.peerConnection=i,"media"===e.type&&t._stream&&i.addStream(t._stream),t.originator){if("data"===e.type){var n={};s.util.supports.sctp||(n={reliable:t.reliable});var r=i.createDataChannel(e.label,n);e.initialize(r)}o.Negotiator._makeOffer(e)}else o.Negotiator.handleSDP("OFFER",e,t.sdp)},o.Negotiator._getPeerConnection=function(e,t){o.Negotiator.pcs[e.type]||s.util.error(e.type+" is not a valid connection type. Maybe you overrode the `type` property somewhere."),o.Negotiator.pcs[e.type][e.peer]||(o.Negotiator.pcs[e.type][e.peer]={});var i;o.Negotiator.pcs[e.type][e.peer];return t.pc&&(i=o.Negotiator.pcs[e.type][e.peer][t.pc]),i&&"stable"===i.signalingState||(i=o.Negotiator._startPeerConnection(e)),i},o.Negotiator._startPeerConnection=function(e){s.util.log("Creating RTCPeerConnection.");var t=o.Negotiator._idPrefix+s.util.randomToken(),i={};"data"!==e.type||s.util.supports.sctp?"media"===e.type&&(i={optional:[{DtlsSrtpKeyAgreement:!0}]}):i={optional:[{RtpDataChannels:!0}]};var n=new r.RTCPeerConnection(e.provider.options.config,i);return o.Negotiator.pcs[e.type][e.peer][t]=n,o.Negotiator._setupListeners(e,n,t),n},o.Negotiator._setupListeners=function(t,e,i){var n=t.peer,r=t.id,o=t.provider;s.util.log("Listening for ICE candidates."),e.onicecandidate=function(e){e.candidate&&(s.util.log("Received ICE candidates for:",t.peer),o.socket.send({type:"CANDIDATE",payload:{candidate:e.candidate,type:t.type,connectionId:t.id},dst:n}))},e.oniceconnectionstatechange=function(){switch(e.iceConnectionState){case"failed":s.util.log("iceConnectionState is disconnected, closing connections to "+n),t.emit("error",new Error("Negotiation of connection to "+n+" failed.")),t.close();break;case"disconnected":s.util.log("iceConnectionState is disconnected, closing connections to "+n),t.close();break;case"completed":e.onicecandidate=s.util.noop}},e.onicechange=e.oniceconnectionstatechange,s.util.log("Listening for data channel"),e.ondatachannel=function(e){s.util.log("Received data channel");var t=e.channel;o.getConnection(n,r).initialize(t)},s.util.log("Listening for remote stream"),e.ontrack=function(e){s.util.log("Received remote stream");var t=e.streams[0],i=o.getConnection(n,r);"media"===i.type&&i.addStream(t)}},o.Negotiator.cleanup=function(e){s.util.log("Cleaning up PeerConnection to "+e.peer);var t=e.pc;t&&(t.readyState&&"closed"!==t.readyState||"closed"!==t.signalingState)&&(t.close(),e.pc=null)},o.Negotiator._makeOffer=function(t){var i=t.pc;i.createOffer(function(e){s.util.log("Created offer."),!s.util.supports.sctp&&"data"===t.type&&t.reliable&&(e.sdp=Reliable.higherBandwidthSDP(e.sdp)),i.setLocalDescription(e,function(){s.util.log("Set localDescription: offer","for:",t.peer),t.provider.socket.send({type:"OFFER",payload:{sdp:e,type:t.type,label:t.label,connectionId:t.id,reliable:t.reliable,serialization:t.serialization,metadata:t.metadata,browser:s.util.browser},dst:t.peer})},function(e){"OperationError: Failed to set local offer sdp: Called in wrong state: kHaveRemoteOffer"!=e&&(t.provider.emitError("webrtc",e),s.util.log("Failed to setLocalDescription, ",e))})},function(e){t.provider.emitError("webrtc",e),s.util.log("Failed to createOffer, ",e)},t.options.constraints)},o.Negotiator._makeAnswer=function(t){var i=t.pc;i.createAnswer(function(e){s.util.log("Created answer."),!s.util.supports.sctp&&"data"===t.type&&t.reliable&&(e.sdp=Reliable.higherBandwidthSDP(e.sdp)),i.setLocalDescription(e,function(){s.util.log("Set localDescription: answer","for:",t.peer),t.provider.socket.send({type:"ANSWER",payload:{sdp:e,type:t.type,connectionId:t.id,browser:s.util.browser},dst:t.peer})},function(e){t.provider.emitError("webrtc",e),s.util.log("Failed to setLocalDescription, ",e)})},function(e){t.provider.emitError("webrtc",e),s.util.log("Failed to create answer, ",e)})},o.Negotiator.handleSDP=function(e,t,i){i=new r.RTCSessionDescription(i);var n=t.pc;s.util.log("Setting remote description",i),n.setRemoteDescription(i,function(){s.util.log("Set remoteDescription:",e,"for:",t.peer),"OFFER"===e&&o.Negotiator._makeAnswer(t)},function(e){t.provider.emitError("webrtc",e),s.util.log("Failed to setRemoteDescription, ",e)})},o.Negotiator.handleCandidate=function(e,t){var i=t.candidate,n=t.sdpMLineIndex;e.pc.addIceCandidate(new r.RTCIceCandidate({sdpMLineIndex:n,candidate:i})),s.util.log("Added ICE candidate for:",e.peer)}},{"./adapter":1,"./util":8}],6:[function(e,t,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0});var p=e("./util"),n=e("eventemitter3"),r=e("./socket"),l=e("./mediaconnection"),h=e("./dataconnection");function o(e,t){if(!(this instanceof o))return new o(e,t);n.EventEmitter.call(this),e&&e.constructor==Object?(t=e,e=void 0):e&&(e=e.toString()),(t=p.util.extend({debug:0,host:p.util.CLOUD_HOST,port:p.util.CLOUD_PORT,path:"/",token:p.util.randomToken(),config:p.util.defaultConfig},t)).key="peerjs","/"===(this.options=t).host&&(t.host=window.location.hostname),"/"!==t.path[0]&&(t.path="/"+t.path),"/"!==t.path[t.path.length-1]&&(t.path+="/"),void 0===t.secure&&t.host!==p.util.CLOUD_HOST?t.secure=p.util.isSecure():t.host==p.util.CLOUD_HOST&&(t.secure=!0),t.logFunction&&p.util.setLogFunction(t.logFunction),p.util.setLogLevel(t.debug),p.util.supports.audioVideo||p.util.supports.data?p.util.validateId(e)?(this.destroyed=!1,this.disconnected=!1,this.open=!1,this.connections={},this._lostMessages={},this._initializeServerConnection(),e?this._initialize(e):this._retrieveId()):this._delayedAbort("invalid-id",'ID "'+e+'" is invalid'):this._delayedAbort("browser-incompatible","The current browser does not support WebRTC")}i.Peer=o,p.util.inherits(o,n.EventEmitter),o.prototype._initializeServerConnection=function(){var t=this;this.socket=new r.Socket(this.options.secure,this.options.host,this.options.port,this.options.path,this.options.key,this.options.wsport),this.socket.on("message",function(e){t._handleMessage(e)}),this.socket.on("error",function(e){t._abort("socket-error",e)}),this.socket.on("disconnected",function(){t.disconnected||(t.emitError("network","Lost connection to server."),t.disconnect())}),this.socket.on("close",function(){t.disconnected||t._abort("socket-closed","Underlying socket is already closed.")})},o.prototype._retrieveId=function(e){var i=this,t=new XMLHttpRequest,n=(this.options.secure?"https://":"http://")+this.options.host+":"+this.options.port+this.options.path+this.options.key+"/id";n+="?ts="+(new Date).getTime()+Math.random(),t.open("get",n,!0),t.onerror=function(e){p.util.error("Error retrieving ID",e);var t="";"/"===i.options.path&&i.options.host!==p.util.CLOUD_HOST&&(t=" If you passed in a `path` to your self-hosted PeerServer, you'll also need to pass in that same path when creating a new Peer."),i._abort("server-error","Could not get an ID from the server."+t)},t.onreadystatechange=function(){4===t.readyState&&(200===t.status?i._initialize(t.responseText):t.onerror())},t.send(null)},o.prototype._initialize=function(e){this.id=e,this.socket.start(this.id,this.options.token)},o.prototype._handleMessage=function(e){var t,i=e.type,n=e.payload,r=e.src;switch(i){case"OPEN":this.emit("open",this.id),this.open=!0;break;case"ERROR":this._abort("server-error",n.msg);break;case"ID-TAKEN":this._abort("unavailable-id","ID `"+this.id+"` is taken");break;case"INVALID-KEY":this._abort("invalid-key",'API KEY "'+this.options.key+'" is invalid');break;case"LEAVE":p.util.log("Received leave message from",r),this._cleanupPeer(r);break;case"EXPIRE":this.emitError("peer-unavailable","Could not connect to peer "+r);break;case"OFFER":var o=n.connectionId;if((t=this.getConnection(r,o))&&(t.close(),p.util.warn("Offer received for existing Connection ID:",o)),"media"===n.type)t=new l.MediaConnection(r,this,{connectionId:o,_payload:n,metadata:n.metadata}),this._addConnection(r,t),this.emit("call",t);else{if("data"!==n.type)return void p.util.warn("Received malformed connection type:",n.type);t=new h.DataConnection(r,this,{connectionId:o,_payload:n,metadata:n.metadata,label:n.label,serialization:n.serialization,reliable:n.reliable}),this._addConnection(r,t),this.emit("connection",t)}for(var s=this._getMessages(o),a=0,u=s.length;a<u;a+=1)t.handleMessage(s[a]);break;default:if(!n)return void p.util.warn("You received a malformed message from "+r+" of type "+i);var c=n.connectionId;(t=this.getConnection(r,c))&&t.pc?t.handleMessage(e):c?this._storeMessage(c,e):p.util.warn("You received an unrecognized message:",e)}},o.prototype._storeMessage=function(e,t){this._lostMessages[e]||(this._lostMessages[e]=[]),this._lostMessages[e].push(t)},o.prototype._getMessages=function(e){var t=this._lostMessages[e];return t?(delete this._lostMessages[e],t):[]},o.prototype.connect=function(e,t){if(this.disconnected)return p.util.warn("You cannot connect to a new Peer because you called .disconnect() on this Peer and ended your connection with the server. You can create a new Peer to reconnect, or call reconnect on this peer if you believe its ID to still be available."),void this.emitError("disconnected","Cannot connect to new Peer after disconnecting from server.");var i=new h.DataConnection(e,this,t);return this._addConnection(e,i),i},o.prototype.call=function(e,t,i){if(this.disconnected)return p.util.warn("You cannot connect to a new Peer because you called .disconnect() on this Peer and ended your connection with the server. You can create a new Peer to reconnect."),void this.emitError("disconnected","Cannot connect to new Peer after disconnecting from server.");if(t){(i=i||{})._stream=t;var n=new l.MediaConnection(e,this,i);return this._addConnection(e,n),n}p.util.error("To call a peer, you must provide a stream from your browser's `getUserMedia`.")},o.prototype._addConnection=function(e,t){this.connections[e]||(this.connections[e]=[]),this.connections[e].push(t)},o.prototype.getConnection=function(e,t){var i=this.connections[e];if(!i)return null;for(var n=0,r=i.length;n<r;n++)if(i[n].id===t)return i[n];return null},o.prototype._delayedAbort=function(e,t){var i=this;p.util.setZeroTimeout(function(){i._abort(e,t)})},o.prototype._abort=function(e,t){p.util.error("Aborting!"),this._lastServerId?this.disconnect():this.destroy(),this.emitError(e,t)},o.prototype.emitError=function(e,t){p.util.error("Error:",t),"string"==typeof t&&(t=new Error(t)),t.type=e,this.emit("error",t)},o.prototype.destroy=function(){this.destroyed||(this._cleanup(),this.disconnect(),this.destroyed=!0)},o.prototype._cleanup=function(){if(this.connections)for(var e=Object.keys(this.connections),t=0,i=e.length;t<i;t++)this._cleanupPeer(e[t]);this.emit("close")},o.prototype._cleanupPeer=function(e){for(var t=this.connections[e],i=0,n=t.length;i<n;i+=1)t[i].close()},o.prototype.disconnect=function(){var e=this;p.util.setZeroTimeout(function(){e.disconnected||(e.disconnected=!0,e.open=!1,e.socket&&e.socket.close(),e.emit("disconnected",e.id),e._lastServerId=e.id,e.id=null)})},o.prototype.reconnect=function(){if(this.disconnected&&!this.destroyed)p.util.log("Attempting reconnection to server with ID "+this._lastServerId),this.disconnected=!1,this._initializeServerConnection(),this._initialize(this._lastServerId);else{if(this.destroyed)throw new Error("This peer cannot reconnect to the server. It has already been destroyed.");if(this.disconnected||this.open)throw new Error("Peer "+this.id+" cannot reconnect because it is not disconnected from the server!");p.util.error("In a hurry? We're still trying to make the initial connection!")}},o.prototype.listAllPeers=function(t){t=t||function(){};var i=this,n=new XMLHttpRequest,e=(this.options.secure?"https://":"http://")+this.options.host+":"+this.options.port+this.options.path+this.options.key+"/peers";e+="?ts="+(new Date).getTime()+Math.random(),n.open("get",e,!0),n.onerror=function(e){i._abort("server-error","Could not get peers from the server."),t([])},n.onreadystatechange=function(){if(4===n.readyState){if(401===n.status){var e="";throw e=i.options.host!==p.util.CLOUD_HOST?"It looks like you're using the cloud server. You can email team@peerjs.com to enable peer listing for your API key.":"You need to enable `allow_discovery` on your self-hosted PeerServer to use this feature.",t([]),new Error("It doesn't look like you have permission to list peers IDs. "+e)}200!==n.status?t([]):t(JSON.parse(n.responseText))}},n.send(null)}},{"./dataconnection":2,"./mediaconnection":4,"./socket":7,"./util":8,eventemitter3:9}],7:[function(e,t,i){"use strict";Object.defineProperty(i,"__esModule",{value:!0});var o=e("./util"),u=e("eventemitter3");function c(e,t,i,n,r,o){if(!(this instanceof c))return new c(e,t,i,n,r,o);o=o||i,u.EventEmitter.call(this),this.disconnected=!1,this._queue=[];var s=e?"https://":"http://",a=e?"wss://":"ws://";this._httpUrl=s+t+":"+i+n+r,this._wsUrl=a+t+":"+o+n+"peerjs?key="+r}i.Socket=c,o.util.inherits(c,u.EventEmitter),c.prototype.start=function(e,t){this.id=e,this._httpUrl+="/"+e+"/"+t,this._wsUrl+="&id="+e+"&token="+t,this._startXhrStream(),this._startWebSocket()},c.prototype._startWebSocket=function(e){var i=this;this._socket||(this._socket=new WebSocket(this._wsUrl),this._socket.onmessage=function(t){try{var e=JSON.parse(t.data)}catch(e){return void o.util.log("Invalid server message",t.data)}i.emit("message",e)},this._socket.onclose=function(e){o.util.log("Socket closed."),i.disconnected=!0,i.emit("disconnected")},this._socket.onopen=function(){i._timeout&&(clearTimeout(i._timeout),setTimeout(function(){i._http.abort(),i._http=null},5e3)),i._sendQueuedMessages(),o.util.log("Socket open")})},c.prototype._startXhrStream=function(e){try{var t=this;this._http=new XMLHttpRequest,this._http._index=1,this._http._streamIndex=e||0,this._http.open("post",this._httpUrl+"/id?i="+this._http._streamIndex,!0),this._http.onerror=function(){clearTimeout(t._timeout),t.emit("disconnected")},this._http.onreadystatechange=function(){2==this.readyState&&this.old?(this.old.abort(),delete this.old):2<this.readyState&&200===this.status&&this.responseText&&t._handleStream(this)},this._http.send(null),this._setHTTPTimeout()}catch(e){o.util.log("XMLHttpRequest not available; defaulting to WebSockets")}},c.prototype._handleStream=function(t){var e=t.responseText.split("\n");if(t._buffer)for(;0<t._buffer.length;){var i=t._buffer.shift(),n=e[i];try{n=JSON.parse(n)}catch(e){t._buffer.shift(i);break}this.emit("message",n)}var r=e[t._index];if(r)if(t._index+=1,t._index===e.length)t._buffer||(t._buffer=[]),t._buffer.push(t._index-1);else{try{r=JSON.parse(r)}catch(e){return void o.util.log("Invalid server message",r)}this.emit("message",r)}},c.prototype._setHTTPTimeout=function(){var t=this;this._timeout=setTimeout(function(){var e=t._http;t._wsOpen()?e.abort():(t._startXhrStream(e._streamIndex+1),t._http.old=e)},25e3)},c.prototype._wsOpen=function(){return this._socket&&1==this._socket.readyState},c.prototype._sendQueuedMessages=function(){for(var e=0,t=this._queue.length;e<t;e+=1)this.send(this._queue[e])},c.prototype.send=function(e){if(!this.disconnected)if(this.id)if(e.type){var t=JSON.stringify(e);if(this._wsOpen())this._socket.send(t);else{var i=new XMLHttpRequest,n=this._httpUrl+"/"+e.type.toLowerCase();i.open("post",n,!0),i.setRequestHeader("Content-Type","application/json"),i.send(t)}}else this.emit("error","Invalid message");else this._queue.push(e)},c.prototype.close=function(){!this.disconnected&&this._wsOpen()&&(this._socket.close(),this.disconnected=!0)}},{"./util":8,eventemitter3:9}],8:[function(e,t,c){"use strict";var i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(c,"__esModule",{value:!0});var u={iceServers:[{urls:"stun:stun.l.google.com:19302"}]},p=1,n=i(e("js-binarypack")),l=e("./adapter");c.util={noop:function(){},CLOUD_HOST:"0.peerjs.com",CLOUD_PORT:443,chunkedBrowsers:{Chrome:1},chunkedMTU:16300,logLevel:0,setLogLevel:function(e){var t=parseInt(e,10);isNaN(parseInt(e,10))?c.util.logLevel=e?3:0:c.util.logLevel=t,c.util.log=c.util.warn=c.util.error=c.util.noop,0<c.util.logLevel&&(c.util.error=c.util._printWith("ERROR")),1<c.util.logLevel&&(c.util.warn=c.util._printWith("WARNING")),2<c.util.logLevel&&(c.util.log=c.util._print)},setLogFunction:function(e){e.constructor!==Function?c.util.warn("The log function you passed in is not a function. Defaulting to regular logs."):c.util._print=e},_printWith:function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t),c.util._print.apply(c.util,e)}},_print:function(){var e=!1,t=Array.prototype.slice.call(arguments);t.unshift("PeerJS: ");for(var i=0,n=t.length;i<n;i++)t[i]instanceof Error&&(t[i]="("+t[i].name+") "+t[i].message,e=!0);e?console.error.apply(console,t):console.log.apply(console,t)},defaultConfig:u,browser:window.mozRTCPeerConnection?"Firefox":window.webkitRTCPeerConnection?"Chrome":window.RTCPeerConnection?"Supported":"Unsupported",supports:function(){if(void 0===l.RTCPeerConnection)return{};var e,t,i=!0,n=!0,r=!1,o=!1,s=!!window.webkitRTCPeerConnection;try{e=new l.RTCPeerConnection(u,{optional:[{RtpDataChannels:!0}]})}catch(e){n=i=!1}if(i)try{t=e.createDataChannel("_PEERJSTEST")}catch(e){i=!1}if(i){try{t.binaryType="blob",r=!0}catch(e){}var a=new l.RTCPeerConnection(u,{});try{o=a.createDataChannel("_PEERJSRELIABLETEST",{}).reliable}catch(e){}a.close()}return n&&(n=!!e.addStream),e&&e.close(),{audioVideo:n,data:i,binaryBlob:r,binary:o,reliable:o,sctp:o,onnegotiationneeded:s}}(),validateId:function(e){return!e||/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(e)},validateKey:function(e){return!e||/^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/.exec(e)},debug:!1,inherits:function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},extend:function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e},pack:n.default.pack,unpack:n.default.unpack,log:function(){if(c.util.debug){var e=!1,t=Array.prototype.slice.call(arguments);t.unshift("PeerJS: ");for(var i=0,n=t.length;i<n;i++)t[i]instanceof Error&&(t[i]="("+t[i].name+") "+t[i].message,e=!0);e?console.error.apply(console,t):console.log.apply(console,t)}},setZeroTimeout:function(t){var i=[],n="zero-timeout-message";function e(e){e.source==t&&e.data==n&&(e.stopPropagation&&e.stopPropagation(),i.length&&i.shift()())}return t.addEventListener?t.addEventListener("message",e,!0):t.attachEvent&&t.attachEvent("onmessage",e),function(e){i.push(e),t.postMessage(n,"*")}}(window),chunk:function(e){for(var t,i=[],n=e.size,r=t=0,o=Math.ceil(n/c.util.chunkedMTU);r<n;){var s=Math.min(n,r+c.util.chunkedMTU),a=e.slice(r,s),u={__peerData:p,n:t,data:a,total:o};i.push(u),r=s,t+=1}return p+=1,i},blobToArrayBuffer:function(e,t){var i=new FileReader;i.onload=function(e){t(e.target.result)},i.readAsArrayBuffer(e)},blobToBinaryString:function(e,t){var i=new FileReader;i.onload=function(e){t(e.target.result)},i.readAsBinaryString(e)},binaryStringToArrayBuffer:function(e){for(var t=new Uint8Array(e.length),i=0;i<e.length;i++)t[i]=255&e.charCodeAt(i);return t.buffer},randomToken:function(){return Math.random().toString(36).substr(2)},isSecure:function(){return"https:"===location.protocol}}},{"./adapter":1,"js-binarypack":10}],9:[function(e,t,i){"use strict";function r(e,t,i){this.fn=e,this.context=t,this.once=i||!1}function n(){}n.prototype._events=void 0,n.prototype.listeners=function(e){if(!this._events||!this._events[e])return[];if(this._events[e].fn)return[this._events[e].fn];for(var t=0,i=this._events[e].length,n=new Array(i);t<i;t++)n[t]=this._events[e][t].fn;return n},n.prototype.emit=function(e,t,i,n,r,o){if(!this._events||!this._events[e])return!1;var s,a,u=this._events[e],c=arguments.length;if("function"==typeof u.fn){switch(u.once&&this.removeListener(e,u.fn,!0),c){case 1:return u.fn.call(u.context),!0;case 2:return u.fn.call(u.context,t),!0;case 3:return u.fn.call(u.context,t,i),!0;case 4:return u.fn.call(u.context,t,i,n),!0;case 5:return u.fn.call(u.context,t,i,n,r),!0;case 6:return u.fn.call(u.context,t,i,n,r,o),!0}for(a=1,s=new Array(c-1);a<c;a++)s[a-1]=arguments[a];u.fn.apply(u.context,s)}else{var p,l=u.length;for(a=0;a<l;a++)switch(u[a].once&&this.removeListener(e,u[a].fn,!0),c){case 1:u[a].fn.call(u[a].context);break;case 2:u[a].fn.call(u[a].context,t);break;case 3:u[a].fn.call(u[a].context,t,i);break;default:if(!s)for(p=1,s=new Array(c-1);p<c;p++)s[p-1]=arguments[p];u[a].fn.apply(u[a].context,s)}}return!0},n.prototype.on=function(e,t,i){var n=new r(t,i||this);return this._events||(this._events={}),this._events[e]?this._events[e].fn?this._events[e]=[this._events[e],n]:this._events[e].push(n):this._events[e]=n,this},n.prototype.once=function(e,t,i){var n=new r(t,i||this,!0);return this._events||(this._events={}),this._events[e]?this._events[e].fn?this._events[e]=[this._events[e],n]:this._events[e].push(n):this._events[e]=n,this},n.prototype.removeListener=function(e,t,i){if(!this._events||!this._events[e])return this;var n=this._events[e],r=[];if(t&&(n.fn&&(n.fn!==t||i&&!n.once)&&r.push(n),!n.fn))for(var o=0,s=n.length;o<s;o++)(n[o].fn!==t||i&&!n[o].once)&&r.push(n[o]);return r.length?this._events[e]=1===r.length?r[0]:r:delete this._events[e],this},n.prototype.removeAllListeners=function(e){return this._events&&(e?delete this._events[e]:this._events={}),this},n.prototype.off=n.prototype.removeListener,n.prototype.addListener=n.prototype.on,n.prototype.setMaxListeners=function(){return this},((n.EventEmitter=n).EventEmitter2=n).EventEmitter3=n,t.exports=n},{}],10:[function(e,t,i){var n=e("./bufferbuilder").BufferBuilder,r=e("./bufferbuilder").binaryFeatures,o={unpack:function(e){return new s(e).unpack()},pack:function(e){var t=new a;return t.pack(e),t.getBuffer()}};function s(e){this.index=0,this.dataBuffer=e,this.dataView=new Uint8Array(this.dataBuffer),this.length=this.dataBuffer.byteLength}function a(){this.bufferBuilder=new n}function u(e){var t=e.charCodeAt(0);return t<=2047?"00":t<=65535?"000":t<=2097151?"0000":t<=67108863?"00000":"000000"}t.exports=o,s.prototype.unpack=function(){var e,t=this.unpack_uint8();if(t<128)return t;if((224^t)<32)return(224^t)-32;if((e=160^t)<=15)return this.unpack_raw(e);if((e=176^t)<=15)return this.unpack_string(e);if((e=144^t)<=15)return this.unpack_array(e);if((e=128^t)<=15)return this.unpack_map(e);switch(t){case 192:return null;case 193:return;case 194:return!1;case 195:return!0;case 202:return this.unpack_float();case 203:return this.unpack_double();case 204:return this.unpack_uint8();case 205:return this.unpack_uint16();case 206:return this.unpack_uint32();case 207:return this.unpack_uint64();case 208:return this.unpack_int8();case 209:return this.unpack_int16();case 210:return this.unpack_int32();case 211:return this.unpack_int64();case 212:case 213:case 214:case 215:return;case 216:return e=this.unpack_uint16(),this.unpack_string(e);case 217:return e=this.unpack_uint32(),this.unpack_string(e);case 218:return e=this.unpack_uint16(),this.unpack_raw(e);case 219:return e=this.unpack_uint32(),this.unpack_raw(e);case 220:return e=this.unpack_uint16(),this.unpack_array(e);case 221:return e=this.unpack_uint32(),this.unpack_array(e);case 222:return e=this.unpack_uint16(),this.unpack_map(e);case 223:return e=this.unpack_uint32(),this.unpack_map(e)}},s.prototype.unpack_uint8=function(){var e=255&this.dataView[this.index];return this.index++,e},s.prototype.unpack_uint16=function(){var e=this.read(2),t=256*(255&e[0])+(255&e[1]);return this.index+=2,t},s.prototype.unpack_uint32=function(){var e=this.read(4),t=256*(256*(256*e[0]+e[1])+e[2])+e[3];return this.index+=4,t},s.prototype.unpack_uint64=function(){var e=this.read(8),t=256*(256*(256*(256*(256*(256*(256*e[0]+e[1])+e[2])+e[3])+e[4])+e[5])+e[6])+e[7];return this.index+=8,t},s.prototype.unpack_int8=function(){var e=this.unpack_uint8();return e<128?e:e-256},s.prototype.unpack_int16=function(){var e=this.unpack_uint16();return e<32768?e:e-65536},s.prototype.unpack_int32=function(){var e=this.unpack_uint32();return e<Math.pow(2,31)?e:e-Math.pow(2,32)},s.prototype.unpack_int64=function(){var e=this.unpack_uint64();return e<Math.pow(2,63)?e:e-Math.pow(2,64)},s.prototype.unpack_raw=function(e){if(this.length<this.index+e)throw new Error("BinaryPackFailure: index is out of range "+this.index+" "+e+" "+this.length);var t=this.dataBuffer.slice(this.index,this.index+e);return this.index+=e,t},s.prototype.unpack_string=function(e){for(var t,i,n=this.read(e),r=0,o="";r<e;)(t=n[r])<128?(o+=String.fromCharCode(t),r++):(192^t)<32?(i=(192^t)<<6|63&n[r+1],o+=String.fromCharCode(i),r+=2):(i=(15&t)<<12|(63&n[r+1])<<6|63&n[r+2],o+=String.fromCharCode(i),r+=3);return this.index+=e,o},s.prototype.unpack_array=function(e){for(var t=new Array(e),i=0;i<e;i++)t[i]=this.unpack();return t},s.prototype.unpack_map=function(e){for(var t={},i=0;i<e;i++){var n=this.unpack(),r=this.unpack();t[n]=r}return t},s.prototype.unpack_float=function(){var e=this.unpack_uint32(),t=(e>>23&255)-127;return(0==e>>31?1:-1)*(8388607&e|8388608)*Math.pow(2,t-23)},s.prototype.unpack_double=function(){var e=this.unpack_uint32(),t=this.unpack_uint32(),i=(e>>20&2047)-1023;return(0==e>>31?1:-1)*((1048575&e|1048576)*Math.pow(2,i-20)+t*Math.pow(2,i-52))},s.prototype.read=function(e){var t=this.index;if(t+e<=this.length)return this.dataView.subarray(t,t+e);throw new Error("BinaryPackFailure: read index out of range")},a.prototype.getBuffer=function(){return this.bufferBuilder.getBuffer()},a.prototype.pack=function(e){var t=typeof e;if("string"==t)this.pack_string(e);else if("number"==t)Math.floor(e)===e?this.pack_integer(e):this.pack_double(e);else if("boolean"==t)!0===e?this.bufferBuilder.append(195):!1===e&&this.bufferBuilder.append(194);else if("undefined"==t)this.bufferBuilder.append(192);else{if("object"!=t)throw new Error('Type "'+t+'" not yet supported');if(null===e)this.bufferBuilder.append(192);else{var i=e.constructor;if(i==Array)this.pack_array(e);else if(i==Blob||i==File)this.pack_bin(e);else if(i==ArrayBuffer)r.useArrayBufferView?this.pack_bin(new Uint8Array(e)):this.pack_bin(e);else if("BYTES_PER_ELEMENT"in e)r.useArrayBufferView?this.pack_bin(new Uint8Array(e.buffer)):this.pack_bin(e.buffer);else if(i==Object)this.pack_object(e);else if(i==Date)this.pack_string(e.toString());else{if("function"!=typeof e.toBinaryPack)throw new Error('Type "'+i.toString()+'" not yet supported');this.bufferBuilder.append(e.toBinaryPack())}}}this.bufferBuilder.flush()},a.prototype.pack_bin=function(e){var t=e.length||e.byteLength||e.size;if(t<=15)this.pack_uint8(160+t);else if(t<=65535)this.bufferBuilder.append(218),this.pack_uint16(t);else{if(!(t<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(219),this.pack_uint32(t)}this.bufferBuilder.append(e)},a.prototype.pack_string=function(e){var t,i=600<(t=e).length?new Blob([t]).size:t.replace(/[^\u0000-\u007F]/g,u).length;if(i<=15)this.pack_uint8(176+i);else if(i<=65535)this.bufferBuilder.append(216),this.pack_uint16(i);else{if(!(i<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(217),this.pack_uint32(i)}this.bufferBuilder.append(e)},a.prototype.pack_array=function(e){var t=e.length;if(t<=15)this.pack_uint8(144+t);else if(t<=65535)this.bufferBuilder.append(220),this.pack_uint16(t);else{if(!(t<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(221),this.pack_uint32(t)}for(var i=0;i<t;i++)this.pack(e[i])},a.prototype.pack_integer=function(e){if(-32<=e&&e<=127)this.bufferBuilder.append(255&e);else if(0<=e&&e<=255)this.bufferBuilder.append(204),this.pack_uint8(e);else if(-128<=e&&e<=127)this.bufferBuilder.append(208),this.pack_int8(e);else if(0<=e&&e<=65535)this.bufferBuilder.append(205),this.pack_uint16(e);else if(-32768<=e&&e<=32767)this.bufferBuilder.append(209),this.pack_int16(e);else if(0<=e&&e<=4294967295)this.bufferBuilder.append(206),this.pack_uint32(e);else if(-2147483648<=e&&e<=2147483647)this.bufferBuilder.append(210),this.pack_int32(e);else if(-0x8000000000000000<=e&&e<=0x8000000000000000)this.bufferBuilder.append(211),this.pack_int64(e);else{if(!(0<=e&&e<=0x10000000000000000))throw new Error("Invalid integer");this.bufferBuilder.append(207),this.pack_uint64(e)}},a.prototype.pack_double=function(e){var t=0;e<0&&(t=1,e=-e);var i=Math.floor(Math.log(e)/Math.LN2),n=e/Math.pow(2,i)-1,r=Math.floor(n*Math.pow(2,52)),o=Math.pow(2,32),s=t<<31|i+1023<<20|r/o&1048575,a=r%o;this.bufferBuilder.append(203),this.pack_int32(s),this.pack_int32(a)},a.prototype.pack_object=function(e){var t=Object.keys(e).length;if(t<=15)this.pack_uint8(128+t);else if(t<=65535)this.bufferBuilder.append(222),this.pack_uint16(t);else{if(!(t<=4294967295))throw new Error("Invalid length");this.bufferBuilder.append(223),this.pack_uint32(t)}for(var i in e)e.hasOwnProperty(i)&&(this.pack(i),this.pack(e[i]))},a.prototype.pack_uint8=function(e){this.bufferBuilder.append(e)},a.prototype.pack_uint16=function(e){this.bufferBuilder.append(e>>8),this.bufferBuilder.append(255&e)},a.prototype.pack_uint32=function(e){var t=4294967295&e;this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t)},a.prototype.pack_uint64=function(e){var t=e/Math.pow(2,32),i=e%Math.pow(2,32);this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t),this.bufferBuilder.append((4278190080&i)>>>24),this.bufferBuilder.append((16711680&i)>>>16),this.bufferBuilder.append((65280&i)>>>8),this.bufferBuilder.append(255&i)},a.prototype.pack_int8=function(e){this.bufferBuilder.append(255&e)},a.prototype.pack_int16=function(e){this.bufferBuilder.append((65280&e)>>8),this.bufferBuilder.append(255&e)},a.prototype.pack_int32=function(e){this.bufferBuilder.append(e>>>24&255),this.bufferBuilder.append((16711680&e)>>>16),this.bufferBuilder.append((65280&e)>>>8),this.bufferBuilder.append(255&e)},a.prototype.pack_int64=function(e){var t=Math.floor(e/Math.pow(2,32)),i=e%Math.pow(2,32);this.bufferBuilder.append((4278190080&t)>>>24),this.bufferBuilder.append((16711680&t)>>>16),this.bufferBuilder.append((65280&t)>>>8),this.bufferBuilder.append(255&t),this.bufferBuilder.append((4278190080&i)>>>24),this.bufferBuilder.append((16711680&i)>>>16),this.bufferBuilder.append((65280&i)>>>8),this.bufferBuilder.append(255&i)}},{"./bufferbuilder":11}],11:[function(e,t,i){var n={};n.useBlobBuilder=function(){try{return new Blob([]),!1}catch(e){return!0}}(),n.useArrayBufferView=!n.useBlobBuilder&&function(){try{return 0===new Blob([new Uint8Array([])]).size}catch(e){return!0}}(),t.exports.binaryFeatures=n;var r=t.exports.BlobBuilder;function o(){this._pieces=[],this._parts=[]}"undefined"!=typeof window&&(r=t.exports.BlobBuilder=window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder||window.BlobBuilder),o.prototype.append=function(e){"number"==typeof e?this._pieces.push(e):(this.flush(),this._parts.push(e))},o.prototype.flush=function(){if(0<this._pieces.length){var e=new Uint8Array(this._pieces);n.useArrayBufferView||(e=e.buffer),this._parts.push(e),this._pieces=[]}},o.prototype.getBuffer=function(){if(this.flush(),n.useBlobBuilder){for(var e=new r,t=0,i=this._parts.length;t<i;t++)e.append(this._parts[t]);return e.getBlob()}return new Blob(this._parts)},t.exports.BufferBuilder=o},{}],12:[function(e,t,i){var c=e("./util");function n(e,t){if(!(this instanceof n))return new n(e);this._dc=e,c.debug=t,this._outgoing={},this._incoming={},this._received={},this._window=1e3,this._mtu=500,this._interval=0,this._count=0,this._queue=[],this._setupDC()}n.prototype.send=function(e){var t=c.pack(e);t.size<this._mtu?this._handleSend(["no",t]):(this._outgoing[this._count]={ack:0,chunks:this._chunk(t)},c.debug&&(this._outgoing[this._count].timer=new Date),this._sendWindowedChunks(this._count),this._count+=1)},n.prototype._setupInterval=function(){var n=this;this._timeout=setInterval(function(){var e=n._queue.shift();if(e._multiple)for(var t=0,i=e.length;t<i;t+=1)n._intervalSend(e[t]);else n._intervalSend(e)},this._interval)},n.prototype._intervalSend=function(e){var t=this;e=c.pack(e),c.blobToBinaryString(e,function(e){t._dc.send(e)}),0===t._queue.length&&(clearTimeout(t._timeout),t._timeout=null)},n.prototype._processAcks=function(){for(var e in this._outgoing)this._outgoing.hasOwnProperty(e)&&this._sendWindowedChunks(e)},n.prototype._handleSend=function(e){for(var t=!0,i=0,n=this._queue.length;i<n;i+=1){var r=this._queue[i];r===e?t=!1:r._multiple&&-1!==r.indexOf(e)&&(t=!1)}t&&(this._queue.push(e),this._timeout||this._setupInterval())},n.prototype._setupDC=function(){var n=this;this._dc.onmessage=function(e){var t=e.data;if(t.constructor===String){var i=c.binaryStringToArrayBuffer(t);t=c.unpack(i),n._handleMessage(t)}}},n.prototype._handleMessage=function(e){var t,i=e[1],n=this._incoming[i],r=this._outgoing[i];switch(e[0]){case"no":var o=i;o&&this.onmessage(c.unpack(o));break;case"end":if(t=n,this._received[i]=e[2],!t)break;this._ack(i);break;case"ack":if(t=r){var s=e[2];t.ack=Math.max(s,t.ack),t.ack>=t.chunks.length?(c.log("Time: ",new Date-t.timer),delete this._outgoing[i]):this._processAcks()}break;case"chunk":if(!(t=n)){if(!0===this._received[i])break;t={ack:["ack",i,0],chunks:[]},this._incoming[i]=t}var a=e[2],u=e[3];t.chunks[a]=new Uint8Array(u),a===t.ack[2]&&this._calculateNextAck(i),this._ack(i);break;default:this._handleSend(e)}},n.prototype._chunk=function(e){for(var t=[],i=e.size,n=0;n<i;){var r=Math.min(i,n+this._mtu),o={payload:e.slice(n,r)};t.push(o),n=r}return c.log("Created",t.length,"chunks."),t},n.prototype._ack=function(e){var t=this._incoming[e].ack;this._received[e]===t[2]&&(this._complete(e),this._received[e]=!0),this._handleSend(t)},n.prototype._calculateNextAck=function(e){for(var t=this._incoming[e],i=t.chunks,n=0,r=i.length;n<r;n+=1)if(void 0===i[n])return void(t.ack[2]=n);t.ack[2]=i.length},n.prototype._sendWindowedChunks=function(e){c.log("sendWindowedChunks for: ",e);for(var t=this._outgoing[e],i=t.chunks,n=[],r=Math.min(t.ack+this._window,i.length),o=t.ack;o<r;o+=1)i[o].sent&&o!==t.ack||(i[o].sent=!0,n.push(["chunk",e,o,i[o].payload]));t.ack+this._window>=i.length&&n.push(["end",e,i.length]),n._multiple=!0,this._handleSend(n)},n.prototype._complete=function(e){c.log("Completed called for",e);var t=this,i=this._incoming[e].chunks,n=new Blob(i);c.blobToArrayBuffer(n,function(e){t.onmessage(c.unpack(e))}),delete this._incoming[e]},n.higherBandwidthSDP=function(e){var t=navigator.appVersion.match(/Chrome\/(.*?) /);if(t&&(t=parseInt(t[1].split(".").shift()))<31){var i=e.split("b=AS:30");if(1<i.length)return i[0]+"b=AS:102400"+i[1]}return e},n.prototype.onmessage=function(e){},t.exports.Reliable=n},{"./util":13}],13:[function(e,t,i){var n=e("js-binarypack"),r={debug:!1,inherits:function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},extend:function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e},pack:n.pack,unpack:n.unpack,log:function(){if(r.debug){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];e.unshift("Reliable: "),console.log.apply(console,e)}},setZeroTimeout:function(t){var i=[],n="zero-timeout-message";function e(e){e.source==t&&e.data==n&&(e.stopPropagation&&e.stopPropagation(),i.length&&i.shift()())}return t.addEventListener?t.addEventListener("message",e,!0):t.attachEvent&&t.attachEvent("onmessage",e),function(e){i.push(e),t.postMessage(n,"*")}}(this),blobToArrayBuffer:function(e,t){var i=new FileReader;i.onload=function(e){t(e.target.result)},i.readAsArrayBuffer(e)},blobToBinaryString:function(e,t){var i=new FileReader;i.onload=function(e){t(e.target.result)},i.readAsBinaryString(e)},binaryStringToArrayBuffer:function(e){for(var t=new Uint8Array(e.length),i=0;i<e.length;i++)t[i]=255&e.charCodeAt(i);return t.buffer},randomToken:function(){return Math.random().toString(36).substr(2)}};t.exports=r},{"js-binarypack":10}]},{},[3]); \ No newline at end of file diff --git a/lib/spectrum.js b/lib/spectrum.js index adb2694c..ca1ed4e9 100644 --- a/lib/spectrum.js +++ b/lib/spectrum.js @@ -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; diff --git a/main.js b/main.js index 23c169b9..65c4fbe1 100644 --- a/main.js +++ b/main.js @@ -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') diff --git a/package.json b/package.json index 26ce43f2..4857beea 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Blockbench", "description": "Minecraft Block Model Editor", - "version": "2.5.1", + "version": "2.6.0", "license": "MIT", "author": { "name": "JannisX11",