This commit is contained in:
JannisX11 2019-03-09 22:06:35 +01:00
parent 293238948a
commit 6ffc628ccc
40 changed files with 3493 additions and 2653 deletions

View File

@ -11,7 +11,7 @@ matrix:
- name: "Windows"
os: osx
script: build -w --x64 --publish=always && build -w --ia32 --publish=always
script: build -w --x64 --publish=always
- name: "Linux"
os: linux

View File

@ -79,78 +79,81 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-mirror_x:before {
content: "\e915";
}
.icon-mirror_y:before {
content: "\e916";
}
.icon-mirror_z:before {
content: "\e917";
}
.icon-saved:before {
content: "\e913";
}
.icon-player:before {
content: "\e914";
}
.icon-vertexsnap:before {
.icon-blockbench_file:before {
content: "\e900";
}
.icon-optifine_file:before {
content: "\e912";
}
.icon-objects:before {
content: "\e902";
}
.icon-create_bitmap:before {
.icon-vertexsnap:before {
content: "\e901";
}
.icon-bow:before {
.icon-create_bitmap:before {
content: "\e902";
}
.icon-objects:before {
content: "\e903";
}
.icon-bb_interface:before {
.icon-bow:before {
content: "\e904";
}
.icon-blockbench_inverted:before {
content: "\e911";
}
.icon-blockbench:before {
.icon-bb_interface:before {
content: "\e905";
}
.icon-x11:before {
.icon-blockbench:before {
content: "\e906";
}
.icon-baby_zombie:before {
.icon-x11:before {
content: "\e907";
}
.icon-armor_stand:before {
.icon-baby_zombie:before {
content: "\e908";
}
.icon-armor_stand_small:before {
.icon-armor_stand:before {
content: "\e909";
}
.icon-ground:before {
.icon-armor_stand_small:before {
content: "\e90a";
}
.icon-hud:before {
.icon-ground:before {
content: "\e90b";
}
.icon-inventory_full:before {
.icon-hud:before {
content: "\e90c";
}
.icon-inventory_nine:before {
.icon-inventory_full:before {
content: "\e90d";
}
.icon-inventory_single:before {
.icon-inventory_nine:before {
content: "\e90e";
}
.icon-player_head:before {
.icon-inventory_single:before {
content: "\e90f";
}
.icon-zombie:before {
.icon-player_head:before {
content: "\e910";
}
.icon-zombie:before {
content: "\e911";
}
.icon-blockbench_inverted:before {
content: "\e912";
}
.icon-optifine_file:before {
content: "\e913";
}
.icon-saved:before {
content: "\e914";
}
.icon-player:before {
content: "\e915";
}
.icon-mirror_x:before {
content: "\e916";
}
.icon-mirror_y:before {
content: "\e917";
}
.icon-mirror_z:before {
content: "\e918";
}
@ -177,6 +180,7 @@
height: 30px;
width: 24px;
text-align: center;
vertical-align: text-top;
}
.dialog .message_box_icon {
font-size: 40pt;
@ -787,7 +791,7 @@
}
.half {
width: calc(50% - 2px);
width: calc(50% - 4px);
}
.tooltip {
@ -928,14 +932,46 @@
.outliner_object:hover {
color: var(--color-light);
}
.drag_hover {
border: 2px solid var(--color-accent);
#cubes_list.drag_hover > .vue-tree {
position: relative;
}
.drag_hover.drag_hover_insert_before {
border-top: 2px solid var(--color-accent);
border-bottom: none;
border-right: none;
border-left: none;
#cubes_list.drag_hover > .vue-tree > ul::before {
content: '';
width: calc(100% - 12px);
height: 2px;
margin-left: 6px;
background: var(--color-accent);
z-index: 3;
display: block;
position: absolute;
bottom: 0px;
}
.drag_hover[order]::before {
content: '';
width: calc(100% - 12px);
height: 2px;
margin-left: 6px;
background: var(--color-accent);
z-index: 3;
display: block;
position: absolute;
}
.drag_hover[order] {
position: relative;
}
.drag_hover[order="-1"]::before {
margin-top: -1px;
}
.drag_hover[order="1"]::before {
bottom: 0px;
}
.drag_hover[order="0"]::before {
width: 5px;
height: 30px;
margin-left: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
/*Cancel Dragover for main list*/
#cubes_list > div > ul > li.outliner_node.parent_li {
@ -957,7 +993,8 @@
float: right;
}
body > .outliner_object {
width: 260px;
width: 180px;
padding-left: 0 !important;
box-shadow: 0 0 4px black;
}
body > .outliner_object a {
@ -992,7 +1029,7 @@
font-weight: normal;
position: absolute;
top: -20px;
left: 40px;
left: 20px;
border: 1px solid var(--color-border);
}
@ -1143,7 +1180,8 @@
white-space: nowrap;
position: relative;
vertical-align: middle;
padding: 8px;
padding-left: 8px;
padding-right: 8px;
border: 1px solid transparent;
box-sizing: border-box;
}
@ -1155,7 +1193,8 @@
border: 1px solid var(--color-border);
}
.texture > i {
margin-top: 4px;
margin-top: 12px;
float: right;
}
.texture > i.clickable:hover {
color: var(--color-light);
@ -1169,7 +1208,6 @@
div.texture_icon_wrapper {
height: 48px;
width: 48px;
margin-top: -8px;
overflow: hidden;
position: relative;
}
@ -1185,16 +1223,8 @@
width: 48px;
height: 48px;
}
.texture_id {
display: none !important;
}
.texture_remove {
width: 24px;
}
.texture_name {
margin-top: 3px;
margin-top: 2px;
margin-left: 6px;
margin-right: 4px;
width: calc(100% - 82px);
@ -1202,7 +1232,18 @@
font-size: 0.94em;
cursor: default;
}
.texture.particle .texture_name {
.texture_res {
margin-top: -3px;
margin-left: 6px;
margin-right: 4px;
width: calc(100% - 82px);
height: 20px;
overflow: hidden;
font-size: 0.86em;
opacity: 0.6;
cursor: default;
}
.texture.particle .texture_name, .texture.particle .texture_res {
width: calc(100% - 106px);
}
.texture_error {
@ -1260,12 +1301,12 @@
}
body.animation_mode #timeline {
display: block;
height: 130px;
background-color: var(--color-dark);
height: 162px;
background-color: var(--color-back);
border-top: 1px solid var(--color-border);
}
body.animation_mode #preview .single_canvas_wrapper {
height: calc(100% - 130px);
height: calc(100% - 162px);
}
#timeline_inner {
overflow-y: hidden;
@ -1482,6 +1523,7 @@
bottom: -3px;
font-size: 0.8em;
cursor: default;
pointer-events: none;
}
#uv .bar.next_to_title {
margin-top: -32px;
@ -1782,6 +1824,30 @@
border: none;
}
/*Keybind recording*/
#overlay_message_box {
height: 100%;
width: 100%;
position: absolute;
z-index: 130;
text-align: center;
background-color: rgba(0, 0, 0, 0.8);
}
#overlay_message_box > div {
margin-top: 64px;
width: 400px;
margin-left: auto;
margin-right: auto;
}
#overlay_message_box > div > p {
margin-bottom: 20px;
}
#overlay_message_box h3 i {
vertical-align: sub;
padding: 8px;
font-size: 1.2em;
}
/*Display*/
.mode_tab {
display: block;
@ -1992,14 +2058,6 @@
cursor: default;
pointer-events: none;
}
#overlay_message_box {
height: 100%;
width: 100%;
position: absolute;
z-index: 130;
text-align: center;
background-color: rgba(0, 0, 0, 0.6);
}
.uv_message_box {
position: absolute;
margin-left: auto;

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Binary file not shown.

View File

@ -19,7 +19,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '2.4.0';
const appVersion = '2.5.0';
</script>
<script src="lib/vue.min.js"></script>
<script src="lib/vue_sortable.js"></script>
@ -28,12 +28,12 @@
<script src="lib/jquery-ui.min.js"></script>
<script src="lib/targa.js"></script>
<script src="lib/jimp.min.js"></script>
<script src="lib/jszip.min.js"></script>
<script src="lib/gif.js"></script>
<script src="lib/spectrum.js"></script>
<script src="lib/three.js"></script>
<script src="lib/three_custom.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/TransformControls.js"></script>
<script src="js/OBJExporter.js"></script>
<script src="js/language.js"></script>
@ -54,8 +54,9 @@
<script src="js/api.js"></script>
<script src="js/io.js"></script>
<script src="js/elements.js"></script>
<script src="js/element.js"></script>
<script src="js/preview.js"></script>
<script src="js/TransformControls.js"></script>
<script src="js/transform.js"></script>
<script src="js/textures.js"></script>
<script src="js/uv.js"></script>
@ -72,6 +73,16 @@
<div id="blackout" onclick="$('.dialog#'+open_dialog).find('.cancel_btn:not([disabled])').click()"></div>
<div id="overlay_message_box" style="display: none;">
<div>
<h3><i class="material-icons">keyboard</i><span class="tl">keybindings.recording</span></h3>
<p class="tl">keybindings.press</p>
<button class="large tl" onclick="Keybinds.recording.stopRecording()">dialog.cancel</button>
<button class="large tl" onclick="Keybinds.recording.clear().stopRecording()">keybindings.clear</button>
<div id="keybind_input_box" contenteditable="true" style="font-size: 0"></div>
</div>
</div>
<div class="dialog draggable" id="welcome_screen">
<div id="welcome_content"></div>
<button type="button" class="large cancel_btn hidden tl" onclick="hideDialog()">dialog.cancel</button>
@ -180,7 +191,7 @@
<i class="material-icons" id="plugin_search_bar_icon">search</i>
</div>
<ul id="pe_list" class="list">
<li v-for="item in searched" v-bind:class="{ selected: item.selected }" v-on:click="selectE(item, $event)" ondblclick="loadPEModel()">
<li v-for="item in searched" v-bind:class="{ selected: item.selected }" v-on:click="selectE(item, $event)" ondblclick="loadEntityModel()">
<img class="pe_icon" v-if="item.icon" v-bind:src="item.icon">
<div class="pe_icon" v-else></div>
<h4>{{ item.title }} <span>({{ item.name }})</span></h4>
@ -189,7 +200,7 @@
</ul>
<div class="dialog_bar">
<button type="button" class="large tl confirm_btn" onclick="loadPEModel()">dialog.import</button>
<button type="button" class="large tl confirm_btn" onclick="loadEntityModel()">dialog.import</button>
<button type="button" class="large tl cancel_btn" 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>
@ -381,7 +392,7 @@
<label for="project_name" class="tl">dialog.project.name</label>
</div>
<div class="dialog_bar">
<input v-model="Project.name" type="text" id="project_name" class="dark_bordered input_wide">
<input v-model="Project.name" type="text" id="project_name" v-on:focusout="syncGeometry()" class="dark_bordered input_wide">
</div>
@ -631,6 +642,7 @@
<a class="open-in-browser" href="https://github.com/oliver-moran/jimp">Jimp</a>,
<a class="open-in-browser" href="https://bgrins.github.io/spectrum">Spectrum</a>,
<a class="open-in-browser" href="https://github.com/jnordberg/gif.js">gif.js</a>
<a class="open-in-browser" href="https://stuk.github.io/jszip/">JSZip</a>
</p>
</div>
<div class="dialog_bar">
@ -855,12 +867,13 @@
<i class="material-icons texture_error" title="Image Error" 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>
<div class="texture_name">{{ texture.name }}</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>
<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>
</li>
</ul>
</div>
@ -882,6 +895,7 @@
<div id="preview">
</div>
<div id="timeline">
<div class="toolbar_wrapper timeline"></div>
<div id="timeline_head">
<div id="timeline_corner"></div>
<div class="channel_head">

View File

@ -19,7 +19,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '2.4.0';
const appVersion = '2.5.0';
</script>
<script src="lib/vue.min.js"></script>
<script src="lib/vue_sortable.js"></script>
@ -28,12 +28,12 @@
<script src="lib/jquery-ui.min.js"></script>
<script src="lib/targa.js"></script>
<script src="lib/jimp.min.js"></script>
<script src="lib/jszip.min.js"></script>
<script src="lib/gif.js"></script>
<script src="lib/spectrum.js"></script>
<script src="lib/three.js"></script>
<script src="lib/three_custom.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/TransformControls.js"></script>
<script src="js/OBJExporter.js"></script>
<script src="js/language.js"></script>
@ -54,8 +54,9 @@
<script src="js/api.js"></script>
<script src="js/io.js"></script>
<script src="js/elements.js"></script>
<script src="js/element.js"></script>
<script src="js/preview.js"></script>
<script src="js/TransformControls.js"></script>
<script src="js/transform.js"></script>
<script src="js/textures.js"></script>
<script src="js/uv.js"></script>
@ -85,9 +86,18 @@
?></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;">
<div>
<h3><i class="material-icons">keyboard</i><span class="tl">keybindings.recording</span></h3>
<p class="tl">keybindings.press</p>
<button class="large tl" onclick="Keybinds.recording.stopRecording()">dialog.cancel</button>
<button class="large tl" onclick="Keybinds.recording.clear().stopRecording()">keybindings.clear</button>
<div id="keybind_input_box" contenteditable="true" style="font-size: 0"></div>
</div>
</div>
<div class="dialog draggable" id="welcome_screen">
<div id="welcome_content"></div>
<button type="button" class="large cancel_btn hidden tl" onclick="hideDialog()">dialog.cancel</button>
@ -196,7 +206,7 @@
<i class="material-icons" id="plugin_search_bar_icon">search</i>
</div>
<ul id="pe_list" class="list">
<li v-for="item in searched" v-bind:class="{ selected: item.selected }" v-on:click="selectE(item, $event)" ondblclick="loadPEModel()">
<li v-for="item in searched" v-bind:class="{ selected: item.selected }" v-on:click="selectE(item, $event)" ondblclick="loadEntityModel()">
<img class="pe_icon" v-if="item.icon" v-bind:src="item.icon">
<div class="pe_icon" v-else></div>
<h4>{{ item.title }} <span>({{ item.name }})</span></h4>
@ -205,7 +215,7 @@
</ul>
<div class="dialog_bar">
<button type="button" class="large tl confirm_btn" onclick="loadPEModel()">dialog.import</button>
<button type="button" class="large tl confirm_btn" onclick="loadEntityModel()">dialog.import</button>
<button type="button" class="large tl cancel_btn" 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>
@ -397,7 +407,7 @@
<label for="project_name" class="tl">dialog.project.name</label>
</div>
<div class="dialog_bar">
<input v-model="Project.name" type="text" id="project_name" class="dark_bordered input_wide">
<input v-model="Project.name" type="text" id="project_name" v-on:focusout="syncGeometry()" class="dark_bordered input_wide">
</div>
@ -647,6 +657,7 @@
<a class="open-in-browser" href="https://github.com/oliver-moran/jimp">Jimp</a>,
<a class="open-in-browser" href="https://bgrins.github.io/spectrum">Spectrum</a>,
<a class="open-in-browser" href="https://github.com/jnordberg/gif.js">gif.js</a>
<a class="open-in-browser" href="https://stuk.github.io/jszip/">JSZip</a>
</p>
</div>
<div class="dialog_bar">
@ -871,12 +882,13 @@
<i class="material-icons texture_error" title="Image Error" 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>
<div class="texture_name">{{ texture.name }}</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>
<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>
</li>
</ul>
</div>
@ -898,6 +910,7 @@
<div id="preview">
</div>
<div id="timeline">
<div class="toolbar_wrapper timeline"></div>
<div id="timeline_head">
<div id="timeline_corner"></div>
<div class="channel_head">

View File

@ -175,7 +175,7 @@ function getMtlFace(obj, index) {
if (tex === null) {
return false
} else if (typeof tex === 'string') {
} else if (!tex || typeof tex === 'string') {
return 'usemtl none\n'
} else {
return 'usemtl ' + tex.id + '\n';

View File

@ -923,7 +923,7 @@
point.sub( offset );
}
if (Toolbox.selected.id === 'rotate_tool') {
if (Toolbox.selected.transformerMode === 'rotate') {
point.sub( worldPosition );
var rotations = [
@ -941,35 +941,7 @@
if (Modes.id === 'edit') {
if (Toolbox.selected.id === 'resize_tool') {
//Scale
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
if (previousValue !== point[axis]) {
beforeFirstChange(event)
selected.forEach(function(obj, i) {
var mesh = scope.objects[i]
var allow_negative = settings.negative_size.value
if (scope.direction) { //Positive
scaleCube(obj, limitNumber(obj.oldScale + point[axis], (allow_negative ? -32000 : 0), 32000), axisNumber)
} else {
scaleCubeNegative(obj, limitNumber(obj.oldScale - point[axis], (allow_negative ? -32000 : 0), 32000), axisNumber)
}
if (Blockbench.entity_mode === true) {
Canvas.updateUV(obj)
}
})
Canvas.updatePositions(true)
centerTransformer()
previousValue = point[axis]
scope.hasChanged = true
}
} else if (Toolbox.selected.id === 'move_tool') {
if (Toolbox.selected.id === 'move_tool') {
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
@ -1001,7 +973,6 @@
}
selected.forEach(function(obj, i) {
var mesh = scope.objects[i]
var valx = obj.from[axisNumber]
valx += difference
moveCube(obj, valx, axisNumber, _has_groups)
@ -1012,6 +983,34 @@
previousValue = point[axis]
scope.hasChanged = true
}
} else if (Toolbox.selected.id === 'resize_tool') {
//Scale
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
if (previousValue !== point[axis]) {
beforeFirstChange(event)
selected.forEach(function(obj, i) {
var mesh = scope.objects[i]
var allow_negative = settings.negative_size.value
if (scope.direction) { //Positive
scaleCube(obj, limitNumber(obj.oldScale + point[axis], (allow_negative ? -32000 : 0), 32000), axisNumber)
} else {
scaleCubeNegative(obj, limitNumber(obj.oldScale - point[axis], (allow_negative ? -32000 : 0), 32000), axisNumber)
}
if (Blockbench.entity_mode === true) {
Canvas.updateUV(obj)
}
})
Canvas.updatePositions(true)
centerTransformer()
previousValue = point[axis]
scope.hasChanged = true
}
} else if (Toolbox.selected.id === 'rotate_tool') {
var snap = getRotationInterval(event)
@ -1029,6 +1028,35 @@
previousValue = angle
scope.hasChanged = true
}
} else if (Toolbox.selected.id === 'pivot_tool') {
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
if (previousValue === undefined) {
previousValue = point[axis]
} else if (previousValue !== point[axis]) {
beforeFirstChange(event)
var difference = point[axis] - previousValue
if (Blockbench.entity_mode && selected_group) {
selected_group.origin[axisNumber] += difference;
} else {
var origin = Transformer.position.toArray()
origin[axisNumber] += difference;
selected.forEach(cube => {
cube.transferOrigin(origin)
})
}
Canvas.updatePositions(true)
centerTransformer()
previousValue = point[axis]
scope.hasChanged = true
}
}
} else if (Animator.open) {

View File

@ -272,7 +272,7 @@ class Widget extends BarItem {
constructor(data) {
super(data);
this.type = 'widget';
this.uniqueNode = true;
//this.uniqueNode = true;
}
}
class NumSlider extends Widget {
@ -283,6 +283,7 @@ class NumSlider extends Widget {
this.icon = 'code'
this.value = 0;
this.width = 79;
this.uniqueNode = true;
if (typeof data.get === 'function') this.get = data.get;
this.onBefore = data.onBefore;
this.onAfter = data.onAfter;
@ -879,10 +880,20 @@ const BARS = {
selectFace: true,
transformerMode: 'rotate',
toolbar: 'transform',
alt_tool: 'move_tool',
alt_tool: 'pivot_tool',
modes: ['edit', 'display', 'animate'],
keybind: new Keybind({key: 82}),
})
new Tool({
id: 'pivot_tool',
icon: 'gps_fixed',
category: 'tools',
transformerMode: 'translate',
toolbar: 'transform',
alt_tool: 'rotate_tool',
modes: ['edit'],
keybind: new Keybind({key: 80}),
})
new Tool({
id: 'vertex_snap_tool',
icon: 'icon-vertexsnap',
@ -1227,10 +1238,9 @@ const BARS = {
//Find Action
new Action({
id: 'select_action',
id: 'action_control',
icon: 'fullscreen',
category: 'blockbench',
condition: isApp,
keybind: new Keybind({key: 70}),
click: function () {
ActionControl.select()
@ -1279,6 +1289,7 @@ const BARS = {
'move_tool',
'resize_tool',
'rotate_tool',
'pivot_tool',
'vertex_snap_tool',
'brush_tool',
'fill_tool',
@ -1370,7 +1381,6 @@ const BARS = {
children: [
'add_animation',
'slider_animation_length',
'play_animation'
],
default_place: true
})
@ -1381,6 +1391,16 @@ const BARS = {
],
default_place: true
})
Toolbars.timeline = new Toolbar({
id: 'timeline',
children: [
'slider_animation_speed',
'previous_keyframe',
'next_keyframe',
'play_animation',
],
default_place: true
})
//Tools
Toolbars.transform = new Toolbar({
id: 'transform',
@ -1965,11 +1985,15 @@ const MenuBar = {
{name: 'menu.file.recent', id: 'recent', icon: 'history', condition: function() {return isApp && recent_projects.length}, children: function() {
var arr = []
recent_projects.forEach(function(p) {
var entity = p.name.substr(0,4) === 'mobs'
switch (p.icon_id) {
default: var icon = 'fa-file-o'; break;
case 1: var icon = 'icon-blockbench_file'; break;
case 2: var icon = 'fa-file-text-o'; break;
}
arr.splice(0, 0, {
name: p.name,
path: p.path,
icon: entity ? 'view_list' : 'insert_drive_file',
icon: icon,
click: function(c, event) {
readFile(p.path, !event.shiftKey)
}

View File

@ -20,9 +20,24 @@ class Animation {
Merge.boolean(this, data, 'override')
Merge.string(this, data, 'anim_time_update')
Merge.number(this, data, 'length')
if (data.bones) {
for (var key in data.bones) {
var group = TreeElements.findRecursive( isUUID(key) ? 'uuid' : 'name', key )
if (group) {
var ba = this.getBoneAnimator(group)
var kfs = data.bones[key]
if (kfs && ba) {
kfs.forEach(kf_data => {
var kf = new Keyframe(kf_data)
ba.pushKeyframe(kf)
})
}
}
}
}
return this;
}
undoCopy() {
undoCopy(options) {
var scope = this;
var copy = {
uuid: this.uuid,
@ -33,11 +48,14 @@ class Animation {
length: this.length,
selected: this.selected,
}
if (this.bones.length) {
if (Object.keys(this.bones).length) {
copy.bones = {}
for (var uuid in this.bones) {
var kfs = this.bones[uuid].keyframes
if (kfs && kfs.length) {
if (options && options.bone_names) {
uuid = this.bones[uuid].getGroup().name
}
var kfs_copy = copy.bones[uuid] = []
kfs.forEach(kf => {
kfs_copy.push(kf.undoCopy())
@ -768,6 +786,8 @@ const Animator = {
}
if (Animator.selected) {
Animator.selected.select()
} else if (Animator.animations.length) {
Animator.animations[0].select()
}
if (isApp && !Prop.animation_path && !Animator.animations.length && Prop.file_path) {
//Load
@ -894,6 +914,7 @@ const Animator = {
const Timeline = {
keyframes: [],//frames
selected: [],//frames
playback_speed: 100,
second: 0,
playing: false,
setTime: function(seconds, editing) {
@ -1024,6 +1045,7 @@ const Timeline = {
}
})
BarItems.slider_animation_speed.update()
Timeline.is_setup = true
Timeline.setTime(0)
},
@ -1144,8 +1166,9 @@ const Timeline = {
loop: function() {
Animator.preview()
if (Animator.selected && Timeline.second < (Animator.selected.length||1e3)) {
Animator.interval = setTimeout(Timeline.loop, 16.66)
Timeline.setTime(Timeline.second + 1/60)
Animator.interval = setTimeout(Timeline.loop, 100/6)
Timeline.setTime(Timeline.second + (1/60) * (Timeline.playback_speed/100))
} else {
Timeline.setTime(0)
if (Animator.selected && Animator.selected.loop) {
@ -1321,6 +1344,50 @@ BARS.defineActions(function() {
Animator.selected.length = limitNumber(value, 0, 1e4)
}
})
new NumSlider({
id: 'slider_animation_speed',
category: 'animation',
condition: () => Animator.open,
get: function() {
return Timeline.playback_speed;
},
change: function(value, fixed) {
if (!fixed) {
value += Timeline.playback_speed
}
Timeline.playback_speed = limitNumber(value, 0, 10000)
},
getInterval: (e) => {
var val = BarItems.slider_animation_speed.get()
if (e.shiftKey) {
if (val < 50) {
return 10;
} else {
return 50;
}
}
if (e.ctrlKey) {
if (val < 500) {
return 1;
} else {
return 10;
}
}
if (val < 10) {
return 1;
} else if (val < 50) {
return 5;
} else if (val < 160) {
return 10;
} else if (val < 300) {
return 20;
} else if (val < 1000) {
return 50;
} else {
return 500;
}
}
})
new NumSlider({
id: 'slider_keyframe_time',
category: 'animation',
@ -1344,6 +1411,72 @@ BARS.defineActions(function() {
Undo.finishEdit('edit keyframe')
}
})
new Action({
id: 'previous_keyframe',
icon: 'fa-arrow-circle-left',
category: 'animation',
condition: () => Animator.open,
click: function () {
var time = Timeline.second;
function getDelta(kf, abs) {
return kf.time - time
}
var matches = []
for (var i = 0; i < Timeline.keyframes.length; i++) {
var kf = Timeline.keyframes[i]
let delta = getDelta(kf)
if (delta < 0) {
matches.push(kf)
}
}
matches.sort((a, b) => {
return Math.abs(getDelta(a)) - Math.abs(getDelta(b))
})
var kf = matches[0]
if (kf) {
kf.select().callMarker()
} else {
if (Timeline.selected.length) {
selectAllKeyframes()
selectAllKeyframes()
}
Timeline.setTime(0)
Animator.preview()
}
}
})
new Action({
id: 'next_keyframe',
icon: 'fa-arrow-circle-right',
category: 'animation',
condition: () => Animator.open,
click: function () {
var time = Timeline.second;
function getDelta(kf, abs) {
return kf.time - time
}
var matches = []
for (var i = 0; i < Timeline.keyframes.length; i++) {
var kf = Timeline.keyframes[i]
let delta = getDelta(kf)
if (delta > 0) {
matches.push(kf)
}
}
matches.sort((a, b) => {
return Math.abs(getDelta(a)) - Math.abs(getDelta(b))
})
var kf = matches[0]
if (kf) {
kf.select().callMarker()
}
}
})
new Action({
id: 'select_all_keyframes',
icon: 'select_all',

View File

@ -380,6 +380,7 @@ class API {
*/
if (Blockbench.isWeb) {
var file_name = options.name + (options.extensions ? '.'+options.extensions[0] : '')
var callback_used;
if (options.custom_writer) {
options.custom_writer(options.content, file_name)
@ -391,6 +392,10 @@ class API {
if (Blockbench.browser === 'firefox') document.body.appendChild(download);
download.click();
if (Blockbench.browser === 'firefox') document.body.removeChild(download);
} else if (options.savetype === 'zip') {
saveAs(options.content, file_name)
} else {
var blob = new Blob([options.content], {type: "text/plain;charset=utf-8"});
saveAs(blob, file_name, {autoBOM: true})
@ -399,7 +404,7 @@ class API {
Prop.project_saved = true;
setProjectTitle(options.name)
}
if (typeof cb === 'function') {
if (!callback_used && typeof cb === 'function') {
cb()
}
} else {
@ -446,7 +451,7 @@ class API {
Prop.project_saved = true;
Project.name = pathToName(file_path, true)
setProjectTitle(pathToName(file_path, false))
addRecentProject({name: pathToName(file_path, Blockbench.entity_mode ? 'mobs_id' : false), path: Prop.file_path})
addRecentProject({name: pathToName(file_path, Blockbench.entity_mode ? 'mobs_id' : true), path: Prop.file_path})
Blockbench.showQuickMessage(tl('message.save_file', [Project.name]))
if (Blockbench.hasFlag('close_after_saving')) {
closeBlockbenchWindow()

View File

@ -88,7 +88,11 @@ function addRecentProject(data) {
}
i--;
}
recent_projects.push({name: data.name, path: data.path})
var icon_id = pathToExtension(data.path) === 'bbmodel' ? 1 : 0;
if (data.name.substr(0,4) === 'mobs') {
icon_id = 2;
}
recent_projects.push({name: data.name, path: data.path, icon_id})
if (recent_projects.length > 8) {
recent_projects.shift()
}
@ -519,6 +523,7 @@ function createBackup(init) {
var days = d.getDate() + (d.getMonth()+1)*30.44 + (d.getYear()-100)*365.25
if (init) {
//Clear old backups
fs.readdir(folder_path, (err, files) => {
if (!err) {
files.forEach((name, i) => {
@ -541,23 +546,15 @@ function createBackup(init) {
}
if (init || elements.length === 0) return;
var model = buildBlockModel({
backup: true,
raw: true,
cube_name: true,
prevent_dialog: true,
comment: false,
groups: true
})
var model = buildBBModel()
var file_name = 'backup_'+d.getDate()+'.'+(d.getMonth()+1)+'.'+(d.getYear()-100)+'_'+d.getHours()+'.'+d.getMinutes()
var file_path = folder_path+osfs+file_name+'.json'
var file_path = folder_path+osfs+file_name+'.bbmodel'
fs.writeFile(file_path, JSON.stringify(model), function (err) {
fs.writeFile(file_path, model, function (err) {
if (err) {
console.log('Error creating backup: '+err)
}
})
//trimBackups
}
//Zoom
function setZoomLevel(mode) {

View File

@ -33,6 +33,12 @@ const Project = {
texture_width : 16,
texture_height : 16,
ambientocclusion: true,
get geometry_name() {
return this.parent;
},
set geometry_name(n) {
this.parent = n;
},
}
const mouse_pos = {x:0,y:0}
const sort_collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
@ -163,9 +169,7 @@ function updateNslideValues() {
BarItems.slider_size_y.update()
BarItems.slider_size_z.update()
if (Blockbench.entity_mode) {
BarItems.slider_inflate.update()
}
BarItems.slider_inflate.update()
}
if (selected.length || (Blockbench.entity_mode && selected_group)) {
BarItems.slider_origin_x.update()
@ -437,7 +441,19 @@ BARS.defineActions(function() {
})
})
//Misc
var Screencam = {
const TickUpdates = {
Run: function() {
if (TickUpdates.outliner) {
delete TickUpdates.outliner;
loadOutlinerDraggable()
}
if (TickUpdates.selection) {
delete TickUpdates.selection;
updateSelection()
}
}
}
const Screencam = {
fullScreen: function(options, cb) {
setTimeout(function() {
currentwindow.capturePage(function(screenshot) {
@ -516,34 +532,6 @@ var Screencam = {
})
var frame_count = (options.length/interval)
/*
gif.on('finished', blob => {
var reader = new FileReader()
reader.onload = () => {
Screencam.returnScreenshot(reader.result, cb)
}
if (Animator.open && Timeline.playing) {
Timeline.pause()
}
reader.readAsDataURL(blob)
if (!options.silent) {
Blockbench.setProgress(0)
Blockbench.setStatusBarText()
}
})
if (!options.silent) {
Blockbench.setStatusBarText(tl('status_bar.recording_gif'))
gif.on('progress', Blockbench.setProgress)
}
for (var frame = 0; frame < frame_count; frame++) {
gif.addFrame(quad_previews.current.canvas, {delay: interval})
}
gif.render()*/
//Problem with this ^^ When recording, frames get generated as fast as possible
gif.on('finished', blob => {
var reader = new FileReader()
reader.onload = () => {
@ -582,7 +570,7 @@ var Screencam = {
}, options.length)
}
}
var Clipbench = {
const Clipbench = {
cubes: [],
copy: function(event, cut) {
var p = Prop.active_panel
@ -632,7 +620,6 @@ var Clipbench = {
}
if (Clipbench.keyframes && Clipbench.keyframes.length) {
if (!Animator.selected) return;
var bone = Animator.selected.getBoneAnimator()
if (bone) {
@ -659,12 +646,12 @@ var Clipbench = {
Undo.initEdit({outliner: true, cubes: [], selection: true});
//Group
var group = 'root'
var target = 'root'
if (selected_group) {
group = selected_group
target = selected_group
selected_group.isOpen = true
} else if (selected[0]) {
group = selected[0]
target = selected[0]
}
selected.length = 0
if (isApp) {
@ -681,18 +668,30 @@ var Clipbench = {
} catch (err) {}
}
if (Clipbench.group) {
if (typeof Clipbench.group.duplicate !== 'function') {
Clipbench.group = new Group(Clipbench.group)
function iterate(obj, parent) {
if (obj.children) {
var copy = new Group(obj)
if (obj.children && obj.children.length) {
obj.children.forEach((child) => {
iterate(child, copy)
})
}
copy.addTo(parent)
} else {
var copy = new Cube(obj)
copy.addTo(parent).init()
selected.push(elements[elements.length-1])
}
}
Clipbench.group.duplicate(group)
} else {
Clipbench.cubes.forEach(function(obj) {
var base_cube = new Cube(obj)
iterate(Clipbench.group, target)
updateSelection()
base_cube.addTo(group, false).init()
} else if (Clipbench.cubes && Clipbench.cubes.length) {
Clipbench.cubes.forEach(function(obj) {
var copy = new Cube(obj)
copy.addTo(target).init()
selected.push(elements[elements.length-1])
})
loadOutlinerDraggable()
updateSelection()
}
Undo.finishEdit('paste', {outliner: true, cubes: selected, selection: true});
@ -759,82 +758,8 @@ var Clipbench = {
}
}
}
TextureAnimator = {
isPlaying: false,
interval: false,
start: function() {
clearInterval(TextureAnimator.interval)
TextureAnimator.isPlaying = true
TextureAnimator.updateButton()
TextureAnimator.interval = setInterval(TextureAnimator.nextFrame, 1000/settings.texture_fps.value)
},
stop: function() {
TextureAnimator.isPlaying = false
clearInterval(TextureAnimator.interval)
TextureAnimator.updateButton()
},
toggle: function() {
if (TextureAnimator.isPlaying) {
TextureAnimator.stop()
} else {
TextureAnimator.start()
}
},
updateSpeed: function() {
if (TextureAnimator.isPlaying) {
TextureAnimator.stop()
TextureAnimator.start()
}
},
nextFrame: function() {
var animated_tex = []
textures.forEach(function(tex, i) {
if (tex.frameCount > 1) {
if (tex.currentFrame === undefined) {
tex.currentFrame = 0
} else if (tex.currentFrame >= tex.frameCount-1) {
tex.currentFrame = 0
} else {
tex.currentFrame++;
}
$($('.texture').get(i)).find('img').css('margin-top', (tex.currentFrame*-48)+'px')
animated_tex.push(''+tex.id)
}
})
elements.forEach(function(obj) {
var update = false
for (var face in obj.faces) {
if (update === false) {
update = (
obj.faces.hasOwnProperty(face) &&
typeof obj.faces[face].texture === 'string' &&
animated_tex.includes(obj.faces[face].texture.replace(/^#/, ''))
)
}
}
if (update) {
Canvas.updateUV(obj, true)
}
})
},
reset: function() {
TextureAnimator.stop()
textures.forEach(function(tex, i) {
if (tex.frameCount) {
tex.currentFrame = 0
$($('.texture').get(i)).find('img').css('margin-top', '0')
}
})
while (i < elements.length) {
Canvas.updateUV(elements[i], true)
i++;
}
},
updateButton: function() {
BarItems.animated_textures.setIcon( TextureAnimator.isPlaying ? 'pause' : 'play_arrow' )
}
}
var Vertexsnap = {
const Vertexsnap = {
step1: true,
vertexes: new THREE.Object3D(),
vertexed_cubes: [],

View File

@ -228,7 +228,8 @@ class refModel {
this.onload()
}
}
load() {
load(index) {
displayReferenceObjects.ref_indexes[display_slot] = index || 0;
displayReferenceObjects.clear()
if (typeof this.onload === 'function') {
this.onload()
@ -1232,15 +1233,15 @@ window.displayReferenceObjects = {
var button = $(
`<div>
<input class="hidden" type="radio" name="refmodel" id="${ref.id}"${ i === 0 ? ' selected' : '' }>
<label class="tool" onclick="displayReferenceObjects.refmodels.${ref.id}.load()" for="${ref.id}">
<label class="tool" onclick="displayReferenceObjects.refmodels.${ref.id}.load(${i})" for="${ref.id}">
<div class="tooltip">${ref.name}</div>
<i class="${icon}"></i>
</label>
</div>`
)
$('#display_ref_bar').append(button)
if (i === 0) {
ref.load()
if (i === displayReferenceObjects.ref_indexes[display_slot]) {
ref.load(i)
button.find('input').prop("checked", true)
}
i++;
@ -1251,6 +1252,16 @@ window.displayReferenceObjects = {
displayReferenceObjects.active = false
$('#donation_hint').hide()
},
ref_indexes: {
thirdperson_righthand: 0,
thirdperson_lefthand: 0,
firstperson_righthand: 0,
firstperson_lefthand: 0,
ground: 0,
gui: 0,
head: 0,
fixed: 0,
},
slots: [
'thirdperson_righthand',
'thirdperson_lefthand',
@ -1280,6 +1291,7 @@ enterDisplaySettings = function() { //Enterung Display Setting Mode, changes th
display_preview.setNormalCamera()
display_preview.camPers.position.set(-80, 40, -30)
display_preview.camPers.setFocalLength(45)
lights.rotation.y = (Math.PI/4)*3
$('body').addClass('display_mode')
$('.m_edit').hide()
@ -1305,6 +1317,7 @@ exitDisplaySettings = function() { //Enterung Display Setting Mode, changes the
setDisplayArea(0,0,0, 0,0,0, 1,1,1)
display_area.updateMatrixWorld()
display_base.updateMatrixWorld()
lights.rotation.y = 0
display_mode = false;
main_preview.fullscreen()

File diff suppressed because it is too large Load Diff

454
js/io.js
View File

@ -43,7 +43,7 @@ function newProject(entity_mode) {
function setupDragHandlers() {
Blockbench.addDragHandler(
'model',
{extensions: ['json', 'jem', 'jpm']},
{extensions: ['json', 'jem', 'jpm', 'bbmodel']},
function(files) {
loadModel(files[0].content, files[0].path || files[0].path)
if (isApp) {
@ -94,14 +94,16 @@ function loadModel(data, filepath, add) {
var model = autoParseJSON(data)
var extension = pathToExtension(filepath)
if (extension === 'jpm') {
if (extension === 'bbmodel') {
loadBBModel(model)
} else if (extension === 'jpm') {
loadJPMModel(model)
} else if (extension === 'jem') {
loadJEMModel(model)
} else { //JSON
for (var key in model) {
if (key.includes('geometry.')) {
loadPEModelFile(model)
loadEntityModelFile(model)
return;
}
}
@ -116,6 +118,80 @@ function loadModel(data, filepath, add) {
Prop.project_saved = true;
}
}
function loadBBModel(model) {
if (!model.meta || !model.meta.format) {
Blockbench.showMessageBox({
translateKey: 'invalid_model',
icon: 'error',
})
return;
}
if (compareVersions(model.meta.format, '1.0')) {
Blockbench.showMessageBox({
translateKey: 'outdated_client',
icon: 'error',
})
return;
}
if (model.meta.box_uv && model.meta.bone_rig) {
entityMode.join()
} else {
Blockbench.entity_mode = false;
}
saveSettings()
Project.name = model.name;
if (model.geo_name) {
Project.geometry_name = model.geo_name;
} else if (model.parent) {
Project.parent = model.parent;
}
if (model.ambientocclusion !== undefined) {
Project.ambientocclusion = !!model.ambientocclusion;
}
if (model.resolution !== undefined) {
Project.texture_width = model.resolution.width;
Project.texture_height = model.resolution.height;
}
if (model.textures) {
model.textures.forEach(tex => {
var tex_copy = new Texture(tex).add(false);
tex_copy.uuid = tex.uuid;
if (tex_copy.mode === 'link') {
tex_copy.fromPath(tex.path)
} else {
tex_copy.fromDataURL(tex.source)
}
})
}
if (model.cubes) {
model.cubes.forEach(function(cube) {
base_cube = new Cube(cube).init(false)
for (var face in base_cube.faces) {
if (!model.meta.box_uv) {
var texture = textures[cube.faces[face].texture]
if (texture) {
base_cube.faces[face].texture = texture.uuid
}
} else if (textures[0]) {
base_cube.faces[face].texture = textures[0].uuid
}
}
})
loadOutlinerDraggable()
}
if (model.outliner) {
parseGroups(model.outliner)
}
if (model.animations) {
model.animations.forEach(ani => {
var base_ani = new Animation(ani).add();
})
}
if (model.display !== undefined) {
DisplayMode.loadJSON(model.display)
}
}
function loadBlockModel(model, filepath, add) {
if (!model.elements && !model.parent && !model.display && !model.textures) {
Blockbench.showMessageBox({
@ -329,6 +405,8 @@ function loadJEMModel(model) {
if (model.models) {
model.models.forEach(function(b) {
if (typeof b !== 'object') return;
var subcount = 0;
//Bone
var group = new Group({
name: b.part,
@ -339,40 +417,60 @@ function loadJEMModel(model) {
group.origin[1] *= -1
group.origin[2] *= -1
//Cubes
if ((b.boxes && b.boxes.length) || (b.submodel && b.submodel.boxes && b.submodel.boxes.length)) {
function addBox(box, i, mirrored) {
var base_cube = new Cube({
name: box.name || group.name,
autouv: 0,
uv_offset: box.textureOffset,
inflate: box.sizeAdd
})
if (box.coordinates) {
base_cube.extend({
from: [
box.coordinates[0],
box.coordinates[1],
box.coordinates[2]
],
to: [
box.coordinates[0]+box.coordinates[3],
box.coordinates[1]+box.coordinates[4],
box.coordinates[2]+box.coordinates[5]
]
function readContent(submodel, p_group) {
if (submodel.boxes && submodel.boxes.length) {
submodel.boxes.forEach(box => {
var base_cube = new Cube({
name: box.name || p_group.name,
autouv: 0,
uv_offset: box.textureOffset,
inflate: box.sizeAdd,
mirror_uv: p_group.mirror_uv
})
}
elements.push(base_cube)
base_cube.addTo(group, false)
if (box.coordinates) {
base_cube.extend({
from: [
box.coordinates[0],
box.coordinates[1],
box.coordinates[2]
],
to: [
box.coordinates[0]+box.coordinates[3],
box.coordinates[1]+box.coordinates[4],
box.coordinates[2]+box.coordinates[5]
]
})
}
if (p_group.parent !== 'root') {
for (var i = 0; i < 3; i++) {
base_cube.from[i] += p_group.origin[i];
base_cube.to[i] += p_group.origin[i];
}
}
elements.push(base_cube)
base_cube.addTo(p_group)
})
}
if (b.boxes && b.boxes.length) {
b.boxes.forEach(addBox)
}
if (b.submodel && b.submodel.boxes && b.submodel.boxes.length) {
b.submodel.boxes.forEach(function(box, i) {addBox(box, i, true)})
if (submodel.submodels && submodel.submodels.length) {
submodel.submodels.forEach(subsub => {
var group = new Group({
name: `${b.part}_sub_${subcount}`,
origin: subsub.translate || submodel.translate,
rotation: subsub.rotate,
mirror_uv: (subsub.mirrorTexture && subsub.mirrorTexture.includes('u'))
})
subcount++;
group.rotation[2] *= -1
group.addTo(p_group)
readContent(subsub, group)
})
}
}
group.addTo(undefined, false)
group.addTo(undefined)
readContent(b, group)
})
}
loadOutlinerDraggable()
@ -382,7 +480,7 @@ function loadJEMModel(model) {
new Texture().fromPath(path).add(false)
}
}
function loadPEModelFile(data) {
function loadEntityModelFile(data) {
pe_list_data.length = 0
entityMode.join()
@ -393,7 +491,7 @@ function loadPEModelFile(data) {
}
}
if (geometries.length === 1) {
loadPEModel({object: data[geometries[0]], name: geometries[0]})
loadEntityModel({object: data[geometries[0]], name: geometries[0]})
return;
}
@ -536,7 +634,7 @@ function loadPEModelFile(data) {
$('input#pe_search_bar').select()
//texturelist._data.elements = textures
}
function loadPEModel(data) {
function loadEntityModel(data) {
if (data === undefined) {
pe_list_data.forEach(function(s) {
if (s.selected === true) {
@ -621,12 +719,12 @@ function loadPEModel(data) {
base_cube.mirror_uv = s.mirror === true
}
elements.push(base_cube)
base_cube.addTo(group, false)
base_cube.addTo(group)
})
}
if (b.children) {
b.children.forEach(function(cg) {
cg.addTo(group, false)
cg.addTo(group)
})
}
var parent_group = 'root';
@ -641,7 +739,7 @@ function loadPEModel(data) {
})
}
}
group.addTo(parent_group, false)
group.addTo(parent_group)
})
}
pe_list_data.length = 0;
@ -700,7 +798,7 @@ var Extruder = {
},
startConversion: function() {
var scan_mode = $('select#scan_mode option:selected').attr('id') /*areas, lines, columns, pixels*/
var texture_index = '#'+textures[textures.length-1].id
var texture = textures[textures.length-1].uuid
var isNewProject = elements.length === 0;
var jimage = Jimp.read(Extruder.ext_img.src).then(function(image) {
@ -809,12 +907,12 @@ var Extruder = {
from: [rect.x*scale_i, 0, rect.y*scale_i],
to: [(rect.x2+1)*scale_i, scale_i, (rect.y2+1)*scale_i],
faces: {
up: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index},
down: {uv:[rect.x*scale_i, (rect.y2+1)*scale_i, (rect.x2+1)*scale_i, rect.y*scale_i], texture: texture_index},
north: {uv:[(rect.x2+1)*scale_i, rect.y*scale_i, rect.x*scale_i, (rect.y+1)*scale_i], texture: texture_index},
south: {uv:[rect.x*scale_i, rect.y2*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index},
east: {uv:[rect.x2*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 90},
west: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x+1)*scale_i, (rect.y2+1)*scale_i], texture: texture_index, rotation: 270}
up: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture},
down: {uv:[rect.x*scale_i, (rect.y2+1)*scale_i, (rect.x2+1)*scale_i, rect.y*scale_i], texture: texture},
north: {uv:[(rect.x2+1)*scale_i, rect.y*scale_i, rect.x*scale_i, (rect.y+1)*scale_i], texture: texture},
south: {uv:[rect.x*scale_i, rect.y2*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture},
east: {uv:[rect.x2*scale_i, rect.y*scale_i, (rect.x2+1)*scale_i, (rect.y2+1)*scale_i], texture: texture, rotation: 90},
west: {uv:[rect.x*scale_i, rect.y*scale_i, (rect.x+1)*scale_i, (rect.y2+1)*scale_i], texture: texture, rotation: 270}
}
})
@ -831,7 +929,7 @@ var Extruder = {
var group = new Group(cube_name).addTo()
selected.forEach(function(s) {
s.addTo(group, false)
s.addTo(group)
})
if (Blockbench.hasFlag('new_project') || isNewProject) {
setProjectTitle(cube_name)
@ -845,6 +943,89 @@ var Extruder = {
}
}
//Export
function buildBBModel(options) {
if (!options) options = 0;
var model = {
meta: {
format: '1.0',
box_uv: Blockbench.entity_mode,
bone_rig: Blockbench.entity_mode,
},
name: Project.name,
}
model[Blockbench.entity_mode ? 'geo_name' : 'parent'] = Project.parent
if (!Blockbench.entity_mode) {
model.ambientocclusion = Project.ambientocclusion
}
if (model.meta.box_uv) {
model.resolution = {
width: Project.texture_width || 16,
height: Project.texture_height || 16,
}
}
model.cubes = []
elements.forEach(cube => {
var el = {
name: cube.name,
from: cube.from,
to: cube.to,
autouv: cube.autouv,
color: cube.color
}
if (!cube.visibility) el.visibility = false;
if (!cube.export) el.export = false;
if (!cube.shade) el.shade = false;
if (cube.inflate) el.inflate = cube.inflate;
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()
}
}
model.cubes.push(el)
})
model.outliner = compileGroups()
model.textures = [];
textures.forEach(tex => {
var t = tex.getUndoCopy();
delete t.selected;
model.textures.push(t);
})
if (Animator.animations.length) {
model.animations = [];
Animator.animations.forEach(a => {
model.animations.push(a.undoCopy({bone_names: true}))
})
}
if (!Blockbench.entity_mode && Object.keys(display).length >= 1) {
var new_display = {}
var entries = 0;
for (var i in DisplayMode.slots) {
var key = DisplayMode.slots[i]
if (DisplayMode.slots.hasOwnProperty(i) && display[key] && display[key].export) {
new_display[key] = display[key].export()
entries++;
}
}
if (entries) {
model.display = new_display
}
}
if (options.raw) {
return model
} else {
return JSON.stringify(model)
}
}
function buildBlockModel(options) {
if (options === undefined) options = {}
var clear_elements = []
@ -865,6 +1046,12 @@ function buildBlockModel(options) {
}
element.from = s.from.slice()
element.to = s.to.slice()
if (s.inflate) {
for (var i = 0; i < 3; i++) {
element.from[i] -= s.inflate;
element.to[i] += s.inflate;
}
}
if (s.shade === false) {
element.shade = false
}
@ -1233,77 +1420,92 @@ function buildJEMModel(options) {
entitymodel.texture = textures[0].name
}
entitymodel.textureSize = [parseInt(Project.texture_width), parseInt(Project.texture_height)];
var models = []
entitymodel.models = []
TreeElements.forEach(function(g) {
if (g.type !== 'group') return;
//Bone
var bone = {}
bone.part = g.name
bone.invertAxis = 'xy'
bone.translate = g.origin.slice()
var bone = {
part: g.name,
invertAxis: 'xy',
translate: g.origin.slice()
}
bone.translate[1] *= -1
bone.translate[2] *= -1
if (g.rotation.join('_') !== '0_0_0') {
if (!g.rotation.allEqual(0)) {
bone.rotate = g.rotation.slice()
}
if (g.mirror_uv) {
bone.mirrorTexture = 'u'
}
//Cubes
if (g.children && g.children.length) {
bone.boxes = []
var mirrored_boxes = []
function iterate(arr) {
var i = 0;
while (i < arr.length) {
if (arr[i].type === 'group') {
iterate(arr[i].children)
} else if (arr[i].type === 'cube') {
var s = arr[i]
if (s !== undefined && s.export !== false) {
var cube = new oneLiner()
var c_pos = s.from.slice()
var c_size = s.size()
cube.coordinates = [
c_pos[0],//b_translate[0] - c_pos[0] - c_size[0],
c_pos[1],//b_translate[1] - c_pos[1] - c_size[1],
c_pos[2],
c_size[0],
c_size[1],
c_size[2]
]
function populate(p_model, group) {
cube.textureOffset = s.uv_offset
if (s.inflate && typeof s.inflate === 'number') {
cube.sizeAdd = s.inflate
}
if (s.mirror_uv === g.mirror_uv) {
bone.boxes.push(cube)
} else {
mirrored_boxes.push(cube)
}
}
if (group.children.length === 0) return;
var mirror_sub;
group.children.forEach(obj => {
if (!obj.export) return;
if (obj.type === 'cube') {
var box = new oneLiner()
var c_size = obj.size()
box.coordinates = [
obj.from[0],
obj.from[1],
obj.from[2],
c_size[0],
c_size[1],
c_size[2]
]
if (p_model && p_model.part === undefined) {
box.coordinates[0] -= p_model.translate[0];
box.coordinates[1] -= p_model.translate[1];
box.coordinates[2] -= p_model.translate[2];
}
i++;
}
}
iterate(g.children)
if (mirrored_boxes.length) {
bone.submodel = {
invertAxis: 'xy',
boxes: mirrored_boxes
}
if (g.shade !== false) {
bone.submodel.mirrorTexture = 'u'
}
}
box.textureOffset = obj.uv_offset
if (obj.inflate && typeof obj.inflate === 'number') {
box.sizeAdd = obj.inflate
}
if (obj.mirror_uv !== group.mirror_uv) {
if (!mirror_sub) {
mirror_sub = {
invertAxis: 'xy',
mirrorTexture: 'u',//xxx
boxes: []
}
if (!p_model.submodels) p_model.submodels = [];
p_model.submodels.splice(0, 0, mirror_sub)
}
mirror_sub.boxes.push(box)
} else {
if (!p_model.boxes) p_model.boxes = []
p_model.boxes.push(box)
}
} else if (obj.type === 'group') {
var bone = {
invertAxis: 'xy',
translate: obj.origin.slice()
}
if (obj.mirror_uv) {
bone.mirrorTexture = 'u'
}
if (!obj.rotation.allEqual(0)) {
bone.rotate = obj.rotation.slice()
bone.rotate[2] *= -1
}
populate(bone, obj)
if (!p_model.submodels) p_model.submodels = [];
p_model.submodels.push(bone)
}
})
}
models.push(bone)
populate(bone, g)
entitymodel.models.push(bone)
})
entitymodel.models = models
if (options.raw) {
return entitymodel
@ -1597,7 +1799,7 @@ BARS.defineActions(function() {
keybind: new Keybind({key: 79, ctrl: true}),
click: function () {
Blockbench.import({
extensions: ['json', 'jem', 'jpm'],
extensions: ['json', 'jem', 'jpm', 'bbmodel'],
type: 'JSON Model'
}, function(files) {
if (isApp) {
@ -1663,6 +1865,21 @@ BARS.defineActions(function() {
})
}
})
new Action({
id: 'export_bbmodel',
icon: 'insert_drive_file',
category: 'file',
click: function () {
Blockbench.export({
type: 'Blockbench Save',
extensions: ['bbmodel'],
name: Project.name||'model',
startpath: Prop.file_path,
project_file: true,
content: buildBBModel()
})
}
})
new Action({
id: 'export_entity',
icon: 'pets',
@ -1748,4 +1965,35 @@ BARS.defineActions(function() {
keybind: new Keybind({key: 83, ctrl: true}),
click: function () {saveFile();saveTextures();}
})
new Action({
id: 'export_asset_archive',
icon: 'archive',
category: 'file',
click: function() {
var archive = new JSZip();
if (Blockbench.entity_mode === false) {
var content = buildBlockModel()
} else {
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});
}
})
archive.generateAsync({type: 'blob'}).then(content => {
Blockbench.export({
type: 'Zip Archive',
extensions: ['zip'],
name: 'assets',
startpath: Prop.file_path,
content: content,
savetype: 'zip',
project_file: true
})
})
}
})
})

View File

@ -143,12 +143,16 @@ class Keybind {
}
record() {
var scope = this;
Keybinds.recording = true;
var overlay = $('<div id="overlay_message_box" contenteditable="true"></div>')
$('body').append(overlay)
overlay.focus()
overlay.on('keyup mousedown', function(event) {
overlay.off('keyup mousedown')
Keybinds.recording = this;
var overlay = $('#overlay_message_box').show()
var input = overlay.find('#keybind_input_box')
var top = limitNumber($(window).height()/2 - 200, 30, 800)
overlay.find('> div').css('margin-top', top+'px')
function onActivate(event) {
if (event.target && event.target.tagName === 'BUTTON') return;
scope.key = event.which
if (scope.ctrl !== null) scope.ctrl = event.ctrlKey
if (scope.shift !== null) scope.shift = event.shiftKey
@ -156,13 +160,26 @@ class Keybind {
if (scope.meta !== null) scope.meta = event.metaKey
scope.label = scope.getText()
scope.save(true)
Keybinds.recording = false
overlay.detach().hide()
}).on('keydown keypress keyup click click dblclick mouseup', function(event) {
Blockbench.showQuickMessage(scope.label)
scope.stopRecording()
}
input.focus().on('keyup', onActivate)
overlay.on('mousedown', onActivate)
overlay.on('keydown keypress keyup click click dblclick mouseup', function(event) {
event.preventDefault()
})
return this;
}
stopRecording() {
var scope = this;
Keybinds.recording = false
$('#overlay_message_box').hide().off('mousedown')
$('#keybind_input_box').off('keyup')
return this;
}
}
onVueSetup(function() {

View File

@ -71,6 +71,8 @@ class BBPainter {
}
startBrushCanvas(data, event) {
Painter.current.x = Painter.current.y = 0
Painter.current.face = data.face;
Painter.current.cube = data.cube;
var texture = data.cube.faces[data.face].getTexture()
if (!texture) {
Blockbench.showQuickMessage('message.untextured')
@ -205,8 +207,7 @@ class BBPainter {
ctx.fillStyle = BarItems.brush_color.get().toRgbString()
var fill_mode = BarItems.fill_mode.get()
var cube = selected[0]
var cube = Painter.current.cube;
if (cube && fill_mode === 'cube') {
for (var face in cube.faces) {
var tag = cube.faces[face]
@ -265,10 +266,7 @@ class BBPainter {
px[3] = result_color.a*255
}
})
}
} else {
ctx.clip()
@ -282,7 +280,6 @@ class BBPainter {
return {r: pxcolor.r, g: pxcolor.g, b: pxcolor.b, a: pxcolor.a*(1-b_opacity*opacity*(noise?Math.random():1))};
})
}
ctx.restore();
}
Painter.editing_area = undefined;
@ -561,7 +558,12 @@ class BBPainter {
return texture.add(false);
}
if (options.entity_template === true) {
Undo.initEdit({textures: Blockbench.entity_mode ? textures : [], cubes: Blockbench.entity_mode ? elements : selected, uv_only: true})
Undo.initEdit({
textures: Blockbench.entity_mode ? textures : [],
cubes: Blockbench.entity_mode ? elements : selected,
uv_only: true,
resolution: true
})
Painter.generateTemplate(options, makeTexture)
} else {
Undo.initEdit({textures: []})

View File

@ -110,6 +110,7 @@ class Preview {
this.camOrtho.updateProjectionMatrix();
}
this.renderer.setSize(this.width, this.height);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.updateBackground()
return this;
}
@ -297,6 +298,7 @@ class Preview {
//Controls
click(event) {
$(':focus').blur()
display_mode
this.static_rclick = event.button === 2
quad_previews.current = this;
if (Transformer.hoverAxis !== null || !Keybinds.extra.preview_select.keybind.isTriggered(event)) return;
@ -310,7 +312,7 @@ class Preview {
main_uv.setFace(data.face, false)
}
Blockbench.dispatchEvent( 'canvas_select', data )
if (Animator.open || (Toolbox.selected.id === 'rotate_tool' && Blockbench.entity_mode)) {
if (Animator.open || (Blockbench.entity_mode && ['rotate_tool', 'pivot_tool'].includes(Toolbox.selected.id))) {
if (data.cube.parent.type === 'group') {
data.cube.parent.select()
}
@ -357,7 +359,7 @@ class Preview {
showContextMenu(event) {
if (this.static_rclick && event.which === 3) {
var data = this.raycast()
if (data && data.cube) {
if (Toolbox.selected.selectCubes && Modes.selected.selectCubes && data && data.cube) {
data.cube.showContextMenu(event)
} else {
this.menu.open(event, this)
@ -560,7 +562,7 @@ class Preview {
var scope = this;
var dialog = new Dialog({
id: 'background_position',
title: 'message.set_background_position.title',
title: tl('message.set_background_position.title'),
lines: [
`<div class="dialog_bar">
<input type="number" class="dark_bordered" value="${scope.background.x}" id="background_pos_x">
@ -611,33 +613,40 @@ class Preview {
if (display_mode && ground_animation) {
ground_animation = false
}
this.render()
setTimeout(function() {
var dataUrl = scope.canvas.toDataURL()
var dataUrl = scope.canvas.toDataURL()
dataUrl = dataUrl.replace('data:image/png;base64,','')
Jimp.read(Buffer.from(dataUrl, 'base64')).then(function(image) {
if (display_mode && display_slot === 'gui') {
var zoom = display_preview.camOrtho.zoom * devicePixelRatio
var resolution = 256 * zoom;
dataUrl = dataUrl.replace('data:image/png;base64,','')
Jimp.read(Buffer.from(dataUrl, 'base64')).then(function(image) {
var start_x = display_preview.width *devicePixelRatio/2 - display_preview.controls.target.x*zoom*40 - resolution/2;
var start_y = display_preview.height*devicePixelRatio/2 + display_preview.controls.target.y*zoom*40 - resolution/2;
image.crop(start_x, start_y, resolution, resolution)
} else {
image.autocrop([0, false])
if (options && options.width && options.height) {
image.contain(options.width, options.height)
}
image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
Screencam.returnScreenshot(dataUrl, cb)
})
});
editVis(obj => {
obj.visible = obj.was_visible
delete obj.was_visible
})
if (display_mode && ground_anim_before) {
ground_animation = ground_anim_before
}
}, 40)
image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
Screencam.returnScreenshot(dataUrl, cb)
})
});
editVis(obj => {
obj.visible = obj.was_visible
delete obj.was_visible
})
if (display_mode && ground_anim_before) {
ground_animation = ground_anim_before
}
}
fullscreen() {
quad_previews.current = this;
@ -735,7 +744,6 @@ class Preview {
}}
])
function openQuadView() {
quad_previews.enabled = true;
@ -760,7 +768,6 @@ function openQuadView() {
updateInterface()
}
//Init/Update
function initCanvas() {
@ -955,6 +962,7 @@ function initCanvas() {
resizeWindow()
}
function animate() {
TickUpdates.Run()
requestAnimationFrame( animate );
previews.forEach(function(prev) {
prev.render()
@ -1092,7 +1100,7 @@ function buildGrid() {
function centerTransformer(offset) {
if (selected.length === 0) return;
var rotate_tool = Toolbox.selected.transformerMode === 'rotate'
var pivot_center = Toolbox.selected.id === 'rotate_tool' || Toolbox.selected.id === 'pivot_tool'
if (Animator.open && selected_group) {
var g_mesh = selected_group.mesh
@ -1110,7 +1118,7 @@ function centerTransformer(offset) {
if (Blockbench.entity_mode) {
Canvas.updateAllBones()
}
if (!rotate_tool) {
if (!pivot_center) {
var first_obj;
var center = getSelectionCenter()
for (var i = 0; i < selected.length && !first_obj; i++) {
@ -1133,13 +1141,13 @@ function centerTransformer(offset) {
Transformer.position.copy(vec)
var mesh = first_obj.mesh
if (mesh && Blockbench.globalMovement === false && !rotate_tool) {
if (mesh && Blockbench.globalMovement === false && !pivot_center) {
Transformer.rotation.copy(mesh.rotation)
}
} else {
//Entity Mode
if (selected_group && rotate_tool) {
if (selected_group && pivot_center) {
var mesh = selected_group.mesh
if (mesh) {
mesh.getWorldPosition(Transformer.position)
@ -1177,7 +1185,7 @@ function centerTransformer(offset) {
vec.z += group.origin[2]
}
Transformer.position.copy(vec)
if (Blockbench.globalMovement === false && !rotate_tool) {
if (Blockbench.globalMovement === false && !pivot_center) {
var rotation = new THREE.Quaternion()
first_obj.mesh.getWorldQuaternion(rotation)
Transformer.rotation.setFromQuaternion( rotation )
@ -1482,18 +1490,19 @@ class CanvasController {
if (!mesh || mesh > 0) mesh = obj.mesh
function setSize(geo) {
if (Blockbench.entity_mode && obj.inflate !== undefined) {
var inflate = obj.inflate
geo.from([ obj.from[0]-inflate, obj.from[1]-inflate, obj.from[2]-inflate ])
geo.to( [ obj.to[0] +inflate, obj.to[1] +inflate, obj.to[2] +inflate ])
} else {
geo.from(obj.from)
geo.to(obj.to)
var from = obj.from.slice()
from.forEach((v, i) => {
from[i] -= obj.inflate
})
var to = obj.to.slice()
to.forEach((v, i) => {
to[i] += obj.inflate
if (from[i] === to[i]) {
to[i] += 0.001
}
}
setSize(mesh.geometry)
})
mesh.geometry.from(from)
mesh.geometry.to(to)
mesh.geometry.computeBoundingSphere()
mesh.scale.set(1, 1, 1)
@ -1684,7 +1693,7 @@ class CanvasController {
frame = 0
if (obj[face].texture && obj[face].texture !== null) {
var tex = obj[face].getTexture()
if (typeof tex === 'object' && tex.constructor.name === 'Texture' && tex.frameCount) {
if (tex instanceof Texture && tex.frameCount !== 1) {
stretch = tex.frameCount
if (animation === true && tex.currentFrame) {
frame = tex.currentFrame

View File

@ -318,6 +318,13 @@ onVueSetup(function() {
})
var project_vue = new Vue({
el: '#project_settings',
data: {Project}
data: {Project},
methods: {
syncGeometry: function() {
if (Blockbench.entity_mode && Project.name.length > 0 && !Project.geometry_name) {
Project.geometry_name = Project.name.toLowerCase().replace(/\s/g, '')
}
}
}
})
})

View File

@ -9,7 +9,7 @@ class Texture {
this.particle = false
this.selected = false
this.error = false;
this.frameCount = 1
this.ratio = 1
this.show_icon = true
this.average_color = {r:0, g:0, b:0}
this.dark_box = false
@ -43,6 +43,11 @@ class Texture {
}
}
}
get frameCount() {
if (this.ratio !== 1) {
return 1/this.ratio
}
}
getUndoCopy(bitmap) {
var copy = {
path: this.path,
@ -57,17 +62,28 @@ class Texture {
old_width: this.old_width,
old_height: this.old_height
}
if (bitmap || this.mode === 'link') {
if (bitmap || this.mode === 'bitmap') {
copy.source = this.source
}
return copy
}
extend(properties) {
extend(data) {
Merge.string(this, data, 'path')
Merge.string(this, data, 'name')
Merge.string(this, data, 'folder')
Merge.string(this, data, 'namespace')
Merge.boolean(this, data, 'particle')
Merge.string(this, data, 'mode')
Merge.boolean(this, data, 'saved')
if (this.mode === 'bitmap') {
Merge.string(this, data, 'source')
}
/*
for (var key in properties) {
if (properties.hasOwnProperty(key)) {
this[key] = properties[key]
}
}
}*/
return this;
}
//Loading
@ -79,7 +95,6 @@ class Texture {
}
this.error = false;
this.show_icon = true
this.frameCount = 1
var img = this.img = new Image()
if (Canvas.materials[scope.uuid] !== undefined) {
@ -117,6 +132,7 @@ class Texture {
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')
@ -128,7 +144,6 @@ class Texture {
//Width / Animation
if (img.naturalWidth !== img.naturalHeight && Blockbench.entity_mode === false) {
if (img.naturalHeight % img.naturalWidth === 0) {
scope.frameCount = img.naturalHeight / img.naturalWidth
Canvas.updateAllUVs()
BARS.updateConditions()
} else {
@ -838,21 +853,10 @@ function saveTextures() {
}
})
}
function getSelectedTextureIndex() {
var index = false
textures.forEach(function(s, i) {
if (s.selected === true) {
index = i
}
})
return index;
}
function saveTextureMenu() {
hideDialog()
Undo.initEdit({textures})
index = getSelectedTextureIndex()
if (index === false) return;
var tex = textures[index]
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()
@ -873,20 +877,31 @@ function loadTextureDraggable() {
if (!t.hasClass('texture')) t = t.parent()
return t.find('.texture_icon_wrapper').clone().addClass('texture_drag_helper').attr('texid', t.attr('texid'))
},
cursorAt: { left: 24, top: 24 },
cursorAt: { left: 2, top: -5 },
revert: 'invalid',
appendTo: 'body',
zIndex: 19,
distance: 4,
drag: function(event, ui) {
$('.outliner_node[order]').attr('order', null)
var tar = $('#cubes_list li .drag_hover.outliner_node').deepest()
var element = TreeElements.findRecursive('uuid', tar.attr('id'))
if (element) {
tar.attr('order', '0')
}
},
stop: function(event, ui) {
setTimeout(function() {
if ($('canvas.preview:hover').length > 0) {
var data = Canvas.getCurrentPreview().raycast()
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({})
if (tex) {
data.cube.applyTexture(tex, [data.face])
}
Undo.finishEdit('apply texture')
}
}
}, 10)
@ -948,6 +963,76 @@ function getTexturesById(id) {
return $.grep(textures, function(e) {return e.id == id});
}
TextureAnimator = {
isPlaying: false,
interval: false,
start: function() {
clearInterval(TextureAnimator.interval)
TextureAnimator.isPlaying = true
TextureAnimator.updateButton()
TextureAnimator.interval = setInterval(TextureAnimator.nextFrame, 1000/settings.texture_fps.value)
},
stop: function() {
TextureAnimator.isPlaying = false
clearInterval(TextureAnimator.interval)
TextureAnimator.updateButton()
},
toggle: function() {
if (TextureAnimator.isPlaying) {
TextureAnimator.stop()
} else {
TextureAnimator.start()
}
},
updateSpeed: function() {
if (TextureAnimator.isPlaying) {
TextureAnimator.stop()
TextureAnimator.start()
}
},
nextFrame: function() {
var animated_tex = []
textures.forEach(function(tex, i) {
if (tex.frameCount > 1) {
if (tex.currentFrame === undefined) {
tex.currentFrame = 0
} else if (tex.currentFrame >= tex.frameCount-1) {
tex.currentFrame = 0
} else {
tex.currentFrame++;
}
$($('.texture').get(i)).find('img').css('margin-top', (tex.currentFrame*-48)+'px')
animated_tex.push(tex)
}
})
elements.forEach(function(obj) {
var update = false
for (var face in obj.faces) {
update = update || animated_tex.includes(obj.faces[face].getTexture());
}
if (update) {
Canvas.updateUV(obj, true)
}
})
},
reset: function() {
TextureAnimator.stop()
textures.forEach(function(tex, i) {
if (tex.frameCount) {
tex.currentFrame = 0
$($('.texture').get(i)).find('img').css('margin-top', '0')
}
})
while (i < elements.length) {
Canvas.updateUV(elements[i], true)
i++;
}
},
updateButton: function() {
BarItems.animated_textures.setIcon( TextureAnimator.isPlaying ? 'pause' : 'play_arrow' )
}
}
onVueSetup(function() {
texturelist = new Vue({
el: '#texture_list',
@ -1018,4 +1103,4 @@ BARS.defineActions(function() {
TextureAnimator.toggle()
}
})
})
})

View File

@ -646,7 +646,7 @@ BARS.defineActions(function() {
//Inflage
new NumSlider({
id: 'slider_inflate',
condition: function() {return Blockbench.entity_mode && selected.length && Modes.id === 'edit'},
condition: function() {return selected.length && Modes.id === 'edit'},
get: function() {
return selected[0].inflate
},

View File

@ -177,7 +177,7 @@ var Undo = {
Canvas.adaptObjectFaces(obj)
Canvas.updateUV(obj)
} else {
obj = new Cube(data, uuid).init(false)
obj = new Cube(data, uuid).init()
}
}
}
@ -269,10 +269,8 @@ var Undo = {
var animation = Animator.animations.findInArray('uuid', save.animation)
if (!animation) {
//new
animation = new Animation()
}
//populate
Animation.extend(save.animation)
} else if (reference.animation) {
@ -356,6 +354,13 @@ var Undo = {
display[slot].extend(data).update()
}
}
if (open_dialog == 'uv_dialog') {
for (var key in uv_dialog.editors) {
if (uv_dialog.editors[key]) {
uv_dialog.editors[key].loadData()
}
}
}
updateSelection()
}
}
@ -371,14 +376,18 @@ BARS.defineActions(function() {
id: 'undo',
icon: 'undo',
category: 'edit',
condition: () => (!open_dialog || open_dialog === 'uv_dialog'),
work_in_dialog: true,
keybind: new Keybind({key: 90, ctrl: true}),
click: function () {Undo.undo()}
click: Undo.undo
})
new Action({
id: 'redo',
icon: 'redo',
category: 'edit',
condition: () => (!open_dialog || open_dialog === 'uv_dialog'),
work_in_dialog: true,
keybind: new Keybind({key: 89, ctrl: true}),
click: function () {Undo.redo()}
click: Undo.redo
})
})

View File

@ -81,6 +81,23 @@ var asyncLoop = function(o){
}
async_loop();//init
}
// 1234567890qwertzuiopuasdfghjklyxcvbn
//Jquery
$.fn.deepest = function() {
if (!this.length) return this;
var opts = []
this.each((i, node) => {
var i = 0;
var obj = $(node)
while (obj.parent().get(0) instanceof HTMLBodyElement === false) {
obj = obj.parent()
i++;
}
opts.push({depth: i, o: node})
})
opts.sort((a, b) => (a.depth < b.depth));
return $(opts[0].o)
}
//Math
function guid() {
@ -92,6 +109,9 @@ function guid() {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
function isUUID(s) {
return (s.length === 36 && s.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/))
}
function bbuid(l) {
l = l || 1
let chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

View File

@ -226,6 +226,7 @@ class UVEditor {
scope.displayMappingOverlay()
})
this.jquery.size.mouseleave(function() {
console.trace('RM')
$(this).find('.uv_mapping_overlay').remove()
})
@ -284,6 +285,9 @@ class UVEditor {
scope.updateDragHandle(ui.position)
if (Blockbench.entity_mode) {
scope.displayAllMappingOverlays()
if (scope.jquery.size.is(':hover')) {
scope.displayMappingOverlay()
}
}
}
})
@ -1137,7 +1141,7 @@ class UVEditor {
return;
}
var tag = selected[0].faces[face]
var new_tag = new Face().extend(tag)
var new_tag = new Face(face, tag)
uv_dialog.clipboard.push(new_tag)
}
if (event.shiftKey) {
@ -1180,7 +1184,7 @@ class UVEditor {
applyFace(uv_dialog.clipboard[0], main_uv.face)
} else {
uv_dialog.clipboard.forEach(function(s) {
applyFace(s)
applyFace(s, s.direction)
})
}
}
@ -1556,19 +1560,25 @@ const uv_dialog = {
function addToClipboard(face) {
var tag = selected[0].faces[face]
uv_dialog.clipboard.push(new Face(tag))
uv_dialog.clipboard.push(new Face(null, tag))
}
if (uv_dialog.hoveredSide) {
addToClipboard(uv_dialog.hoveredSide)
uv_dialog.editors[uv_dialog.hoveredSide].message('uv_editor.copied')
} else if (uv_dialog.single) {
addToClipboard(uv_dialog.editors.single.face)
uv_dialog.editors.single.message('uv_editor.copied')
} else if (uv_dialog.selection.length > 0) {
uv_dialog.selection.forEach(function(s) {
addToClipboard(s)
uv_dialog.editors[s].message('uv_editor.copied')
})
} else {
uv_dialog.allFaces.forEach(function(s) {
addToClipboard(s)
uv_dialog.editors[s].message('uv_editor.copied')
})
}
},
@ -1588,15 +1598,22 @@ const uv_dialog = {
} else if (uv_dialog.selection.length === 1) {
applyFace(uv_dialog.clipboard[0], uv_dialog.selection[0])
if (uv_dialog.single) {
uv_dialog.editors.single.message('uv_editor.pasted')
} else {
uv_dialog.editors[uv_dialog.selection[0]].message('uv_editor.pasted')
}
} else {
if (uv_dialog.clipboard.length === 1) {
uv_dialog.selection.forEach(function(s) {
applyFace(uv_dialog.clipboard[0], s)
uv_dialog.editors[s].message('uv_editor.pasted')
})
} else {
uv_dialog.clipboard.forEach(function(s) {
if (uv_dialog.selection.includes(s.face)) {
applyFace(s)
uv_dialog.editors[s].message('uv_editor.pasted')
}
})
}
@ -1717,9 +1734,7 @@ BARS.defineActions(function() {
category: 'uv',
condition: () => !Blockbench.entity_mode && selected.length,
click: function (event) {
Undo.initEdit({cubes: selected, uv_only: true})
uv_dialog.forSelection('clear', event)
Undo.finishEdit('remove face')
}
})
new Action({

View File

@ -836,5 +836,23 @@
"action.toggle_uv_overlay.desc": "Blendet UV Masken für alle Elemente über der Textur ein",
"menu.texture.blank": "Auf leere Flächen anwenden",
"dialog.scale.select_overflow": "Überstehendes auswählen",
"dialog.create_texture.compress": "Template verdichten"
"dialog.create_texture.compress": "Template verdichten",
"action.action_control": "Kommandozentrale",
"action.action_control.desc": "Suche und benutze jede verfügbare Aktion",
"keybindings.recording": "Tastenkombination wird aufgezeichnet",
"keybindings.press": "Drücke eine Taste bzw. Tastenkombination oder klicke auf den Bildschirm, um eine Tastenkombination aufzuzeichnen.",
"action.pivot_tool": "Drehpunktwerkzeug",
"action.pivot_tool.desc": "Werkzeug zum Bearbeiten des Drehpunktes",
"action.slider_animation_speed": "Wiedergabegeschwindigkeit",
"action.slider_animation_speed.desc": "Wiedergabegeschwindigkeit der Timeline in Prozent",
"action.previous_keyframe": "Vorheriger Keyframe",
"action.previous_keyframe.desc": "Springe zum vorherigen Keyframe",
"action.next_keyframe": "Nächster Keyframe",
"action.next_keyframe.desc": "Springe zum nächsten Keyframe",
"message.outdated_client.title": "Version veraltet",
"message.outdated_client.message": "Bitte update auf die neuste Version von Blockbench",
"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"
}

View File

@ -80,6 +80,8 @@
"message.invalid_file.message": "Could not open json file: %0",
"message.invalid_model.title": "Invalid Model File",
"message.invalid_model.message": "This file does not contain valid model data.",
"message.outdated_client.title": "Outdated client",
"message.outdated_client.message": "Please update to the latest version of Blockbench to do this.",
"message.child_model_only.title": "Empty Child Model",
"message.child_model_only.message": "This file is a child of %0 and does not contain a model.",
"message.drag_background.title": "Position Background",
@ -263,6 +265,8 @@
"keybindings.reset": "Reset",
"keybindings.clear": "Empty",
"keybindings.recording": "Recording Keybinding",
"keybindings.press": "Press a key or key combination or click anywhere on the screen to record your keybinding.",
"layout.color.back": "Back",
"layout.color.back.desc": "Backgrounds and input fields",
@ -482,6 +486,8 @@
"action.resize_tool.desc": "Tool to select and resize elements",
"action.rotate_tool": "Rotate",
"action.rotate_tool.desc": "Tool to select and rotate elements",
"action.pivot_tool": "Pivot Tool",
"action.pivot_tool.desc": "Tool to change the pivot point of cubes and bones",
"action.brush_tool": "Paint Brush",
"action.brush_tool.desc": "Tool to paint on bitmap textures on surfaces or the UV editor",
"action.fill_tool": "Paint Bucket",
@ -514,6 +520,10 @@
"action.extrude_texture.desc": "Generate a model by stretching out a texture",
"action.export_blockmodel": "Export Blockmodel",
"action.export_blockmodel.desc": "Export a Minecraft block or item model",
"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_entity": "Export Bedrock Entity",
"action.export_entity.desc": "Add the current model as an entity to a mobs.json file",
"action.export_class_entity": "Export Java Entity",
@ -534,6 +544,8 @@
"action.update_window.desc": "Search for Blockbench updates.",
"action.donate": "Donate",
"action.donate.desc": "Donate to Blockbench",
"action.action_control": "Action Control",
"action.action_control.desc": "Search and execute any available action",
"action.reset_keybindings": "Reset Keybindings",
"action.reset_keybindings.desc": "Reset all keybindings to Blockbench's defaults",
@ -729,7 +741,7 @@
"action.add_animation.desc": "Create a blank animation",
"action.load_animation_file": "Import Animations",
"action.load_animation_file.desc": "Import an animation file",
"action.play_animation": "Play Animations",
"action.play_animation": "Play Animation",
"action.play_animation.desc": "Preview the selected animation",
"action.export_animation_file": "Export Animations",
"action.export_animation_file.desc": "Export a json file with the current animations",
@ -741,7 +753,12 @@
"action.select_all_keyframes.desc": "Select all keyframes of the current bone",
"action.delete_keyframes": "Delete Keyframes",
"action.delete_keyframes.desc": "Delete all selected keyframes",
"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",
"timeline.rotation": "Rotation",
"timeline.position": "Position",

View File

@ -821,20 +821,38 @@
"action.open_backup_folder.desc": "Abre la carpeta de backups de Blockbench",
"switches.mirror": "Invertir UV",
"language_name": "Inglés",
"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",
"action.fill_mode.face": "Face",
"message.plugin_reload": "Recargados %0 plugins locales",
"settings.brightness": "Brillo",
"settings.brightness.desc": "Brillo de la previsualización. Por defecto es 50",
"menu.preview.perspective.reset": "Resetear Cámara",
"action.fill_mode": "Modo de Llenado",
"action.fill_mode.desc": "Modo de la herramienta de llenado",
"action.fill_mode.face": "Cara",
"action.fill_mode.color": "Color",
"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.fill_mode.cube": "Cubo",
"action.toggle_mirror_uv": "Invertir UV",
"action.toggle_mirror_uv.desc": "Activa el invertido de UV en el eje X de los cubos seleccionados",
"action.toggle_uv_overlay": "Cambiar Superposición de UV",
"action.toggle_uv_overlay.desc": "Cuando está activado, muestra las superposiciones de UV sobre la textura",
"menu.texture.blank": "Aplicar a Caras sin Textura",
"dialog.scale.select_overflow": "Seleccionar Overflow",
"dialog.create_texture.compress": "Comprimir Plantilla",
"action.action_control": "Control de Acción",
"action.action_control.desc": "Busca y ejecuta cualquier acción disponible",
"keybindings.recording": "Anotando Atajo de Teclado",
"keybindings.press": "Pulsa una tecla o una combinación de teclas o haz click en cualquier lugar de tu pantalla para anotar el atajo de teclado",
"action.pivot_tool": "Herramienta de Pivote",
"action.pivot_tool.desc": "Herramienta para cambiar el punto de pivote de cubos y huesos",
"action.slider_animation_speed": "Velocidad de Playback",
"action.slider_animation_speed.desc": "Velocidad de playback del línea de tiempo en porcentaje",
"action.previous_keyframe": "Frame Anterior",
"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"
}

View File

@ -836,5 +836,23 @@
"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"
"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"
}

View File

@ -822,19 +822,37 @@
"switches.mirror": "ミラーUV",
"language_name": "English",
"message.plugin_reload": "Reloaded %0 local plugins",
"settings.brightness": "Brightness",
"settings.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",
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Color",
"action.fill_mode.cube": "Cube",
"action.toggle_mirror_uv": "Mirror UV",
"menu.preview.perspective.reset": "カメラをリセット",
"action.fill_mode": "塗りつぶし",
"action.fill_mode.desc": "塗りつぶしモード",
"action.fill_mode.face": "",
"action.fill_mode.color": "カラー",
"action.fill_mode.cube": "キューブ",
"action.toggle_mirror_uv": "ミラー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",
"menu.texture.blank": "テクスチャのない面に適用",
"dialog.scale.select_overflow": "Select Overflow",
"dialog.create_texture.compress": "Compress Template"
"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"
}

View File

@ -836,5 +836,23 @@
"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"
"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"
}

View File

@ -836,5 +836,23 @@
"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"
"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"
}

View File

@ -815,26 +815,44 @@
"action.fill_tool.desc": "Paint bucket to fill entire faces with one color",
"action.eraser": "Ластик",
"action.eraser.desc": "Eraser tool to replace colors on a texture with transparency",
"action.color_picker": "Color Picker",
"action.color_picker": "Выбор цвета",
"action.color_picker.desc": "Tool to pick the color of pixels on your texture",
"action.open_backup_folder": "Открыть папку автосохранений",
"action.open_backup_folder.desc": "Opens the Blockbench backup folder",
"switches.mirror": "Mirror UV",
"language_name": "English",
"message.plugin_reload": "Reloaded %0 local plugins",
"settings.brightness": "Brightness",
"language_name": "Английский",
"message.plugin_reload": "Перезагружено %0 локальных плагинов",
"settings.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": "Режим заполнения",
"action.fill_mode.desc": "Mode of the fill tool",
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Color",
"action.fill_mode.cube": "Cube",
"action.fill_mode.face": "Грань",
"action.fill_mode.color": "Цвет",
"action.fill_mode.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"
"dialog.create_texture.compress": "Сжать шаблон",
"action.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": "Скорость воспроизведения",
"action.slider_animation_speed.desc": "Playback speed of the timeline in percent",
"action.previous_keyframe": "Предыдущий кадр",
"action.previous_keyframe.desc": "Jump to the previous keyframe",
"action.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"
}

View File

@ -836,5 +836,23 @@
"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"
"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"
}

View File

@ -1,5 +1,5 @@
{
"dialog.ok": "确",
"dialog.ok": "确",
"dialog.cancel": "取消",
"dialog.confirm": "确认",
"dialog.close": "关闭",
@ -228,8 +228,8 @@
"settings.shading.desc": "启用阴影",
"settings.transparency": "透明度",
"settings.transparency.desc": "渲染贴图的透明部分",
"settings.texture_fps": "动贴图 FPS",
"settings.texture_fps.desc": "动贴图的帧数",
"settings.texture_fps": "动贴图 FPS",
"settings.texture_fps.desc": "动贴图的帧数",
"settings.base_grid": "小网格",
"settings.base_grid.desc": "显示小网格和轴",
"settings.large_grid": "大网格",
@ -516,7 +516,7 @@
"menu.edit": "编辑",
"menu.transform": "模型控制",
"menu.filter": "插件目录",
"menu.display": "物品显示",
"menu.display": "显示",
"menu.view": "视图",
"menu.file.new": "新建",
"menu.file.recent": "最近",
@ -590,12 +590,12 @@
"uv_editor.title": "UV 编辑器",
"uv_editor.all_faces": "全部",
"uv_editor.no_faces": "无",
"face.north": "北/-X面",
"face.south": "南/X面",
"face.west": "西/-Y面",
"face.east": "东/Y面",
"face.up": "上/Z面",
"face.down": "下/-Z面",
"face.north": "北面/-x",
"face.south": "南面/+x",
"face.west": "西面/-y",
"face.east": "东面/+y",
"face.up": "顶面/+z",
"face.down": "底面/-z",
"direction.north": "北",
"direction.south": "南",
"direction.west": "西",
@ -606,7 +606,7 @@
"display.slot.third_left": "第三人称左手",
"display.slot.first_right": "第一人称右手",
"display.slot.first_left": "第一人称左手",
"display.slot.head": "头",
"display.slot.head": "头",
"display.slot.ground": "地面",
"display.slot.frame": "展示框",
"display.slot.gui": "GUI",
@ -642,7 +642,7 @@
"dialog.update.installed": "已安装的版本",
"dialog.update.update": "更新",
"action.brush_mode.brush": "笔刷",
"action.brush_mode.noise": "像素点",
"action.brush_mode.noise": "点",
"action.vertex_snap_mode.move": "移动",
"action.vertex_snap_mode.scale": "规模",
"action.open_model_folder": "打开模型文件夹",
@ -702,13 +702,13 @@
"uv_editor.maximized": "最大化",
"uv_editor.autouv": "尺寸自动",
"uv_editor.mirrored": "镜像",
"uv_editor.to_all": "适用于所有面",
"uv_editor.to_all": "适用于所有面",
"uv_editor.transparent": "设置透明度",
"uv_editor.cullface_on": "开启面剔除",
"uv_editor.cullface_off": "关闭面剔除",
"uv_editor.tint_on": "开启着色",
"uv_editor.tint_off": "关闭着色",
"action.uv_apply_all": "适用于所有面",
"action.uv_apply_all": "适用于所有面",
"action.uv_apply_all.desc": "将当前面的设置应用于所有面",
"message.convert_mode.title": "模型转换",
"message.convert_mode.message": "您确定要将此类型转换为 %0 吗?您无法撤消此步骤",
@ -737,13 +737,13 @@
"action.move_back.desc": "相对于当前摄像机角度向后移动选定的立方体",
"layout.color.wireframe": "线框",
"layout.color.wireframe.desc": "线框模式中的线",
"action.add_animation": "添加动",
"action.add_animation": "添加动",
"action.add_animation.desc": "创建一个空白动画",
"action.load_animation_file": "导入动文件",
"action.load_animation_file": "导入动文件",
"action.load_animation_file.desc": "导入动画文件",
"action.play_animation": "播放动",
"action.play_animation": "播放动",
"action.play_animation.desc": "预览所选的动画",
"action.export_animation_file": "导出动文件",
"action.export_animation_file": "导出动文件",
"action.export_animation_file.desc": "导出当前动画的JSON文件",
"action.slider_keyframe_time": "时间码",
"action.slider_keyframe_time.desc": "修改选定关键帧的时间码",
@ -752,7 +752,7 @@
"timeline.scale": "放大",
"menu.timeline.add": "添加关键帧",
"menu.keyframe.quaternion": "四元数",
"panel.animations": "动",
"panel.animations": "动",
"panel.keyframe": "关键帧",
"panel.keyframe.type": "关键帧(%0",
"generic.delete": "删除",
@ -772,7 +772,7 @@
"menu.animation.override": "覆盖",
"menu.animation.anim_time_update": "更新变量",
"message.display_skin_model.title": "皮肤模型",
"message.display_skin_model.message": "选择你的皮肤模型类型",
"message.display_skin_model.message": "选择皮肤模型类型",
"message.display_skin_model.classic": "经典\nSteve",
"message.display_skin_model.slim": "瘦小\nAlex",
"message.bone_material": "改变骨骼材料",
@ -820,21 +820,39 @@
"action.open_backup_folder": "打开备份文件夹",
"action.open_backup_folder.desc": "打开Blockbench备份文件夹",
"switches.mirror": "镜像 UV",
"language_name": "中文",
"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",
"action.fill_mode.face": "Face",
"action.fill_mode.color": "Color",
"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"
"language_name": "简体中文",
"message.plugin_reload": "重新加载 %0 个本地插件",
"settings.brightness": "亮度",
"settings.brightness.desc": "预览的亮度,默认值为50",
"menu.preview.perspective.reset": "重置相机位置",
"action.fill_mode": "填充模式",
"action.fill_mode.desc": "填充工具的模式",
"action.fill_mode.face": "面",
"action.fill_mode.color": "颜色",
"action.fill_mode.cube": "立方体",
"action.toggle_mirror_uv": "镜面 UV",
"action.toggle_mirror_uv.desc": "在所选立方体的X轴上切换UV镜像",
"action.toggle_uv_overlay": "切换UV覆盖",
"action.toggle_uv_overlay.desc": "启用后将在纹理上方显示所有UV贴图叠加层",
"menu.texture.blank": "适用于无纹理面",
"dialog.scale.select_overflow": "选择溢出",
"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"
}

15
lib/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@ const url = require('url')
let orig_win;
function createWindow() {
if (!app.requestSingleInstanceLock()) {
if (app.requestSingleInstanceLock && !app.requestSingleInstanceLock()) {
return;
}
let win = new BrowserWindow({
@ -83,7 +83,7 @@ app.on('window-all-closed', () => {
})
app.on('activate', () => {
if (win === null) {
/*if (win === null) {
createWindow()
}
}*/
})

View File

@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Minecraft Block Model Editor",
"version": "2.4.0",
"version": "2.5.0",
"license": "MIT",
"author": {
"name": "JannisX11",
@ -77,7 +77,7 @@
},
"devDependencies": {
"async": "^2.4.1",
"electron": "4.0.1",
"electron": "4.0.3",
"electron-builder": "^20.38.4"
}
}