@@ -1109,7 +1091,7 @@ mode.start.new
diff --git a/js/animations.js b/js/animations.js
index 70bfe1574..bcad247a2 100644
--- a/js/animations.js
+++ b/js/animations.js
@@ -20,6 +20,9 @@ class Animation {
Merge.boolean(this, data, 'override')
Merge.string(this, data, 'anim_time_update')
Merge.number(this, data, 'length')
+ if (typeof data.length == 'number') {
+ this.setLength(this.length)
+ }
if (data.bones && !data.animators) {
data.animators = data.bones;
}
@@ -30,9 +33,9 @@ class Animation {
var ba = this.getBoneAnimator(group)
var kfs = data.animators[key]
if (kfs && ba) {
- ba.rotation.length = 0;
- ba.position.length = 0;
- ba.scale.length = 0;
+ ba.rotation.empty();
+ ba.position.empty();
+ ba.scale.empty();
kfs.forEach(kf_data => {
ba.addKeyframe(kf_data, kf_data.uuid);
})
@@ -87,6 +90,7 @@ class Animation {
Timeline.animators.purge();
Timeline.selected.empty();
Timeline.vue._data.markers = this.markers;
+ Timeline.vue._data.animation_length = this.length;
this.selected = true;
this.playing = true;
Animator.selected = this;
@@ -103,6 +107,14 @@ class Animation {
Animator.preview();
return this;
}
+ setLength(len) {
+ len = limitNumber(len, 0, 1e4)
+ this.length = len;
+ if (Animator.selected == this) {
+ Timeline.vue._data.animation_length = this.length;
+ BarItems.slider_animation_length.update()
+ }
+ }
createUniqueName(arr) {
var scope = this;
var others = Animator.animations;
@@ -217,7 +229,7 @@ class Animation {
i++;
}
}
- this.length = len
+ this.setLength(len)
if (this == Animator.selected) {
BarItems.slider_animation_length.update()
}
@@ -294,6 +306,7 @@ class GeneralAnimator {
})
}
select() {
+ if (this.getGroup().locked) return;
var scope = this;
TickUpdates.keyframes = true;
for (var key in Animator.selected.animators) {
@@ -339,7 +352,7 @@ class GeneralAnimator {
if (value) {
keyframe.extend(value);
} else if (this.fillValues) {
- this.fillValues(keyframe, value);
+ this.fillValues(keyframe, value, true);
}
keyframe.channel = channel;
keyframe.time = time;
@@ -468,7 +481,7 @@ class BoneAnimator extends GeneralAnimator {
}
return this;
}
- fillValues(keyframe, values) {
+ fillValues(keyframe, values, allow_expression) {
if (values instanceof Array) {
keyframe.extend({
@@ -486,7 +499,7 @@ class BoneAnimator extends GeneralAnimator {
z: values
})
} else if (values == null) {
- var ref = this.interpolate(Timeline.time, keyframe.channel, true)
+ var ref = this.interpolate(keyframe.channel, allow_expression)
if (ref) {
let e = 1e2
ref.forEach((r, i) => {
@@ -551,7 +564,8 @@ class BoneAnimator extends GeneralAnimator {
bone.scale.z *= arr[2] || 0.00001;
return this;
}
- interpolate(time, channel, allow_expression) {
+ interpolate(channel, allow_expression) {
+ let time = Timeline.time;
var i = 0;
var before = false
var after = false
@@ -607,13 +621,13 @@ class BoneAnimator extends GeneralAnimator {
}
return result
}
- displayFrame(time) {
+ displayFrame() {
if (!this.doRender()) return;
this.getGroup()
- if (!this.muted.rotation) this.displayRotation(this.interpolate(time, 'rotation'))
- if (!this.muted.position) this.displayPosition(this.interpolate(time, 'position'))
- if (!this.muted.scale) this.displayScale(this.interpolate(time, 'scale'))
+ if (!this.muted.rotation) this.displayRotation(this.interpolate('rotation'))
+ if (!this.muted.position) this.displayPosition(this.interpolate('position'))
+ if (!this.muted.scale) this.displayScale(this.interpolate('scale'))
}
}
BoneAnimator.prototype.channels = ['rotation', 'position', 'scale']
@@ -643,6 +657,7 @@ class EffectAnimator extends GeneralAnimator {
if (diff >= 0 && diff < (1/60) * (Timeline.playback_speed/100)) {
if (kf.file && !kf.cooldown) {
var media = new Audio(kf.file);
+ window._media = media
media.volume = Math.clamp(settings.volume.value/100, 0, 1);
media.play();
Timeline.playing_sounds.push(media);
@@ -810,9 +825,6 @@ class Keyframe {
this.get('y'),
this.get('z'),
]
- if (this.channel === 'rotation' && this.isQuaternion) {
- arr.push(this.get('w'))
- }
return arr;
}
getFixed() {
@@ -860,7 +872,7 @@ class Keyframe {
Timeline.selected.forEach(function(kf) {
kf.selected = false
})
- Timeline.selected.length = 0
+ Timeline.selected.empty()
}
if (event && event.shiftKey && Timeline.selected.length) {
var last = Timeline.selected[Timeline.selected.length-1]
@@ -1162,7 +1174,7 @@ const Animator = {
join() {
Animator.open = true;
- selected.length = 0
+ selected.empty()
Canvas.updateAllBones()
//if (quad_previews.enabled) {
@@ -1182,7 +1194,7 @@ const Animator = {
}
TickUpdates.keyframes = true;
if (outlines.children.length) {
- outlines.children.length = 0
+ outlines.children.empty()
Canvas.updateAllPositions()
}
if (!Animator.selected && Animator.animations.length) {
@@ -1222,7 +1234,7 @@ const Animator = {
Group.all.forEach(group => {
Animator.animations.forEach(animation => {
if (animation.playing) {
- animation.getBoneAnimator(group).displayFrame(Timeline.time)
+ animation.getBoneAnimator(group).displayFrame()
}
})
group.mesh.updateMatrixWorld()
@@ -1391,7 +1403,7 @@ const Animator = {
ani_tag.timeline[timecode] = kf.instructions.split('\n');
})
- } else if (a.animators[uuid].keyframes.length && a.animators[uuid].group) {
+ } else if (a.animators[uuid].keyframes.length && a.animators[uuid].getGroup()) {
var group = a.animators[uuid].group;
var bone_tag = ani_tag.bones[group.name] = {};
@@ -1413,6 +1425,12 @@ const Animator = {
let timecodes = Object.keys(channels[channel])
if (timecodes.length === 1) {
bone_tag[channel] = channels[channel][timecodes[0]]
+ if (channel == 'scale' &&
+ channels[channel][timecodes[0]] instanceof Array &&
+ channels[channel][timecodes[0]].allEqual(channels[channel][timecodes[0]][0])
+ ) {
+ bone_tag[channel] = channels[channel][timecodes[0]][0];
+ }
} else {
timecodes.sort().forEach((time) => {
if (!bone_tag[channel]) {
@@ -1442,6 +1460,7 @@ const Timeline = {
playback_speed: 100,
time: 0,
get second() {return Timeline.time},
+ get animation_length() {return Animator.selected ? Animator.selected.length : 0;},
playing: false,
selector: {
start: [0, 0],
@@ -1502,7 +1521,7 @@ const Timeline = {
var min_time = (rect.ax-Timeline.vue._data.head_width-8)/Timeline.vue._data.size;
var max_time = (rect.bx-Timeline.vue._data.head_width-8)/Timeline.vue._data.size;
- Timeline.selected.length = 0;
+ Timeline.selected.empty()
for (var animator of Timeline.animators) {
var node = $('#timeline_body_inner .animator[uuid=' + animator.uuid + ']').get(0)
var offset = node && node.offsetTop;
@@ -1562,10 +1581,14 @@ const Timeline = {
if (!editing) {
Timeline.setTimecode(seconds)
}
- Timeline.updateSize()
- //Scroll
+ if (Timeline.getMaxLength() !== Timeline.vue._data.length) {
+ Timeline.updateSize()
+ }
+ Timeline.revealTime(seconds)
+ },
+ revealTime(time) {
var scroll = $('#timeline_body').scrollLeft()
- var playhead = Timeline.time * Timeline.vue._data.size + 8
+ var playhead = time * Timeline.vue._data.size + 8
if (playhead < scroll || playhead > scroll + $('#timeline_body').width() - Timeline.vue._data.head_width) {
$('#timeline_body').scrollLeft(playhead-16)
}
@@ -1595,14 +1618,24 @@ const Timeline = {
$('#timeline_time').on('mousedown touchstart', e => {
if (e.which !== 1 && !event.changedTouches) return;
if (e.target.classList.contains('timeline_marker')) return;
- convertTouchEvent(e);
- Timeline.dragging_playhead = true;
- let time = (e.offsetX) / Timeline.vue._data.size
- Timeline.setTime(Timeline.snapTime(time))
- Animator.preview()
+
+ if (e.target.id == 'timeline_endbracket') {
+
+ Timeline.dragging_endbracket = true;
+ Undo.initEdit({animations: [Animator.selected]});
+
+ } else {
+
+ convertTouchEvent(e);
+ Timeline.dragging_playhead = true;
+ let time = (e.offsetX) / Timeline.vue._data.size
+ Timeline.setTime(Timeline.snapTime(time))
+ Animator.preview()
+ }
})
$(document).on('mousemove touchmove', e => {
if (Timeline.dragging_playhead) {
+
convertTouchEvent(e);
let offset = e.clientX - $('#timeline_time').offset().left;
let time = Timeline.snapTime(offset / Timeline.vue._data.size)
@@ -1610,12 +1643,26 @@ const Timeline = {
Timeline.setTime(time)
Animator.preview()
}
+ } else if (Timeline.dragging_endbracket) {
+
+ convertTouchEvent(e);
+ let offset = e.clientX - $('#timeline_time').offset().left;
+ let time = Timeline.snapTime(offset / Timeline.vue._data.size)
+
+ Animator.selected.setLength(time)
+ Timeline.revealTime(time)
+
}
})
.on('mouseup touchend', e => {
if (Timeline.dragging_playhead) {
delete Timeline.dragging_playhead
Timeline.pause();
+
+ } else if (Timeline.dragging_endbracket) {
+
+ Undo.finishEdit('Change Animation Length')
+ delete Timeline.dragging_endbracket
}
})
//Keyframe inputs
@@ -1679,16 +1726,33 @@ const Timeline = {
}
})
- $('#timeline_vue').on('mousewheel', function() {
+ $('#timeline_vue').on('mousewheel scroll', function(e) {
+ e.preventDefault()
var body = $('#timeline_body').get(0)
if (event.shiftKey) {
body.scrollLeft += event.deltaY/4
+
} else if (event.ctrlOrCmd) {
- var offset = 1 - event.deltaY/600
- Timeline.vue._data.size = limitNumber(Timeline.vue._data.size * offset, 10, 1000)
- body.scrollLeft *= offset
+
+ let mouse_pos = event.clientX - $(this).offset().left;
+
+ var zoom = 1 - event.deltaY/600
+ let original_size = Timeline.vue._data.size
+ Timeline.vue._data.size = limitNumber(Timeline.vue._data.size * zoom, 10, 1000)
+ //let val = ((body.scrollLeft + mouse_pos) * (Timeline.vue._data.size - original_size) ) / 128
+
+ let size_ratio = Timeline.vue._data.size / original_size
+ let offset = mouse_pos - body.scrollLeft - 180
+ let val = (size_ratio-1) * offset;
+ // todo: optimize zooming in
+ body.scrollLeft += val
+ /*
+ Timeline.vue._data.size = limitNumber(Timeline.vue._data.size * zoom, 10, 1000)
+ body.scrollLeft *= zoom
let l = (event.offsetX / body.clientWidth) * 500 * (event.deltaY<0?1:-0.2)
body.scrollLeft += l
+ */
+
} else {
body.scrollTop += event.deltaY/6.25
}
@@ -1752,88 +1816,56 @@ const Timeline = {
}
Undo.addKeyframeCasualties(deleted);
Undo.finishEdit('drag keyframes')
- }
- })
- $('#timeline_body .keyframe:not(.ui-draggable)').draggable({
- axis: 'x',
- distance: 4,
- helper: () => $(''),
- start: function(event, ui) {
-
- var id = $(event.target).attr('id');
- var clicked = Timeline.keyframes.findInArray('uuid', id)
-
- if (!$(event.target).hasClass('selected') && !event.shiftKey && Timeline.selected.length != 0) {
- clicked.select()
- } else if (clicked && !clicked.selected) {
- clicked.select({shiftKey: true})
- }
-
- Undo.initEdit({keyframes: Timeline.selected})
- Timeline.dragging_keyframes = true;
-
- var i = 0;
- for (var kf of Timeline.selected) {
- kf.time_before = kf.time
- }
- },
- drag: function(event, ui) {
- var difference = (ui.position.left - ui.originalPosition.left - 8) / Timeline.vue._data.size;
- var id = $(ui.helper).attr('id')
- var snap_value = false
- var nearest
-
- for (var kf of Timeline.selected) {
- var t = limitNumber(kf.time_before + difference, 0, 256)
- if (kf.uuid === id) {
- ui.position.left = t * Timeline.vue._data.size + 8
- }
- kf.time = Timeline.snapTime(t);
- }
- BarItems.slider_keyframe_time.update()
- Animator.preview()
- },
- stop: function(event, ui) {
- var deleted = []
- for (var kf of Timeline.selected) {
- delete kf.time_before;
- kf.replaceOthers(deleted);
- }
- Undo.addKeyframeCasualties(deleted);
- Undo.finishEdit('drag keyframes')
+ setTimeout(() => {
+ Timeline.dragging_keyframes = false;
+ }, 20)
}
})
},
- updateSize() {
- let size = Timeline.vue._data.size
+ getMaxLength() {
var max_length = ($('#timeline_body').width()-8) / Timeline.vue._data.size;
Timeline.keyframes.forEach((kf) => {
max_length = Math.max(max_length, kf.time)
})
max_length = Math.max(max_length, Timeline.time) + 50/Timeline.vue._data.size
- Timeline.vue._data.length = max_length
- Timeline.vue._data.timecodes.length = 0
+ return max_length;
+ },
+ updateSize() {
+ let size = Timeline.vue._data.size
+ Timeline.vue._data.length = Timeline.getMaxLength()
+ Timeline.vue._data.timecodes.empty()
var step = 1
if (size < 1) {step = 1}
else if (size < 20) {step = 4}
else if (size < 40) {step = 2}
- else if (size < 90) {step = 1}
- else if (size < 180) {step = 0.5}
- else if (size < 400) {step = 0.2}
- else if (size < 800) {step = 0.1}
+ else if (size < 100) {step = 1}
+ else if (size < 256) {step = 0.5}
+ else if (size < 520) {step = 0.25}
+ else if (size < 660) {step = 0.2}
+ else if (size < 860) {step = 0.1}
else {step = 0.05}
+
if (step < 1) {
var FPS = Timeline.getStep();
step = Math.round(step/FPS) * FPS
- step = 1/Math.round(1/step)
+ //step = 1/Math.round(1/step)
+ }
+
+ let substeps = step / Timeline.getStep()
+ while (substeps > 8) {
+ substeps /= 2;
}
+ //substeps = Math.round(substeps)
+
var i = 0;
while (i < Timeline.vue._data.length) {
Timeline.vue._data.timecodes.push({
time: i,
+ width: step,
+ substeps: substeps,
text: Math.round(i*100)/100
})
i += step;
@@ -1907,6 +1939,7 @@ const Timeline = {
'_',
'select_all',
'bring_up_all_animations',
+ 'fold_all_animations',
'clear_timeline',
])
}
@@ -1954,8 +1987,9 @@ onVueSetup(function() {
data: {
size: 150,
length: 10,
+ animation_length: 0,
scroll_left: 0,
- head_width: 170,
+ head_width: 180,
timecodes: [],
animators: Timeline.animators,
markers: [],
@@ -2006,6 +2040,7 @@ BARS.defineActions(function() {
}
}
Blockbench.import({
+ resource_id: 'animation',
extensions: ['json'],
type: 'JSON Animation',
startpath: path
@@ -2035,6 +2070,7 @@ BARS.defineActions(function() {
}
}
Blockbench.export({
+ resource_id: 'animation',
type: 'JSON Animation',
extensions: ['json'],
name: (Project.geometry_name||'model')+'.animation',
@@ -2075,7 +2111,13 @@ BARS.defineActions(function() {
return Animator.selected.length
},
change: function(modify) {
- Animator.selected.length = limitNumber(modify(Animator.selected.length), 0, 1e4)
+ Animator.selected.setLength(limitNumber(modify(Animator.selected.length), 0, 1e4))
+ },
+ onBefore: function() {
+ Undo.initEdit({animations: [Animator.selected]});
+ },
+ onAfter: function() {
+ Undo.finishEdit('Change Animation Length')
}
})
new NumSlider('slider_animation_speed', {
@@ -2169,12 +2211,28 @@ BARS.defineActions(function() {
Animator.preview()
}
})
+ new Action('resolve_keyframe_expressions', {
+ icon: 'functions',
+ category: 'animation',
+ condition: () => Animator.open && Timeline.selected.length,
+ click: function () {
+ Undo.initEdit({keyframes: Timeline.selected})
+ Timeline.selected.forEach((kf) => {
+ if (kf.animator.fillValues) {
+ kf.animator.fillValues(kf, null, false);
+ }
+ })
+ Undo.finishEdit('reset keyframes')
+ updateKeyframeSelection()
+ }
+ })
new Action('change_keyframe_file', {
icon: 'fa-file-audio',
category: 'animation',
condition: () => (Animator.open && Timeline.selected.length && Timeline.selected[0].channel == 'sound' && isApp),
click: function () {
Blockbench.import({
+ resource_id: 'animation_audio',
extensions: ['ogg'],
type: 'Audio File',
startpath: Timeline.selected[0].file
@@ -2365,6 +2423,7 @@ BARS.defineActions(function() {
category: 'animation',
condition: {modes: ['animate']},
click: function () {
+ if (!Animator.selected) return;
for (var uuid in Animator.selected.animators) {
var ba = Animator.selected.animators[uuid]
if (ba && ba.keyframes.length) {
@@ -2374,6 +2433,17 @@ BARS.defineActions(function() {
}
})
+ new Action('fold_all_animations', {
+ icon: 'format_indent_decrease',
+ category: 'animation',
+ condition: {modes: ['animate']},
+ click: function () {
+ for (var animator of Timeline.animators) {
+ animator.expanded = false;
+ }
+
+ }
+ })
new Action('clear_timeline', {
icon: 'clear_all',
category: 'animation',
@@ -2398,6 +2468,7 @@ BARS.defineActions(function() {
new BarSelect('timeline_focus', {
options: {
all: true,
+ used: true,
rotation: tl('timeline.rotation'),
position: tl('timeline.position'),
scale: tl('timeline.scale'),
diff --git a/js/api.js b/js/api.js
index d20135b3c..b818d2407 100644
--- a/js/api.js
+++ b/js/api.js
@@ -299,6 +299,7 @@ const Blockbench = {
//startpath
//title
//errorbox
+ //resource_id
if (isApp) {
var properties = []
@@ -309,6 +310,9 @@ const Blockbench = {
options.type = 'Images'
options.extensions = ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'tif', 'gif']
}
+ if (!options.startpath && options.resource_id) {
+ options.startpath = StateMemory.dialog_paths[options.resource_id]
+ }
ElecDialogs.showOpenDialog(
currentwindow,
@@ -323,6 +327,11 @@ const Blockbench = {
defaultPath: options.startpath
},
function (fileNames) {
+ if (!fileNames) return;
+ if (options.resource_id) {
+ StateMemory.dialog_paths[options.resource_id] = PathModule.dirname(fileNames[0])
+ StateMemory.save('dialog_paths')
+ }
Blockbench.read(fileNames, options, cb)
})
} else {
@@ -478,6 +487,7 @@ const Blockbench = {
savetype
project_file
custom_writer
+ resource_id
*/
if (Blockbench.isWeb) {
var file_name = options.name + (options.extensions ? '.'+options.extensions[0] : '')
@@ -504,6 +514,12 @@ const Blockbench = {
cb(file_name)
}
} else {
+ if (!options.startpath && options.resource_id) {
+ options.startpath = StateMemory.dialog_paths[options.resource_id]
+ if (options.name) {
+ options.startpath += osfs + options.name + (options.extensions ? '.'+options.extensions[0] : '');
+ }
+ }
ElecDialogs.showSaveDialog(currentwindow, {
dontAddToRecent: true,
filters: [ {
@@ -515,6 +531,10 @@ const Blockbench = {
: options.name
}, function (file_path) {
if (!file_path) return;
+ if (options.resource_id) {
+ StateMemory.dialog_paths[options.resource_id] = PathModule.dirname(file_path)
+ StateMemory.save('dialog_paths')
+ }
var extension = pathToExtension(file_path);
if (!extension && options.extensions && options.extensions[0]) {
file_path += '.'+options.extensions[0]
@@ -635,6 +655,38 @@ if (isApp) {
if (Blockbench.platform.includes('win32') === true) osfs = '\\';
}
+
+
+const StateMemory = {
+ init(key, type) {
+ let saved = localStorage.getItem(`StateMemory.${key}`)
+ if (typeof saved == 'string') {
+ try {
+ saved = JSON.parse(saved)
+ } catch (err) {
+ localStorage.clearItem(`StateMemory.${key}`)
+ }
+ }
+ if ( saved !== null && (typeof saved == type || (type == 'array' && saved instanceof Array)) ) {
+ StateMemory[key] = saved;
+ } else {
+ StateMemory[key] = (() => {switch (type) {
+ case 'string': return ''; break;
+ case 'number': return 0; break;
+ case 'boolean': return false; break;
+ case 'object': return {}; break;
+ case 'array': return []; break;
+ }})();
+ }
+ },
+ save(key) {
+ let serialized = JSON.stringify(StateMemory[key])
+ localStorage.setItem(`StateMemory.${key}`, serialized)
+ }
+}
+
+
+
document.ondragover = function(event) {
event.preventDefault()
}
diff --git a/js/blockbench.js b/js/blockbench.js
index ed614424c..96d98f7b6 100644
--- a/js/blockbench.js
+++ b/js/blockbench.js
@@ -29,39 +29,7 @@ var Prop = {
connections : 0,
facing : 'north'
}
-const Project = {
- name : '',
- parent : '',
- geometry_name : '',
- description : '',
- _box_uv : false,
- get box_uv() {return Project._box_uv},
- set box_uv(v) {
- if (Project._box_uv != v) {
- Project._box_uv = v;
- switchBoxUV(v);
- }
- },
- get texture_width() {return Project._texture_width},
- get texture_height() {return Project._texture_height},
- set texture_width(n) {
- n = parseInt(n)||16
- Vue.nextTick(updateProjectResolution)
- Project._texture_width = n;
- },
- set texture_height(n) {
- n = parseInt(n)||16
- Vue.nextTick(updateProjectResolution)
- Project._texture_height = n;
- },
- _texture_width : 16,
- _texture_height : 16,
- ambientocclusion: true,
- front_gui_light: false,
- get optional_box_uv() {
- return Format.optional_box_uv;
- }
-}
+
const mouse_pos = {x:0,y:0}
const sort_collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
@@ -175,12 +143,13 @@ function updateProjectResolution() {
//Selections
function updateSelection() {
elements.forEach(obj => {
- if (selected.includes(obj) && !obj.selected) {
+ if (selected.includes(obj) && !obj.selected && !obj.locked) {
obj.selectLow()
- } else if (!selected.includes(obj) && obj.selected) {
+ } else if ((!selected.includes(obj) || obj.locked) && obj.selected) {
obj.unselect()
}
})
+ if (Group.selected && Group.selected.locked) Group.selected.unselect()
Cube.all.forEach(cube => {
if (cube.visibility) {
@@ -221,7 +190,6 @@ function updateSelection() {
BarItems.cube_counter.update();
updateNslideValues();
- Blockbench.globalMovement = isMovementGlobal();
updateCubeHighlights();
Canvas.updateOrigin();
Transformer.updateSelection();
@@ -298,7 +266,7 @@ function createSelection() {
setInterval(function() {
if (Outliner.root.length || textures.length) {
try {
- var model = Codecs.project.compile({compressed: true});
+ var model = Codecs.project.compile({compressed: false});
localStorage.setItem('backup_model', model)
} catch (err) {
console.log('Unable to create backup. ', err)
diff --git a/js/boot_loader.js b/js/boot_loader.js
index 7a8af90d1..34a650e3b 100644
--- a/js/boot_loader.js
+++ b/js/boot_loader.js
@@ -1,6 +1,8 @@
CustomTheme.setup()
+StateMemory.init('dialog_paths', 'object')
+
initCanvas()
animate()
@@ -117,4 +119,6 @@ Modes.options.start.select()
loadInstalledPlugins();
+document.getElementById('page_wrapper').classList.remove('hidden')
+
Blockbench.setup_successful = true;
diff --git a/js/desktop.js b/js/desktop.js
index 7c981cf60..31d681184 100644
--- a/js/desktop.js
+++ b/js/desktop.js
@@ -7,6 +7,7 @@ const zlib = require('zlib');
const exec = require('child_process').exec;
const originalFs = require('original-fs');
const https = require('https');
+const PathModule = require('path')
const currentwindow = electron.getCurrentWindow();
const ElecDialogs = {};
@@ -49,7 +50,9 @@ function initializeDesktopApp() {
shell.openExternal(event.target.href);
return true;
});
- if (compareVersions('5.0.0', process.versions.electron)) {
+ if (currentwindow.webContents.zoomLevel !== undefined) {
+ Prop.zoom = 100 + currentwindow.webContents.zoomLevel*12
+ } else if (compareVersions('5.0.0', process.versions.electron)) {
Prop.zoom = 100 + currentwindow.webContents._getZoomLevel()*12
} else {
Prop.zoom = 100 + currentwindow.webContents.getZoomLevel()*12
@@ -114,6 +117,7 @@ function addRecentProject(data) {
}
i--;
}
+ if (data.name.length > 48) data.name = data.name.substr(0, 20) + '...' + data.name.substr(-20);
let project = {
name: data.name,
path: data.path,
@@ -214,7 +218,7 @@ function installUpdate() {
var file = originalFs.createWriteStream(asar_path);
- var request = https.get("https://blockbench.net/api/app.asar", function(response) {
+ https.get("https://blockbench.net/api/app.asar", function(response) {
response.pipe(file);
total_bytes = parseInt(response.headers['content-length']);
@@ -252,12 +256,12 @@ function changeImageEditor(texture, from_settings) {
var path;
if (Blockbench.platform == 'darwin') {
switch (id) {
- case 'ps': path = '/Applications/Adobe Photoshop CC 2019/Adobe Photoshop CC 2019.app'; break;
+ case 'ps': path = '/Applications/Adobe Photoshop CC 2020/Adobe Photoshop CC 2020.app'; break;
case 'gimp':path = '/Applications/Gimp-2.10.app'; break;
}
} else {
switch (id) {
- case 'ps': path = 'C:\\Program Files\\Adobe\\Adobe Photoshop CC 2019\\Photoshop.exe'; break;
+ case 'ps': path = 'C:\\Program Files\\Adobe\\Adobe Photoshop CC 2020\\Photoshop.exe'; break;
case 'gimp':path = 'C:\\Program Files\\GIMP 2\\bin\\gimp-2.10.exe'; break;
case 'pdn': path = 'C:\\Program Files\\paint.net\\PaintDotNet.exe'; break;
}
diff --git a/js/display_mode.js b/js/display_mode.js
index 8f919ad14..1fd344d63 100644
--- a/js/display_mode.js
+++ b/js/display_mode.js
@@ -1778,6 +1778,7 @@ window.changeDisplaySkin = function() {
}, function(result) {
if (result === 0) {
Blockbench.import({
+ resource_id: 'minecraft_skin',
extensions: ['png'],
type: 'PNG Player Skin',
readtype: 'image'
@@ -1937,6 +1938,51 @@ BARS.defineActions(function() {
condition: () => display_mode,
click: function () {showDialog('create_preset')}
})
+ new Action('apply_display_preset', {
+ icon: 'fa-list',
+ category: 'display',
+ condition: () => display_mode,
+ click: function (e) {
+ new Menu(this.children()).open(e.target)
+ },
+ children: function() {
+ var presets = []
+ display_presets.forEach(function(p) {
+ var icon = 'label'
+ if (p.fixed) {
+ switch(p.id) {
+ case 'item': icon = 'filter_vintage'; break;
+ case 'block': icon = 'fa-cube'; break;
+ case 'handheld': icon = 'build'; break;
+ case 'rod': icon = 'remove'; break;
+ }
+ }
+ presets.push({
+ icon: icon,
+ name: p.id ? tl('display.preset.'+p.id) : p.name,
+ children: [
+ {name: 'action.apply_display_preset.here', icon: 'done', click() {
+ DisplayMode.applyPreset(p)
+ }},
+ {name: 'action.apply_display_preset.everywhere', icon: 'done_all', click() {
+ DisplayMode.applyPreset(p, true)
+ }},
+ {
+ icon: 'delete',
+ name: 'generic.delete',
+ condition: !p.fixed,
+ click: function() {
+ display_presets.splice(display_presets.indexOf(p), 1);
+ localStorage.setItem('display_presets', JSON.stringify(display_presets))
+ }
+ }
+ ]
+ })
+ })
+ return presets;
+ }
+ })
+
new BarSelect('gui_light', {
options: {
side: true,
diff --git a/js/interface/actions.js b/js/interface/actions.js
index 90604a536..8f75b3ed7 100644
--- a/js/interface/actions.js
+++ b/js/interface/actions.js
@@ -172,6 +172,7 @@ class Action extends BarItem {
this.linked_setting = data.linked_setting
}
if (data.condition) this.condition = data.condition
+ this.children = data.children;
//Node
this.click = data.click
@@ -751,7 +752,7 @@ class BarSelect extends Widget {
}
let menu = new Menu(items);
menu.node.style['min-width'] = this.node.clientWidth+'px';
- menu.open(this.node, this);
+ menu.open(event.target, this);
}
trigger(event) {
if (!event) event = 0;
@@ -1518,8 +1519,13 @@ const BARS = {
'export_palette',
'generate_palette',
'sort_palette',
+ 'load_palette',
]
})
+ //update 3.5
+ if (!Toolbars.palette.children.includes(BarItems.load_palette)) {
+ Toolbars.palette.add(BarItems.load_palette)
+ }
Toolbars.color_picker = new Toolbar({
id: 'color_picker',
children: [
@@ -1538,14 +1544,11 @@ const BARS = {
'copy',
'paste',
'add_display_preset',
+ 'apply_display_preset',
'gui_light'
],
default_place: true
})
- //update 3.3.1
- if (!Toolbars.display.children.includes(BarItems.gui_light)) {
- Toolbars.display.add(BarItems.gui_light)
- }
//UV
Toolbars.main_uv = new Toolbar({
id: 'main_uv',
@@ -1682,6 +1685,7 @@ const BARS = {
BarItems.reset_keybindings.toElement('#keybinds_title_bar')
BarItems.load_plugin.toElement('#plugins_header_bar')
+ BarItems.load_plugin_from_url.toElement('#plugins_header_bar')
BarItems.uv_dialog.toElement('#uv_title_bar')
BarItems.uv_dialog_full.toElement('#uv_title_bar')
BarItems.toggle_chat.toElement('#chat_title_bar')
diff --git a/js/interface/dialog.js b/js/interface/dialog.js
index 261d33e35..21b42d446 100644
--- a/js/interface/dialog.js
+++ b/js/interface/dialog.js
@@ -72,7 +72,7 @@ function Dialog(settings) {
}
if (scope.form) {
for (var form_id in scope.form) {
- var data = scope.form[form_id]
+ let data = scope.form[form_id]
if (data === '_') {
jq_dialog.append('
')
@@ -94,10 +94,25 @@ function Dialog(settings) {
}
bar.append(list)
}
+ if (data.type == 'password') {
+ bar.append(`
+
+
`)
+ let input = bar.find('input').attr('type', 'password')
+ let hidden = true;
+ let this_bar = bar;
+ this_bar.find('.password_toggle').click(e => {
+ hidden = !hidden;
+ input.attr('type', hidden ? 'password' : 'text');
+ this_bar.find('.password_toggle i')[0].className = hidden ? 'fas fa-eye-slash' : 'fas fa-eye';
+ })
+ }
break;
case 'textarea':
bar.append(``)
break;
+
+
case 'select':
var el = $(``)
var sel = el.find('select')
@@ -107,6 +122,8 @@ function Dialog(settings) {
}
bar.append(el)
break;
+
+
case 'radio':
var el = $(``)
for (var key in data.options) {
@@ -118,14 +135,31 @@ function Dialog(settings) {
}
bar.append(el)
break;
- case 'text':
+
+
+ case 'info':
data.text = marked(tl(data.text))
bar.append(`${data.text}
`)
bar.addClass('small_text')
break;
+
+
case 'number':
- bar.append(``)
+ bar.append(``)
break;
+
+
+ case 'vector':
+ let group = $(``)
+ bar.append(group)
+ for (var i = 0; i < (data.dimensions || 3); i++) {
+ group.append(``)
+ }
+ break;
+
+
case 'color':
if (!data.colorpicker) {
data.colorpicker = new ColorPicker({
@@ -137,9 +171,13 @@ function Dialog(settings) {
}
bar.append(data.colorpicker.getNode())
break;
+
+
case 'checkbox':
bar.append(``)
break;
+
+
case 'file':
case 'folder':
case 'save':
@@ -170,6 +208,7 @@ function Dialog(settings) {
switch (data.type) {
case 'file':
Blockbench.import({
+ resource_id: data.resource_id,
extensions: data.extensions,
type: data.filetype,
startpath: data.value
@@ -185,6 +224,7 @@ function Dialog(settings) {
break;
case 'save':
Blockbench.export({
+ resource_id: data.resource_id,
extensions: data.extensions,
type: data.filetype,
startpath: data.value,
@@ -194,7 +234,6 @@ function Dialog(settings) {
}
})
- case 'folder':
}
if (data.readonly) {
bar.find('input').attr('readonly', 'readonly').removeClass('focusable_input')
@@ -223,7 +262,7 @@ function Dialog(settings) {
} else if (this.singleButton) {
- jq_dialog.append('' +
+ jq_dialog.append('
' +
'' +
'
')
@@ -247,7 +286,7 @@ function Dialog(settings) {
default:
result[form_id] = jq_dialog.find('input#'+form_id).val()
break;
- case 'text':
+ case 'info':
break;
case 'textarea':
result[form_id] = jq_dialog.find('textarea#'+form_id).val()
@@ -261,6 +300,13 @@ function Dialog(settings) {
case 'number':
result[form_id] = Math.clamp(parseFloat(jq_dialog.find('input#'+form_id).val())||0, data.min, data.max)
break;
+ case 'vector':
+ result[form_id] = [];
+ for (var i = 0; i < (data.dimensions || 3); i++) {
+ let num = Math.clamp(parseFloat(jq_dialog.find(`input#${form_id}_${i}`).val())||0, data.min, data.max)
+ result[form_id].push(num)
+ }
+ break;
case 'color':
result[form_id] = data.colorpicker.get();
break;
diff --git a/js/interface/interface.js b/js/interface/interface.js
index e3667c6de..c583f1603 100644
--- a/js/interface/interface.js
+++ b/js/interface/interface.js
@@ -116,9 +116,10 @@ class ResizeLine {
var jq = $('
')
this.node = jq.get(0)
jq.draggable({
- axis: this.horizontal ? 'y' : 'y',
+ axis: this.horizontal ? 'y' : 'x',
containment: '#page_wrapper',
revert: true,
+ revertDuration: 0,
start: function(e, u) {
scope.before = data.get()
},
@@ -126,7 +127,7 @@ class ResizeLine {
if (scope.horizontal) {
data.set(scope.before, u.position.top - u.originalPosition.top)
} else {
- data.set(scope.before, (e.clientX - u.position.left))
+ data.set(scope.before, (u.position.left - u.originalPosition.left))
}
updateInterface()
},
@@ -189,13 +190,14 @@ var Interface = {
},
get: function() {return Interface.data.left_bar_width},
set: function(o, diff) {
- Interface.data.left_bar_width = limitNumber(o + diff, 128, $(window).width()- 240 - Interface.data.right_bar_width)
+ let calculated = limitNumber(o + diff, 128, $(window).width()- 120 - Interface.data.right_bar_width)
+ Interface.data.left_bar_width = Math.snapToValues(calculated, [Interface.default_data.left_bar_width], 16);
},
position: function(line) {
line.setPosition({
- top: 32,
+ top: 26,
bottom: 0,
- left: Interface.data.left_bar_width-3
+ left: Interface.data.left_bar_width+2
})
}
}),
@@ -210,13 +212,14 @@ var Interface = {
},
get: function() {return Interface.data.right_bar_width},
set: function(o, diff) {
- Interface.data.right_bar_width = limitNumber(o - diff, 128, $(window).width()- 240 - Interface.data.left_bar_width)
+ let calculated = limitNumber(o - diff, 128, $(window).width()- 120 - Interface.data.left_bar_width);
+ Interface.data.right_bar_width = Math.snapToValues(calculated, [Interface.default_data.right_bar_width], 12);
},
position: function(line) {
line.setPosition({
- top: 32,
+ top: 56,
bottom: 0,
- right: Interface.data.right_bar_width-3
+ right: Interface.data.right_bar_width-2
})
}
}),
@@ -307,7 +310,6 @@ function setupInterface() {
$('.edit_session_active').hide()
$('#center').toggleClass('checkerboard', settings.preview_checkerboard.value);
- $('#UVEditor_main_uv').toggleClass('checkerboard_trigger', settings.uv_checkerboard.value);
$('.sidebar').droppable({
accept: 'h3',
@@ -846,7 +848,7 @@ var documentReady = new Promise((resolve, reject) => {
//Electron
- if (isApp && !compareVersions(process.versions.electron, '4.0.0')) {
+ if (isApp && !compareVersions(process.versions.electron, '6.0.0')) {
addStartScreenSection({
graphic: {type: 'icon', icon: 'fas.fa-atom'},
text: [
@@ -869,8 +871,21 @@ var documentReady = new Promise((resolve, reject) => {
last: true
})
}
+ //Twitter
+ if (Blockbench.startup_count < 20 && Blockbench.startup_count % 5 === 4) {
+ addStartScreenSection({
+ color: '#1da1f2',
+ text_color: '#ffffff',
+ graphic: {type: 'icon', icon: 'fab.fa-twitter'},
+ text: [
+ {type: 'h1', text: 'Blockbench on Twitter'},
+ {text: 'Follow Blockbench on Twitter for the latest news as well as cool models from the community! [twitter.com/blockbench](https://twitter.com/blockbench/)'}
+ ],
+ last: true
+ })
+ }
//Donation reminder
- if (Blockbench.startup_count % 12 === 11) {
+ if (Blockbench.startup_count % 20 === 19) {
addStartScreenSection({
graphic: {type: 'icon', icon: 'fas.fa-heart'},
text: [
diff --git a/js/interface/menu.js b/js/interface/menu.js
index cf0fc6282..e086a156a 100644
--- a/js/interface/menu.js
+++ b/js/interface/menu.js
@@ -81,7 +81,7 @@ class Menu {
} else if (index >= MenuBar.keys.length) {
index = 0;
}
- MenuBar.menues[MenuBar.keys[index]].open()
+ MenuBar.menus[MenuBar.keys[index]].open()
}
} else {
obj.find('> li:first-child').addClass('focused')
@@ -113,6 +113,30 @@ class Menu {
ctxmenu.children().detach()
+ function createChildList(object, node) {
+
+ if (typeof object.children == 'function') {
+ var list = object.children(context)
+ } else {
+ var list = object.children
+ }
+ if (list.length) {
+ node.addClass('parent')
+ .find('ul.contextMenu.sub').detach()
+ var childlist = $('')
+ node.append(childlist)
+ list.forEach(function(s2, i) {
+ getEntry(s2, childlist)
+ })
+ var last = childlist.children().last()
+ if (last.length && last.hasClass('menu_separator')) {
+ last.remove()
+ }
+ return childlist.children().length;
+ }
+ return 0;
+ }
+
function getEntry(s, parent) {
var entry;
@@ -129,15 +153,20 @@ class Menu {
if (!s) {
return;
}
- entry = s.menu_node
+ entry = $(s.menu_node)
if (BARS.condition(s.condition)) {
- if (!entry.hasMenuEvents) {
- entry.hasMenuEvents = true
- entry.addEventListener('click', (e) => {s.trigger(e)})
- $(entry).on('mouseenter mousedown', function(e) {
- scope.hover(this, e)
- })
+ entry.off('click')
+ entry.off('mouseenter mousedown')
+ entry.on('mouseenter mousedown', function(e) {
+ scope.hover(this, e)
+ })
+ //Submenu
+ if (typeof s.children == 'function' || typeof s.children == 'object') {
+ createChildList(s, entry)
+ } else {
+ entry.on('click', (e) => {s.trigger(e)})
+ //entry[0].addEventListener('click', )
}
parent.append(entry)
}
@@ -161,24 +190,7 @@ class Menu {
}
//Submenu
if (typeof s.children == 'function' || typeof s.children == 'object') {
- if (typeof s.children == 'function') {
- var list = s.children(context)
- } else {
- var list = s.children
- }
- if (list.length) {
- entry.addClass('parent')
- var childlist = $('')
- entry.append(childlist)
- list.forEach(function(s2, i) {
- getEntry(s2, childlist)
- })
- var last = childlist.children().last()
- child_count = childlist.children().length;
- if (last.length && last.hasClass('menu_separator')) {
- last.remove()
- }
- }
+ child_count = createChildList(s, entry)
}
if (child_count !== 0 || typeof s.click === 'function') {
parent.append(entry)
@@ -204,6 +216,11 @@ class Menu {
if (position && position.clientX !== undefined) {
var offset_left = position.clientX
var offset_top = position.clientY+1
+
+ } else if (position == document.body) {
+ var offset_left = (document.body.clientWidth-el_width)/2
+ var offset_top = (document.body.clientHeight-el_height)/2
+
} else {
if (!position && scope.type === 'bar_menu') {
position = scope.label
@@ -338,7 +355,7 @@ class BarMenu extends Menu {
constructor(id, structure, condition) {
super()
var scope = this;
- MenuBar.menues[id] = this
+ MenuBar.menus[id] = this
this.type = 'bar_menu'
this.id = id
this.children = [];
@@ -367,9 +384,10 @@ class BarMenu extends Menu {
}
}
const MenuBar = {
- menues: {},
+ menus: {},
open: undefined,
setup() {
+ MenuBar.menues = MenuBar.menus;
new BarMenu('file', [
'project_window',
'_',
@@ -470,6 +488,7 @@ const MenuBar = {
'add_cube',
'add_group',
'add_locator',
+ 'unlock_everything',
'duplicate',
'delete',
'_',
@@ -500,6 +519,7 @@ const MenuBar = {
]},
{name: 'menu.transform.properties', id: 'properties', icon: 'navigate_next', children: [
'toggle_visibility',
+ 'toggle_locked',
'toggle_export',
'toggle_autouv',
'toggle_shade',
@@ -514,66 +534,7 @@ const MenuBar = {
'paste',
'_',
'add_display_preset',
- {name: 'menu.display.preset', icon: 'fa-list', children: function() {
- var presets = []
- display_presets.forEach(function(p) {
- var icon = 'label'
- if (p.fixed) {
- switch(p.id) {
- case 'item': icon = 'filter_vintage'; break;
- case 'block': icon = 'fa-cube'; break;
- case 'handheld': icon = 'build'; break;
- case 'rod': icon = 'remove'; break;
- }
- }
- presets.push({
- icon: icon,
- name: p.id ? tl('display.preset.'+p.id) : p.name,
- click: function() {
- DisplayMode.applyPreset(p)
- }
- })
- })
- return presets;
- }},
- {name: 'menu.display.preset_all', icon: 'fa-list', children: function() {
- var presets = []
- display_presets.forEach(function(p) {
- var icon = 'label'
- if (p.fixed) {
- switch(p.id) {
- case 'item': icon = 'filter_vintage'; break;
- case 'block': icon = 'fa-cube'; break;
- case 'handheld': icon = 'build'; break;
- case 'rod': icon = 'remove'; break;
- }
- }
- presets.push({
- icon: icon,
- name: p.id ? tl('display.preset.'+p.id) : p.name,
- click: function() {
- DisplayMode.applyPreset(p, true)
- }
- })
- })
- return presets;
- }},
- {name: 'menu.display.remove_preset', icon: 'fa-list', children: function() {
- var presets = []
- display_presets.forEach(function(p) {
- if (!p.fixed) {
- presets.push({
- icon: 'label',
- name: p.name,
- click: function() {
- display_presets.splice(display_presets.indexOf(p), 1);
- localStorage.setItem('display_presets', JSON.stringify(display_presets))
- }
- })
- }
- })
- return presets;
- }}
+ 'apply_display_preset'
], () => Modes.display)
new BarMenu('filter', [
@@ -650,7 +611,9 @@ const MenuBar = {
{name: 'menu.help.developer.reset_storage', icon: 'fas.fa-hdd', click: () => {
if (confirm(tl('menu.help.developer.reset_storage.confirm'))) {
localStorage.clear()
+ Blockbench.addFlag('no_localstorage_saving')
console.log('Cleared Local Storage')
+ window.location.reload(true)
}
}},
{name: 'menu.help.developer.cache_reload', icon: 'cached', condition: !isApp, click: () => {
@@ -672,10 +635,10 @@ const MenuBar = {
var bar = $('#menu_bar')
bar.children().detach()
this.keys = []
- for (var menu in MenuBar.menues) {
- if (MenuBar.menues.hasOwnProperty(menu)) {
- if (MenuBar.menues[menu].conditionMet()) {
- bar.append(MenuBar.menues[menu].label)
+ for (var menu in MenuBar.menus) {
+ if (MenuBar.menus.hasOwnProperty(menu)) {
+ if (MenuBar.menus[menu].conditionMet()) {
+ bar.append(MenuBar.menus[menu].label)
this.keys.push(menu)
}
}
@@ -686,7 +649,7 @@ const MenuBar = {
addAction(action, path) {
if (path) {
path = path.split('.')
- var menu = MenuBar.menues[path.splice(0, 1)[0]]
+ var menu = MenuBar.menus[path.splice(0, 1)[0]]
if (menu) {
menu.addAction(action, path.join('.'))
}
@@ -695,7 +658,7 @@ const MenuBar = {
removeAction(path) {
if (path) {
path = path.split('.')
- var menu = MenuBar.menues[path.splice(0, 1)[0]]
+ var menu = MenuBar.menus[path.splice(0, 1)[0]]
if (menu) {
menu.removeAction(path.join('.'))
}
diff --git a/js/interface/settings.js b/js/interface/settings.js
index 787f0b432..206533f30 100644
--- a/js/interface/settings.js
+++ b/js/interface/settings.js
@@ -17,6 +17,7 @@ class Setting {
case 'toggle': this.value = true; break;
case 'number': this.value = 0; break;
case 'text': this.value = ''; break;
+ case 'password': this.value = ''; break;
case 'select': this.value; break;
case 'click': this.value = false; break;
}
@@ -38,6 +39,9 @@ class Setting {
if (this.type == 'select') {
this.options = data.options;
}
+ if (this.type == 'password') {
+ this.hidden = true;
+ }
if (typeof data.onChange == 'function') {
this.onChange = data.onChange
}
@@ -88,7 +92,7 @@ const Settings = {
$('#center').toggleClass('checkerboard', settings.preview_checkerboard.value);
}});
new Setting('uv_checkerboard', {category: 'interface', value: false, onChange() {
- $('#UVEditor_main_uv').toggleClass('checkerboard_trigger', settings.uv_checkerboard.value);
+ $('.UVEditor').toggleClass('checkerboard_trigger', settings.uv_checkerboard.value);
}});
//Preview
@@ -111,12 +115,13 @@ const Settings = {
new Setting('deactivate_size_limit',{category: 'edit', value: false});
//Grid
- new Setting('base_grid', {category: 'grid', value: true,});
- new Setting('large_grid', {category: 'grid', value: false});
- new Setting('full_grid', {category: 'grid', value: false});
- new Setting('large_box', {category: 'grid', value: false});
- new Setting('display_grid', {category: 'grid', value: false});
- new Setting('painting_grid',{category: 'grid', value: true, onChange() {
+ new Setting('base_grid', {category: 'grid', value: true,});
+ new Setting('large_grid', {category: 'grid', value: false});
+ new Setting('full_grid', {category: 'grid', value: false});
+ new Setting('large_box', {category: 'grid', value: false});
+ new Setting('large_grid_size', {category: 'grid', value: 3, type: 'number'});
+ new Setting('display_grid', {category: 'grid', value: false});
+ new Setting('painting_grid', {category: 'grid', value: true, onChange() {
Cube.all.forEach(cube => {
Canvas.buildGridBox(cube)
})
@@ -130,7 +135,8 @@ const Settings = {
new Setting('animation_snap',{category: 'snapping', value: 25, type: 'number'});
//Paint
- new Setting('paint_side_restrict', {category: 'paint', value: true});
+ new Setting('paint_side_restrict', {category: 'paint', value: true});
+ //new Setting('layered_textures', {category: 'paint', value: false});
new Setting('brush_opacity_modifier', {category: 'paint', value: 'pressure', type: 'select', options: {
'pressure': tl('settings.brush_modifier.pressure'),
'tilt': tl('settings.brush_modifier.tilt'),
@@ -149,18 +155,13 @@ const Settings = {
new Setting('default_path', {category: 'defaults', value: false, type: 'click', condition: isApp, icon: 'burst_mode', click: function() { openDefaultTexturePath() }});
//Dialogs
- new Setting('dialog_unsaved_textures', {category: 'dialogs', value: true});
new Setting('dialog_larger_cubes', {category: 'dialogs', value: true});
new Setting('dialog_rotation_limit', {category: 'dialogs', value: true});
//Export
new Setting('minifiedout', {category: 'export', value: false});
new Setting('export_groups', {category: 'export', value: true});
- new Setting('class_export_version', {category: 'export', value: '1.12', type: 'select', options: {
- '1.12': '1.12',
- '1.14': '1.14',
- }});
- new Setting('sketchfab_token', {category: 'export', value: '', type: 'text'});
+ new Setting('sketchfab_token', {category: 'export', value: '', type: 'password'});
new Setting('credit', {category: 'export', value: 'Made with Blockbench', type: 'text'});
},
addCategory(id, data) {
@@ -181,18 +182,20 @@ const Settings = {
setSettingsTab('setting')
},
saveLocalStorages() {
- localStorage.setItem('canvas_scenes', JSON.stringify(canvas_scenes))
var settings_copy = {}
for (var key in settings) {
settings_copy[key] = {value: settings[key].value}
}
localStorage.setItem('settings', JSON.stringify(settings_copy) )
+
+ localStorage.setItem('canvas_scenes', JSON.stringify(canvas_scenes))
localStorage.setItem('colors', JSON.stringify({
palette: ColorPanel.vue._data.palette,
history: ColorPanel.vue._data.history,
}))
},
save() {
+ Settings.saveLocalStorages()
function hasSettingChanged(id) {
return (settings[id].value !== Settings.old[id])
}
@@ -205,7 +208,7 @@ const Settings = {
action.toggleLinkedSetting(false)
}
}
- if (hasSettingChanged('base_grid') || hasSettingChanged('large_grid') || hasSettingChanged('full_grid')
+ if (hasSettingChanged('base_grid') || hasSettingChanged('large_grid') || hasSettingChanged('full_grid') || hasSettingChanged('large_grid_size')
||hasSettingChanged('large_box') || hasSettingChanged('display_grid') || hasSettingChanged('edit_size')) {
buildGrid()
}
@@ -264,9 +267,14 @@ const Settings = {
},
old: {}
}
-$(window).on('unload', Settings.saveLocalStorages)
Settings.setup()
+window.onunload = function() {
+ if (!Blockbench.hasFlag('no_localstorage_saving')) {
+ Settings.saveLocalStorages()
+ }
+}
+
onVueSetup(function() {
Settings.structure.search_results = {
name: tl('dialog.settings.search_results'),
diff --git a/js/interface/themes.js b/js/interface/themes.js
index ecc408848..81e21f3ba 100644
--- a/js/interface/themes.js
+++ b/js/interface/themes.js
@@ -15,6 +15,7 @@ const CustomTheme = {
button: '#3a3f4b',
bright_ui: '#f4f3ff',
accent: '#3e90ff',
+ frame: '#181a1f',
text: '#cacad4',
light: '#f4f3ff',
accent_text: '#000006',
@@ -61,7 +62,7 @@ const CustomTheme = {
var hex = CustomTheme.data.colors[key];
document.body.style.setProperty('--color-'+key, hex);
}
- $('meta[name=theme-color]').attr('content', CustomTheme.data.colors.border);
+ $('meta[name=theme-color]').attr('content', CustomTheme.data.colors.frame);
var c_outline = parseInt('0x'+CustomTheme.data.colors.accent.replace('#', ''))
if (c_outline !== gizmo_colors.outline.getHex()) {
@@ -197,6 +198,7 @@ BARS.defineActions(function() {
category: 'blockbench',
click: function () {
Blockbench.import({
+ resource_id: 'theme',
extensions: ['bbstyle', 'bbtheme'],
type: 'Blockbench Theme'
}, function(files) {
@@ -209,6 +211,7 @@ BARS.defineActions(function() {
category: 'blockbench',
click: function () {
Blockbench.export({
+ resource_id: 'theme',
type: 'Blockbench Theme',
extensions: ['bbtheme'],
content: autoStringify(CustomTheme.data)
diff --git a/js/io/bbmodel.js b/js/io/bbmodel.js
index 7d5dcc5cc..5c8f6b983 100644
--- a/js/io/bbmodel.js
+++ b/js/io/bbmodel.js
@@ -38,6 +38,12 @@ var codec = new Codec('project', {
model.ambientocclusion = Project.ambientocclusion
model.front_gui_light = Project.front_gui_light;
}
+ if (Format.id == 'bedrock' || Format.id == 'bedrock_legacy') {
+ model.visible_box = Project.visible_box
+ }
+ if (Format.id == 'modded_entity') {
+ model.modded_entity_version = Project.modded_entity_version
+ }
model.resolution = {
width: Project.texture_width || 16,
height: Project.texture_height || 16,
@@ -156,6 +162,12 @@ var codec = new Codec('project', {
if (model.front_gui_light !== undefined) {
Project.front_gui_light = !!model.front_gui_light;
}
+ if (model.visible_box) {
+ Project.visible_box.splice(0, Infinity, ...model.visible_box)
+ }
+ if (model.modded_entity_version) {
+ Project.modded_entity_version = model.modded_entity_version
+ }
if (model.resolution !== undefined) {
Project.texture_width = model.resolution.width;
Project.texture_height = model.resolution.height;
@@ -179,12 +191,12 @@ var codec = new Codec('project', {
var copy = NonGroup.fromSave(element, true)
for (var face in copy.faces) {
- if (!Format.single_texture) {
+ if (!Format.single_texture && element.faces) {
var texture = element.faces[face].texture !== null && textures[element.faces[face].texture]
if (texture) {
copy.faces[face].texture = texture.uuid
}
- } else if (textures[0] && copy.faces[face].texture !== null) {
+ } else if (textures[0] && copy.faces && copy.faces[face].texture !== null) {
copy.faces[face].texture = textures[0].uuid
}
}
diff --git a/js/io/bedrock.js b/js/io/bedrock.js
index bccb2166e..767c9c639 100644
--- a/js/io/bedrock.js
+++ b/js/io/bedrock.js
@@ -207,6 +207,46 @@ window.BedrockEntityManager = {
}
}
+function calculateVisibleBox() {
+ var visible_box = new THREE.Box3()
+ Cube.all.forEach(cube => {
+ if (cube.export && cube.mesh) {
+ visible_box.expandByObject(cube.mesh);
+ }
+ })
+
+ var offset = new THREE.Vector3(8,8,8);
+ visible_box.max.add(offset);
+ visible_box.min.add(offset);
+
+ // Width
+ var radius = Math.max(
+ visible_box.max.x,
+ visible_box.max.z,
+ -visible_box.min.x,
+ -visible_box.min.z
+ )
+ if (Math.abs(radius) === Infinity) {
+ radius = 0
+ }
+ let width = Math.ceil((radius*2) / 16)
+ width = Math.max(width, Project.visible_box[0]);
+ Project.visible_box[0] = width;
+
+ // Height
+ let height = Math.ceil(Math.abs(visible_box.max.y - visible_box.min.y) / 16)
+ if (height === Infinity) height = 0;
+ height = Math.max(height, Project.visible_box[1]);
+
+ // Y
+ let y = height/2 + Math.floor(visible_box.min.y / 16)
+ if (y === Infinity) y = 0;
+ y = Math.min(y, Project.visible_box[2]);
+
+ Project.visible_box.replace([width, height, y])
+ return Project.visible_box;
+}
+
(function() {
function parseGeometry(data) {
@@ -221,16 +261,25 @@ function parseGeometry(data) {
}
}
codec.dispatchEvent('parse', {model: data.object});
+ let {description} = data.object;
- Project.geometry_name = (data.object.description.identifier && data.object.description.identifier.replace(/^geometry\./, '')) || '';
+ Project.geometry_name = (description.identifier && description.identifier.replace(/^geometry\./, '')) || '';
Project.texture_width = 16;
Project.texture_height = 16;
- if (data.object.description.texture_width !== undefined) {
- Project.texture_width = data.object.description.texture_width;
+ if (typeof description.visible_bounds_width == 'number' && typeof description.visible_bounds_height == 'number') {
+ Project.visible_box[0] = Math.max(Project.visible_box[0], description.visible_bounds_width);
+ Project.visible_box[1] = Math.max(Project.visible_box[1], description.visible_bounds_height);
+ if (description.visible_bounds_offset && typeof description.visible_bounds_offset[1] == 'number') {
+ Project.visible_box[2] = Math.min(Project.visible_box[2], description.visible_bounds_offset[1]);
+ }
+ }
+
+ if (description.texture_width !== undefined) {
+ Project.texture_width = description.texture_width;
}
- if (data.object.description.texture_height !== undefined) {
- Project.texture_height = data.object.description.texture_height;
+ if (description.texture_height !== undefined) {
+ Project.texture_height = description.texture_height;
}
var bones = {}
@@ -367,6 +416,7 @@ function parseGeometry(data) {
}
+
var codec = new Codec('bedrock', {
name: 'Bedrock Model',
extension: 'json',
@@ -385,7 +435,6 @@ var codec = new Codec('bedrock', {
texture_height: Project.texture_height || 16,
}
var bones = []
- var visible_box = new THREE.Box3()
var groups = getAllGroups();
var loose_cubes = [];
@@ -485,11 +534,6 @@ var codec = new Codec('bedrock', {
}
}
}
- //Visible Bounds
- var mesh = obj.mesh
- if (mesh) {
- visible_box.expandByObject(mesh)
- }
cubes.push(template)
} else if (obj instanceof Locator) {
@@ -510,26 +554,11 @@ var codec = new Codec('bedrock', {
})
if (bones.length && options.visible_box !== false) {
- var offset = new THREE.Vector3(8,8,8)
- visible_box.max.add(offset)
- visible_box.min.add(offset)
- //Width
- var radius = Math.max(
- visible_box.max.x,
- visible_box.max.z,
- -visible_box.min.x,
- -visible_box.min.z
- ) * 0.9
- if (Math.abs(radius) === Infinity) {
- radius = 0
- }
- entitymodel.description.visible_bounds_width = Math.ceil((radius*2) / 16)
- //Height
- entitymodel.description.visible_bounds_height = Math.ceil(((visible_box.max.y - visible_box.min.y) * 0.9) / 16)
- if (Math.abs(entitymodel.description.visible_bounds_height) === Infinity) {
- entitymodel.description.visible_bounds_height = 0;
- }
- entitymodel.description.visible_bounds_offset = [0, entitymodel.description.visible_bounds_height/2 , 0]
+
+ let visible_box = calculateVisibleBox();
+ entitymodel.description.visible_bounds_width = visible_box[0];
+ entitymodel.description.visible_bounds_height = visible_box[1];
+ entitymodel.description.visible_bounds_offset = [0, visible_box[2] , 0]
}
if (bones.length) {
entitymodel.bones = bones
@@ -777,6 +806,7 @@ var format = new ModelFormat({
}
})
+//Object.defineProperty(format, 'single_texture', {get: _ => !settings.layered_textures.value})
codec.format = format;
BARS.defineActions(function() {
diff --git a/js/io/bedrock_old.js b/js/io/bedrock_old.js
index e262da33f..a84a30801 100644
--- a/js/io/bedrock_old.js
+++ b/js/io/bedrock_old.js
@@ -16,6 +16,14 @@ function parseGeometry(data) {
Project.texture_width = data.object.texturewidth || 64;
Project.texture_height = data.object.textureheight || 64;
+ if (typeof data.object.visible_bounds_width == 'number' && typeof data.object.visible_bounds_height == 'number') {
+ Project.visible_box[0] = Math.max(Project.visible_box[0], data.object.visible_bounds_width);
+ Project.visible_box[1] = Math.max(Project.visible_box[1], data.object.visible_bounds_height);
+ if (data.object.visible_bounds_offset && typeof data.object.visible_bounds_offset[1] == 'number') {
+ Project.visible_box[2] = Math.min(Project.visible_box[2], data.object.visible_bounds_offset[1]);
+ }
+ }
+
var bones = {}
if (data.object.bones) {
@@ -203,26 +211,11 @@ var codec = new Codec('bedrock_old', {
})
if (bones.length && options.visible_box !== false) {
- var offset = new THREE.Vector3(8,8,8)
- visible_box.max.add(offset)
- visible_box.min.add(offset)
- //Width
- var radius = Math.max(
- visible_box.max.x,
- visible_box.max.z,
- -visible_box.min.x,
- -visible_box.min.z
- ) * 0.9
- if (Math.abs(radius) === Infinity) {
- radius = 0
- }
- entitymodel.visible_bounds_width = Math.ceil((radius*2) / 16)
- //Height
- entitymodel.visible_bounds_height = Math.ceil(((visible_box.max.y - visible_box.min.y) * 0.9) / 16)
- if (Math.abs(entitymodel.visible_bounds_height) === Infinity) {
- entitymodel.visible_bounds_height = 0;
- }
- entitymodel.visible_bounds_offset = [0, entitymodel.visible_bounds_height/2 , 0]
+
+ let visible_box = calculateVisibleBox();
+ entitymodel.visible_bounds_width = visible_box[0];
+ entitymodel.visible_bounds_height = visible_box[1];
+ entitymodel.visible_bounds_offset = [0, visible_box[2] , 0]
}
if (bones.length) {
entitymodel.bones = bones
@@ -258,18 +251,12 @@ var codec = new Codec('bedrock_old', {
pe_list._data.search_text = ''
}
- function rotateOriginCoord(pivot, y, z) {
- return [
- pivot[1] - pivot[2] + z,
- pivot[2] - y + pivot[1]
- ]
- }
function create_thumbnail(model_entry, isize) {
var included_bones = []
model_entry.object.bones.forEach(function(b) {
included_bones.push(b.name)
})
- var thumbnail = new Jimp(48, 48, 0x00000000, function(err, image) {
+ new Jimp(48, 48, 0x00000000, function(err, image) {
model_entry.object.bones.forEach(function(b) {
//var rotate_bone = false;
//if (b.name === 'body' &&
@@ -400,6 +387,7 @@ var codec = new Codec('bedrock_old', {
export() {
var scope = this;
Blockbench.export({
+ resource_id: 'model',
type: this.name,
extensions: [this.extension],
name: this.fileName(),
@@ -492,6 +480,7 @@ var format = new ModelFormat({
}
})
+//Object.defineProperty(format, 'single_texture', {get: _ => !settings.layered_textures.value})
codec.format = format;
BARS.defineActions(function() {
diff --git a/js/io/gltf.js b/js/io/gltf.js
index 071f41aa9..ad418d2e0 100644
--- a/js/io/gltf.js
+++ b/js/io/gltf.js
@@ -1,6 +1,99 @@
(function() {
+function buildAnimationTracks() {
+ let tracks = [];
+ Animator.animations.forEach(animation => {
+ let tracks = [];
+ for (var uuid in animation.animators) {
+ let animator = animation.animators[uuid];
+
+ if (animator instanceof BoneAnimator && animator.getGroup()) {
+ for (var channel of animator.channels) {
+ if (animator[channel] && animator[channel].length) {
+ let times = [];
+ let values = [];
+ let keyframes = animator[channel].slice();
+
+ // Sampling calculated (molang) values
+ let contains_script
+ for (var kf of keyframes) {
+ if (isNaN(kf.x) || isNaN(kf.y) || isNaN(kf.z)) {
+ contains_script = true; break;
+ }
+ }
+ if (contains_script) {
+ var last_values;
+ for (var time = 0; time < animation.length; time += 1/24) {
+ Timeline.time = time;
+ let values = animator.interpolate(channel, false)
+ if (!values.equals(last_values) && !keyframes.find(kf => Math.epsilon(kf.time, time, 1/24))) {
+ let new_keyframe = new Keyframe({
+ time, channel,
+ x: values[0], y: values[1], z: values[2],
+ })
+ new_keyframe.animator = animator;
+ keyframes.push(new_keyframe)
+ }
+ last_values = values;
+ }
+ }
+
+ keyframes.sort((a, b) => a.time - b.time)
+
+ // Sampling rotation steps that exceed 180 degrees
+ if (channel === 'rotation' && !contains_script) {
+ let original_keyframes = keyframes.slice();
+ original_keyframes.forEach((kf, i) => {
+ let next = original_keyframes[i+1]
+ if (!next) return;
+
+ let k1 = kf.getArray();
+ let k2 = next.getArray();
+ let max_diff = Math.max(Math.abs(k1[0] - k2[0]), Math.abs(k1[1] - k2[1]), Math.abs(k1[2] - k2[2]));
+ let steps = Math.floor(max_diff / 180 + 1);
+
+ for (var step = 1; step < steps; step++) {
+
+ Timeline.time = kf.time + (next.time - kf.time) * (step/steps);
+ let values = animator.interpolate(channel, false)
+ let new_keyframe = new Keyframe({
+ time: Timeline.time, channel,
+ x: values[0], y: values[1], z: values[2],
+ })
+ new_keyframe.animator = animator;
+ keyframes.splice(keyframes.indexOf(kf) + step, 0, new_keyframe);
+ }
+ })
+ }
+
+ keyframes.forEach(kf => {
+ times.push(kf.time);
+ Timeline.time = kf.time;
+ kf.getFixed().toArray(values, values.length);
+ })
+ let trackType = THREE.VectorKeyframeTrack;
+ if (channel === 'rotation') {
+ trackType = THREE.QuaternionKeyframeTrack;
+ channel = 'quaternion';
+ }
+ let track = new trackType(animator.group.mesh.uuid+'.'+channel, times, values, THREE.InterpolateLinear);
+ tracks.push(track);
+ }
+ }
+ } else if (animator instanceof BoneAnimator) {
+ console.log(`Skip export of track ${uuid.substr(0, 7)}... - No connected bone`)
+ }
+ }
+ if (tracks.length) {
+ let clip = new THREE.AnimationClip(animation.name, animation.length, tracks)
+ tracks.push(clip);
+ } else {
+ console.log(`Skip export of animation ${animation.name} - No tracks generated`)
+ }
+ })
+ return tracks;
+}
var codec = new Codec('gltf', {
name: 'GLTF Model',
@@ -21,45 +114,9 @@ var codec = new Codec('gltf', {
Animator.showDefaultPose();
}
if (options.animations !== false) {
- Animator.animations.forEach(animation => {
-
- let tracks = [];
- for (var uuid in animation.animators) {
- let animator = animation.animators[uuid];
-
- if (animator instanceof BoneAnimator && animator.getGroup()) {
- for (var channel of animator.channels) {
- if (animator[channel] && animator[channel].length) {
- let times = [];
- let values = [];
- let keyframes = animator[channel].slice();
- keyframes.sort((a, b) => a.time - b.time)
- keyframes.forEach(kf => {
- times.push(kf.time);
- Timeline.time = kf.time;
- kf.getFixed().toArray(values, values.length);
- })
- let trackType = THREE.VectorKeyframeTrack;
- if (channel === 'rotation') {
- trackType = THREE.QuaternionKeyframeTrack;
- channel = 'quaternion';
- }
- let track = new trackType(animator.group.mesh.uuid+'.'+channel, times, values, THREE.InterpolateLinear);
- tracks.push(track);
- }
- }
- } else if (animator instanceof BoneAnimator) {
- console.log(`Skip export of track ${uuid.substr(0, 7)}... - No connected bone`)
- }
- }
- if (tracks.length) {
- let clip = new THREE.AnimationClip(animation.name, animation.length, tracks)
- animations.push(clip);
- } else {
- console.log(`Skip export of animation ${animation.name} - No tracks generated`)
- }
- })
+ animations = buildAnimationTracks();
}
+
exporter.parse(gl_scene, (json) => {
scope.dispatchEvent('compile', {model: json, options});
@@ -82,14 +139,17 @@ var codec = new Codec('gltf', {
export() {
var scope = codec;
scope.compile(0, content => {
- Blockbench.export({
- type: scope.name,
- extensions: [scope.extension],
- name: scope.fileName(),
- startpath: scope.startPath(),
- content,
- custom_writer: isApp ? (a, b) => scope.write(a, b) : null,
- }, path => scope.afterDownload(path))
+ setTimeout(_ => {
+ Blockbench.export({
+ resource_id: 'gltf',
+ type: scope.name,
+ extensions: [scope.extension],
+ name: scope.fileName(),
+ startpath: scope.startPath(),
+ content,
+ custom_writer: isApp ? (a, b) => scope.write(a, b) : null,
+ }, path => scope.afterDownload(path))
+ }, 20)
})
}
})
diff --git a/js/io/io.js b/js/io/io.js
index 53473941f..44b0f34a4 100644
--- a/js/io/io.js
+++ b/js/io/io.js
@@ -27,6 +27,7 @@ class ModelFormat {
this.box_uv = false;
this.optional_box_uv = false;
this.single_texture = false;
+ this.animated_textures = false;
this.bone_rig = false;
this.centered_grid = false;
this.rotate_cubes = false;
@@ -47,6 +48,7 @@ class ModelFormat {
Merge.boolean(this, data, 'box_uv');
Merge.boolean(this, data, 'optional_box_uv');
Merge.boolean(this, data, 'single_texture');
+ Merge.boolean(this, data, 'animated_textures');
Merge.boolean(this, data, 'bone_rig');
Merge.boolean(this, data, 'centered_grid');
Merge.boolean(this, data, 'rotate_cubes');
@@ -81,6 +83,9 @@ class ModelFormat {
preview.loadAnglePreset(DefaultCameraPresets[preview.angle+1])
}
})
+ uv_dialog.all_editors.forEach(editor => {
+ editor.img.style.objectFit = Format.animated_textures ? 'cover' : 'fill';
+ })
updateSelection()
Modes.vue.$forceUpdate()
Canvas.updateRenderSides()
@@ -257,6 +262,7 @@ class Codec {
export() {
var scope = this;
Blockbench.export({
+ resource_id: 'model',
type: scope.name,
extensions: [scope.extension],
name: scope.fileName(),
@@ -324,60 +330,6 @@ class Codec {
}
}
-//New
-function resetProject() {
- Blockbench.dispatchEvent('reset_project');
- if (Toolbox.selected.id !== 'move_tool') BarItems.move_tool.select();
- Format = 0;
- elements.length = 0;
- Outliner.root.purge();
- Canvas.materials.length = 0;
- textures.length = 0;
- selected.length = 0;
-
- Screencam.stopTimelapse();
-
- Group.all.empty();
- Group.selected = undefined;
- Cube.all.empty();
- Cube.selected.empty();
- Locator.all.empty();
- Locator.selected.empty();
-
- Blockbench.display_settings = display = {};
- Project.name = Project.parent = Project.geometry_name = Project.description = '';
- Project.texture_width = Project.texture_height = 16;
- Project.ambientocclusion = true;
- Project.front_gui_light = false;
- ModelMeta.save_path = ModelMeta.export_path = ModelMeta.animation_path = ModelMeta.name = '';
- ModelMeta.saved = true;
- Prop.project_saved = true;
- Prop.added_models = 0;
- Canvas.updateAll();
- Outliner.vue.$forceUpdate();
- texturelist.$forceUpdate();
- Undo.history.length = 0;
- Undo.index = 0;
- Undo.current_save = null;
- Painter.current = {};
- Animator.animations.purge();
- Timeline.animators.purge();
- Animator.selected = undefined;
- $('#var_placeholder_area').val('');
-}
-function newProject(format, force) {
- if (force || showSaveDialog()) {
- resetProject();
- Modes.options.edit.select();
- if (format instanceof ModelFormat) {
- format.select();
- }
- Blockbench.dispatchEvent('new_project');
- return true;
- } else {
- return false;
- }
-}
//Import
function setupDragHandlers() {
@@ -399,7 +351,7 @@ function setupDragHandlers() {
'plugin',
{extensions: ['bbplugin', 'js']},
function(files) {
- loadPluginFromFile(files[0])
+ new Plugin().loadFromFile(files[0], true)
}
)
Blockbench.addDragHandler(
@@ -661,8 +613,8 @@ function uploadSketchfabModel() {
title: 'dialog.sketchfab_uploader.title',
width: 540,
form: {
- token: {label: 'dialog.sketchfab_uploader.token', value: settings.sketchfab_token.value},
- about_token: {type: 'text', text: tl('dialog.sketchfab_uploader.about_token', ['[sketchfab.com/settings/password](https://sketchfab.com/settings/password)'])},
+ token: {label: 'dialog.sketchfab_uploader.token', value: settings.sketchfab_token.value, type: 'password'},
+ about_token: {type: 'info', text: tl('dialog.sketchfab_uploader.about_token', ['[sketchfab.com/settings/password](https://sketchfab.com/settings/password)'])},
name: {label: 'dialog.sketchfab_uploader.name'},
description: {label: 'dialog.sketchfab_uploader.description', type: 'textarea'},
tags: {label: 'dialog.sketchfab_uploader.tags', placeholder: 'Tag1 Tag2'},
@@ -696,24 +648,6 @@ function uploadSketchfabModel() {
settings.sketchfab_token.value = formResult.token
-
- /*
-
- var archive = new JSZip();
- var model_data = Codecs.obj.compile({all_files: true})
- archive.file('model.obj', model_data.obj)
- archive.file('model.mtl', model_data.mtl)
- for (var key in model_data.images) {
- var tex = model_data.images[key];
- if (tex) {
- archive.file(pathToName(tex.name) + '.png', tex.getBase64(), {base64: true});
- }
- }
- archive.generateAsync({type: 'blob'}).then(blob => {
- var file = new File([blob], 'model.zip', {type: 'application/x-zip-compressed'})
- */
-
-
Codecs.gltf.compile({animations: formResult.animations}, (content) => {
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
@@ -870,142 +804,12 @@ BARS.defineActions(function() {
icon: 'icon-format_free',
rotate_cubes: true,
bone_rig: true,
- centered_grid: false,
+ centered_grid: true,
optional_box_uv: true,
uv_rotation: true,
animation_mode: true,
})
- //Project
- new Action('project_window', {
- icon: 'featured_play_list',
- category: 'file',
- condition: () => Format,
- click: function () {
-
- var dialog = new Dialog({
- id: 'project',
- title: 'dialog.project.title',
- width: 540,
- form: {
- format: {type: 'text', label: 'data.format', text: Format.name||'unknown'},
- name: {label: 'dialog.project.name', value: Project.name},
- parent: {label: 'dialog.project.parent', value: Project.parent, condition: !Format.bone_rig, list: ['paro', 'foo', 'bar']},
- geometry_name: {label: 'dialog.project.geoname', value: Project.geometry_name, condition: Format.bone_rig},
- ambientocclusion: {label: 'dialog.project.ao', type: 'checkbox', value: Project.ambientocclusion, condition: Format.id == 'java_block'},
- box_uv: {label: 'dialog.project.box_uv', type: 'checkbox', value: Project.box_uv, condition: Format.optional_box_uv},
- texture_width: {
- label: 'dialog.project.width',
- type: 'number',
- value: Project.texture_width,
- min: 1
- },
- texture_height: {
- label: 'dialog.project.height',
- type: 'number',
- value: Project.texture_height,
- min: 1
- },
- },
- onConfirm: function(formResult) {
- var save;
- if (Project.box_uv != formResult.box_uv ||
- Project.texture_width != formResult.texture_width ||
- Project.texture_height != formResult.texture_height
- ) {
- if (!Project.box_uv && !formResult.box_uv
- && (Project.texture_width != formResult.texture_width
- || Project.texture_height != formResult.texture_height)
- ) {
- save = Undo.initEdit({uv_only: true, elements: Cube.all, uv_mode: true})
- Cube.all.forEach(cube => {
- for (var key in cube.faces) {
- var uv = cube.faces[key].uv;
- uv[0] *= formResult.texture_width / Project.texture_width;
- uv[2] *= formResult.texture_width / Project.texture_width;
- uv[1] *= formResult.texture_height / Project.texture_height;
- uv[3] *= formResult.texture_height / Project.texture_height;
- }
- })
- } else {
- save = Undo.initEdit({uv_mode: true})
- }
- Project.texture_width = formResult.texture_width;
- Project.texture_height = formResult.texture_height;
-
- if (Format.optional_box_uv) Project.box_uv = formResult.box_uv;
- Canvas.updateAllUVs()
- updateSelection()
- }
-
- Project.name = formResult.name;
- Project.parent = formResult.parent;
- Project.geometry_name = formResult.geometry_name;
- Project.ambientocclusion = formResult.ambientocclusion;
-
- if (save) {
- Undo.finishEdit('change global UV')
- }
-
- BARS.updateConditions()
- if (EditSession.active) {
- EditSession.sendAll('change_project_meta', JSON.stringify(Project));
- }
- dialog.hide()
- }
- })
- dialog.show()
- }
- })
- new Action('close_project', {
- icon: 'cancel_presentation',
- category: 'file',
- condition: () => (!EditSession.active || EditSession.hosting) && Format,
- click: function () {
- if (showSaveDialog()) {
- resetProject()
- Modes.options.start.select()
- Modes.vue.$forceUpdate()
- Blockbench.dispatchEvent('close_project');
- }
- }
- })
- new Action('convert_project', {
- icon: 'fas.fa-file-import',
- category: 'file',
- condition: () => (!EditSession.active || EditSession.hosting),
- click: function () {
-
- var options = {};
- for (var key in Formats) {
- if (key !== Format.id && key !== 'skin') {
- options[key] = Formats[key].name;
- }
- }
- var dialog = new Dialog({
- id: 'convert_project',
- title: 'dialog.convert_project.title',
- width: 540,
- form: {
- text: {type: 'text', text: 'dialog.convert_project.text'},
- format: {
- label: 'data.format',
- type: 'select',
- default: Format.id,
- options,
- },
- },
- onConfirm: function(formResult) {
- var format = Formats[formResult.format]
- if (format && format != Format) {
- format.convertTo()
- }
- dialog.hide()
- }
- })
- dialog.show()
- }
- })
//Import
new Action('open_model', {
icon: 'assessment',
@@ -1023,6 +827,7 @@ BARS.defineActions(function() {
}
}
Blockbench.import({
+ resource_id: 'model',
extensions: ['json', 'jem', 'jpm', 'java', 'bbmodel'],
type: 'Model',
startpath
@@ -1037,6 +842,7 @@ BARS.defineActions(function() {
condition: _ => (Format.id == 'java_block'),
click: function () {
Blockbench.import({
+ resource_id: 'model',
extensions: ['json'],
type: 'JSON Model',
multiple: true,
@@ -1054,6 +860,7 @@ BARS.defineActions(function() {
condition: _ => !Project.box_uv,
click: function () {
Blockbench.import({
+ resource_id: 'texture',
extensions: ['png'],
type: 'PNG Texture',
readtype: 'image'
diff --git a/js/io/java_block.js b/js/io/java_block.js
index 458dcdd60..c35264f4b 100644
--- a/js/io/java_block.js
+++ b/js/io/java_block.js
@@ -9,7 +9,7 @@ var codec = new Codec('java_block', {
var clear_elements = []
var textures_used = []
var element_index_lut = []
- var largerCubesNr = 0;
+ var overflow_cubes = [];
function computeCube(s) {
if (s.export == false) return;
@@ -100,7 +100,7 @@ var codec = new Codec('java_block', {
element.faces = e_faces
function inVd(n) {
- return n > 32 || n < -16
+ return n < -16 || n > 32;
}
if (inVd(element.from[0]) ||
inVd(element.from[1]) ||
@@ -109,7 +109,7 @@ var codec = new Codec('java_block', {
inVd(element.to[1]) ||
inVd(element.to[2])
) {
- largerCubesNr++;
+ overflow_cubes.push(s);
}
if (Object.keys(element.faces).length) {
clear_elements.push(element)
@@ -155,17 +155,17 @@ var codec = new Codec('java_block', {
}
})
- //if (options.prevent_dialog !== true && hasUnsavedTextures && settings.dialog_unsaved_textures.value) {
- // Blockbench.showMessageBox({
- // translateKey: 'unsaved_textures',
- // icon: 'broken_image',
- // })
- //}
- if (options.prevent_dialog !== true && largerCubesNr > 0 && settings.dialog_larger_cubes.value) {
+ if (options.prevent_dialog !== true && overflow_cubes.length > 0 && settings.dialog_larger_cubes.value) {
Blockbench.showMessageBox({
translateKey: 'model_clipping',
icon: 'settings_overscan',
- message: tl('message.model_clipping.message', [largerCubesNr])
+ message: tl('message.model_clipping.message', [overflow_cubes.length]),
+ buttons: ['dialog.scale.select_overflow', 'dialog.ok']
+ }, (result) => {
+ if (result == 0) {
+ selected.splice(0, Infinity, ...overflow_cubes)
+ updateSelection();
+ }
})
}
if (options.prevent_dialog !== true && clear_elements.length && ['item/generated', 'item/handheld'].includes(Project.parent)) {
@@ -246,7 +246,6 @@ var codec = new Codec('java_block', {
this.dispatchEvent('parse', {model});
- var previous_length = add ? elements.length : 0
var previous_texture_length = add ? textures.length : 0
var new_cubes = [];
var new_textures = [];
@@ -445,6 +444,7 @@ var format = new ModelFormat({
rotation_limit: true,
optional_box_uv: true,
uv_rotation: true,
+ animated_textures: true,
display_mode: true,
codec
})
diff --git a/js/io/modded_entity.js b/js/io/modded_entity.js
index 8e2f64ad8..2ab66acef 100644
--- a/js/io/modded_entity.js
+++ b/js/io/modded_entity.js
@@ -1,150 +1,313 @@
(function() {
+function F(num) {
+ var s = trimFloatNumber(num) + '';
+ if (!s.includes('.')) {
+ s += '.0';
+ }
+ return s+'F';
+}
+function I(num) {
+ return Math.floor(num)
+}
+const Templates = {
+ '1.12': {
+ name: '1.12',
+ flip_y: true,
+ integer_size: true,
+ file:
+ `// Made with Blockbench %(bb_version)
+ // Exported for Minecraft version 1.12
+ // Paste this class into your mod and generate all required imports
+
+
+ public class %(identifier) extends ModelBase {
+ %(fields)
+
+ public %(identifier)() {
+ textureWidth = %(texture_width);
+ textureHeight = %(texture_height);
+
+ %(content)
+ }
+
+ @Override
+ public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
+ %(renderers)
+ }
+
+ public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z) {
+ modelRenderer.rotateAngleX = x;
+ modelRenderer.rotateAngleY = y;
+ modelRenderer.rotateAngleZ = z;
+ }
+ }`,
+ field: `private final ModelRenderer %(bone);`,
+ bone:
+ `%(bone) = new ModelRenderer(this);
+ %(bone).setRotationPoint(%(x), %(y), %(z));
+ ?(has_parent)%(parent).addChild(%(bone));
+ ?(has_rotation)setRotationAngle(%(bone), %(rx), %(ry), %(rz));
+ %(cubes)`,
+ renderer: `%(bone).render(f5);`,
+ cube: `%(bone).cubeList.add(new ModelBox(%(bone), %(uv_x), %(uv_y), %(x), %(y), %(z), %(dx), %(dy), %(dz), %(inflate), %(mirror)));`,
+ },
+
+ '1.14': {
+ name: '1.14',
+ flip_y: true,
+ integer_size: true,
+ file:
+ `// Made with Blockbench %(bb_version)
+ // Exported for Minecraft version 1.14
+ // Paste this class into your mod and generate all required imports
+
+
+ public class %(identifier) extends EntityModel {
+ %(fields)
+
+ public %(identifier)() {
+ textureWidth = %(texture_width);
+ textureHeight = %(texture_height);
+
+ %(content)
+ }
+
+ @Override
+ public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
+ %(renderers)
+ }
+
+ public void setRotationAngle(RendererModel modelRenderer, float x, float y, float z) {
+ modelRenderer.rotateAngleX = x;
+ modelRenderer.rotateAngleY = y;
+ modelRenderer.rotateAngleZ = z;
+ }
+ }`,
+ field: `private final RendererModel %(bone);`,
+ bone:
+ `%(bone) = new RendererModel(this);
+ %(bone).setRotationPoint(%(x), %(y), %(z));
+ ?(has_parent)%(parent).addChild(%(bone));
+ ?(has_rotation)setRotationAngle(%(bone), %(rx), %(ry), %(rz));
+ %(cubes)`,
+ renderer: `%(bone).render(f5);`,
+ cube: `%(bone).cubeList.add(new ModelBox(%(bone), %(uv_x), %(uv_y), %(x), %(y), %(z), %(dx), %(dy), %(dz), %(inflate), %(mirror)));`,
+ },
+
+ '1.15': {
+ name: '1.15',
+ flip_y: true,
+ integer_size: false,
+ radians: true,
+ file:
+ `// Made with Blockbench %(bb_version)
+ // Exported for Minecraft version 1.15
+ // Paste this class into your mod and generate all required imports
+
+
+ public class %(identifier) extends EntityModel
{
+ %(fields)
+
+ public %(identifier)() {
+ textureWidth = %(texture_width);
+ textureHeight = %(texture_height);
+
+ %(content)
+ }
+
+ @Override
+ public void setRotationAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch){
+ //previously the render function, render code was moved to a method below
+ }
+
+ @Override
+ public void render(MatrixStack matrixStack, IVertexBuilder buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha){
+ %(renderers)
+ }
+
+ public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z) {
+ modelRenderer.rotateAngleX = x;
+ modelRenderer.rotateAngleY = y;
+ modelRenderer.rotateAngleZ = z;
+ }
+ }`,
+ field: `private final ModelRenderer %(bone);`,
+ bone:
+ `%(bone) = new ModelRenderer(this);
+ %(bone).setRotationPoint(%(x), %(y), %(z));
+ ?(has_parent)%(parent).addChild(%(bone));
+ ?(has_rotation)setRotationAngle(%(bone), %(rx), %(ry), %(rz));
+ %(cubes)`,
+ renderer: `%(bone).render(matrixStack, buffer, packedLight, packedOverlay);`,
+ cube: `%(bone).setTextureOffset(%(uv_x), %(uv_y)).addBox(%(x), %(y), %(z), %(dx), %(dy), %(dz), %(inflate), %(mirror));`,
+ },
+
+ get(key, version = Project.modded_entity_version) {
+ let temp = Templates[version][key];
+ if (typeof temp === 'string') temp = temp.replace(/\t\t\t/g, '');
+ return temp;
+ },
+ keepLine(line) {
+ return line.replace(/\?\(\w+\)/, '');
+ },
+ getVariableRegex(name) {
+ return new RegExp(`%\\(${name}\\)`, 'g');
+ }
+}
+
+function getIdentifier() {
+ return Project.geometry_name.replace(/[\s-]+/g, '_') || 'custom_model';
+}
+
var codec = new Codec('modded_entity', {
name: 'Java Class',
extension: 'java',
remember: true,
compile(options) {
- function F(num) {
- var s = trimFloatNumber(num) + '';
- if (!s.includes('.')) {
- s += '.0';
- }
- return s+'F';
- }
-
- var bone_nr = 1
- var model_id = Project.geometry_name.replace(/[\s-]+/g, '_') || 'custom_model';
- var all_groups = getAllGroups();
- var renderers = {};
- var ver = settings.class_export_version.value == '1.14' ? 1 : 0;
- var rendererName = ver == 1 ? 'RendererModel' : 'ModelRenderer'
+ let R = Templates.getVariableRegex;
+ let identifier = getIdentifier();
- var loose_cubes = []
- Outliner.root.forEach(obj => {
- if (obj.type === 'cube') {
- loose_cubes.push(obj)
- }
+ let all_groups = getAllGroups();
+ let loose_cubes = [];
+ Cube.all.forEach(cube => {
+ if (cube.parent == 'root') loose_cubes.push(cube)
})
if (loose_cubes.length) {
- var group = {
+ all_groups.push({
name: 'bb_main',
rotation: [0, 0, 0],
origin: [0, 0, 0],
parent: 'root',
children: loose_cubes
- }
- all_groups.splice(0, 0, group)
+ })
}
- all_groups.forEach((g) => {
- //model += `\nthis.bone${bone_nr} = new ModelRenderer`
- var id = g.name
- bone_nr++;
- if (g.export === false) return;
-
- var bone = {
- id: id,
- rootBone: g.parent instanceof Group == false,
- lines: [
- `${id} = new ${rendererName}(this);`,//Texture Offset
- ]
- }
- var origin = [-g.origin[0], -g.origin[1], g.origin[2]]
- //Rotation
- if (!g.rotation.allEqual(0)) {
- bone.lines.push(
- `setRotationAngle(${id}, ${
- F(Math.degToRad(-g.rotation[0])) }, ${
- F(Math.degToRad(-g.rotation[1])) }, ${
- F(Math.degToRad(g.rotation[2])) });`
- )
- }
- //Parent
- if (!bone.rootBone && all_groups.indexOf(g.parent) >= 0) {
- bone.lines.push(
- `${ g.parent.name }.addChild(${id});`
- )
- origin[0] += g.parent.origin[0]
- origin[1] += g.parent.origin[1]
- origin[2] -= g.parent.origin[2]
- } else {
- origin[1] += 24
+ let model = Templates.get('file');
+
+ model = model.replace(R('bb_version'), Blockbench.version);
+ model = model.replace(R('identifier'), identifier);
+ model = model.replace(R('texture_width'), Project.texture_width);
+ model = model.replace(R('texture_height'), Project.texture_height);
+
+ model = model.replace(R('fields'), () => {
+ let group_snippets = [];
+ for (var group of all_groups) {
+ if (group instanceof Group === false || !group.export) continue;
+ let snippet = Templates.get('field')
+ .replace(R('bone'), group.name)
+ group_snippets.push(snippet);
}
- //origin
- bone.lines.splice(1, 0,
- `${id}.setRotationPoint(${F(origin[0])}, ${F(origin[1])}, ${F(origin[2])});`
- )
-
- //Boxes
- g.children.forEach((obj) => {
- if (obj.export === false || obj.type !== 'cube') return;
- var values = [
- ''+id,
- Math.floor(obj.uv_offset[0]),
- Math.floor(obj.uv_offset[1]),
- F(g.origin[0] - obj.to[0]),
- F(-obj.from[1] - obj.size(1, true) + g.origin[1]),
- F(obj.from[2] - g.origin[2]),
- obj.size(0, true),
- obj.size(1, true),
- obj.size(2, true),
- F(obj.inflate),
- obj.mirror_uv
- ]
- bone.lines.push(
- `${id}.cubeList.add(new ModelBox(${ values.join(', ') }));`
- )
- })
+ return group_snippets.join('\n\t')
+ });
- renderers[id] = bone;
+ model = model.replace(R('content'), () => {
- })
+ let group_snippets = [];
+ for (var group of all_groups) {
+ if (group instanceof Group === false || !group.export) continue;
+ let snippet = Templates.get('bone')
- var model = (settings.credit.value
- ? '// '+settings.credit.value+'\n'
- : '')+
- '// Paste this code into your mod.\n' +
- '// Make sure to generate all required imports\n'
- if (ver == 1) {
- model += '\npublic class '+model_id+' extends EntityModel {';
- } else {
- model += '\npublic class '+model_id+' extends ModelBase {';
- }
+ .replace(R('bone'), group.name)
- for (var r_id in renderers) {
- model += `\n private final ${rendererName} ${r_id};`;
- }
+ .replace(/\n\?\(has_rotation\).+/, group.rotation.allEqual(0) ? '' : Templates.keepLine)
- model += '\n'+
- '\n public '+model_id+'() {'+
- '\n textureWidth = '+ (Project.texture_width || 32) +';'+
- '\n textureHeight = '+ (Project.texture_height|| 32) +';\n';
+ if (Templates.get('radians')) {
+ snippet = snippet
+ .replace(R('rx'), F(Math.degToRad(-group.rotation[0])))
+ .replace(R('ry'), F(Math.degToRad(-group.rotation[1])))
+ .replace(R('rz'), F(Math.degToRad(group.rotation[2])))
+ } else {
+ snippet = snippet
+ .replace(R('rx'), F(-group.rotation[0]))
+ .replace(R('ry'), F(-group.rotation[1]))
+ .replace(R('rz'), F(group.rotation[2]))
+ }
- for (var r_id in renderers) {
- model += `\n ${renderers[r_id].lines.join('\n ')}\n`;
- }
- model +=
- ' }\n'+
- '\n @Override'+
- '\n public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {'
-
- for (var r_id in renderers) {
- if (renderers[r_id].rootBone) {
- model += `\n ${r_id}.render(f5);`;
+ var origin = group.origin.slice();
+ if (group.parent instanceof Group) {
+ origin.V3_subtract(group.parent.origin)
+ }
+ origin[0] *= -1;
+ if (Templates.get('flip_y')) {
+ origin[1] *= -1;
+ if (group.parent instanceof Group === false) {
+ origin[1] += 24
+ }
+ }
+
+ snippet = snippet
+ .replace(R('x'), F(origin[0]))
+ .replace(R('y'), F(origin[1]))
+ .replace(R('z'), F(origin[2]))
+
+ .replace(/\n\?\(has_parent\).+/, group.parent instanceof Group ? Templates.keepLine : '')
+ .replace(R('parent'), group.parent.name)
+
+ .replace(R('cubes'), () => {
+
+ let cube_snippets = [];
+ for (var cube of group.children) {
+ if (cube instanceof Cube === false || !cube.export) continue;
+
+ let c_snippet = Templates.get('cube')
+ .replace(R('bone'), group.name)
+
+ .replace(R('uv_x'), I(cube.uv_offset[0]))
+ .replace(R('uv_y'), I(cube.uv_offset[1]))
+
+ .replace(R('inflate'), F(cube.inflate))
+ .replace(R('mirror'), cube.mirror_uv)
+
+ if (Templates.get('flip_y')) {
+ c_snippet = c_snippet
+ .replace(R('x'), F(group.origin[0] - cube.to[0]) )
+ .replace(R('y'), F(-cube.from[1] - cube.size(1) + group.origin[1]) )
+ .replace(R('z'), F(cube.from[2] - group.origin[2]) )
+
+ } else {
+ c_snippet = c_snippet
+ .replace(R('x'), F(group.origin[0] - cube.to[0]) )
+ .replace(R('y'), F(cube.from[1] - group.origin[1]) )
+ .replace(R('z'), F(cube.from[2] - group.origin[2]) )
+ }
+ if (Templates.get('integer_size')) {
+ c_snippet = c_snippet
+ .replace(R('dx'), I(cube.size(0, true)) )
+ .replace(R('dy'), I(cube.size(1, true)) )
+ .replace(R('dz'), I(cube.size(2, true)) )
+
+ } else {
+ c_snippet = c_snippet
+ .replace(R('dx'), F(cube.size(0)) )
+ .replace(R('dy'), F(cube.size(1)) )
+ .replace(R('dz'), F(cube.size(2)) )
+ }
+
+ cube_snippets.push(c_snippet);
+ }
+ return cube_snippets.join('\n');
+ })
+ .replace(/\n/g, '\n\t\t')
+
+ group_snippets.push(snippet);
}
- }
- model +=
- '\n }'+
- `\n public void setRotationAngle(${rendererName} modelRenderer, float x, float y, float z) {`+
- '\n modelRenderer.rotateAngleX = x;'+
- '\n modelRenderer.rotateAngleY = y;'+
- '\n modelRenderer.rotateAngleZ = z;'+
- '\n }'+
- '\n}';
+ return group_snippets.join('\n\n\t\t')
+ });
+
+ model = model.replace(R('renderers'), () => {
+ let group_snippets = [];
+ for (var group of all_groups) {
+ if (group instanceof Group === false || !group.export) continue;
+ let snippet = Templates.get('renderer')
+ .replace(R('bone'), group.name)
+ group_snippets.push(snippet);
+ }
+ return group_snippets.join('\n\t\t')
+ });
this.dispatchEvent('compile', {model, options});
return model;
@@ -160,11 +323,6 @@ var codec = new Codec('modded_entity', {
}
})
- var getArgs = function(input) {
- var i = input.search('(')
- var args = input.substr(i+1).replace(/\)$/, '');
- return args.split(/, ?/);
- }
function parseScheme(scheme, input) {
scheme = scheme.replace(/\(/g, '\\(').replace(/\)/g, '\\)').replace(/\./g, '\\.');
var parts = scheme.split('$');
@@ -269,6 +427,18 @@ var codec = new Codec('modded_entity', {
last_uv = [match[1], match[2]];
} else
+ if (
+ parseScheme('$v = new ModelRenderer(this)', line)
+ ) {
+ // Blockbench for 1.15
+ if (!bones[match[0]]) {
+ bones[match[0]] = new Group({
+ name: match[0],
+ origin: [0, 0, 0]
+ }).init();
+ }
+ } else
+
if (parseScheme('$v.setRotationPoint($f, $f, $f)', line)) {
var bone = bones[match[0]]
if (bone) {
@@ -288,6 +458,7 @@ var codec = new Codec('modded_entity', {
child.addTo(parent);
child.origin.V3_add(parent.origin)
child.origin[1] -= 24;
+
child.children.forEach(cube => {
if (cube instanceof Cube) {
cube.from[0] += parent.origin[0]; cube.to[0] += parent.origin[0];
@@ -347,6 +518,30 @@ var codec = new Codec('modded_entity', {
cube.addTo(bones[match[0]]).init();
} else
+ if (parseScheme('$v.setTextureOffset($i, $i).addBox($f, $f, $f, $f, $f, $f, $f, $b)', line)
+ ) {
+ var group = bones[match[0]];
+ var cube = new Cube({
+ name: match[0],
+ uv_offset: [match[1], match[2]],
+ from: [
+ group.origin[0] - match[3] - match[6],
+ group.origin[1] - match[4] - match[7],
+ group.origin[2] + match[5]
+ ],
+ inflate: match[9],
+ mirror_uv: match[10]
+ })
+ cube.extend({
+ to: [
+ cube.from[0] + match[6],
+ cube.from[1] + match[7],
+ cube.from[2] + match[8],
+ ]
+ });
+ cube.addTo(bones[match[0]]).init();
+ } else
+
//Rotation
if (parseScheme('setRotationAngle($v, $f, $f, $f)', line)) {
@@ -423,8 +618,12 @@ var codec = new Codec('modded_entity', {
}
})
Canvas.updateAll();
+ },
+ fileName() {
+ return getIdentifier();
}
})
+codec.templates = Templates;
var format = new ModelFormat({
id: 'modded_entity',
@@ -432,12 +631,13 @@ var format = new ModelFormat({
codec,
box_uv: true,
single_texture: true,
- integer_size: true,
bone_rig: true,
centered_grid: true,
})
+Object.defineProperty(format, 'integer_size', {get: _ => Templates.get('integer_size')})
codec.format = format;
+
BARS.defineActions(function() {
new Action({
id: 'export_class_entity',
diff --git a/js/io/obj.js b/js/io/obj.js
index a5ea50f48..4dd747c21 100644
--- a/js/io/obj.js
+++ b/js/io/obj.js
@@ -210,6 +210,7 @@ var codec = new Codec('obj', {
var scope = this;
if (isApp) {
Blockbench.export({
+ resource_id: 'obj',
type: this.name,
extensions: [this.extension],
name: this.fileName(),
diff --git a/js/io/optifine_jem.js b/js/io/optifine_jem.js
index f05061b61..5039cef43 100644
--- a/js/io/optifine_jem.js
+++ b/js/io/optifine_jem.js
@@ -259,6 +259,7 @@ var format = new ModelFormat({
centered_grid: true,
codec
})
+Object.defineProperty(format, 'integer_size', {get: _ => Format.box_uv})
codec.format = format;
diff --git a/js/io/optifine_jpm.js b/js/io/optifine_jpm.js
index 3bfb49d04..688806586 100644
--- a/js/io/optifine_jpm.js
+++ b/js/io/optifine_jpm.js
@@ -241,6 +241,7 @@ BARS.defineActions(function() {
condition: () => (Format.id == 'optifine_entity' || Format.id == 'optifine_part'),
click: function () {
Blockbench.import({
+ resource_id: 'model',
extensions: ['jpm'],
type: 'JPM Entity Part Model',
multiple: true,
diff --git a/js/io/project.js b/js/io/project.js
new file mode 100644
index 000000000..b5b881487
--- /dev/null
+++ b/js/io/project.js
@@ -0,0 +1,238 @@
+const Project = {
+ name : '',
+ parent : '',
+ geometry_name : '',
+ description : '',
+ _box_uv : false,
+ get box_uv() {return Project._box_uv},
+ set box_uv(v) {
+ if (Project._box_uv != v) {
+ Project._box_uv = v;
+ switchBoxUV(v);
+ }
+ },
+ get texture_width() {return Project._texture_width},
+ get texture_height() {return Project._texture_height},
+ set texture_width(n) {
+ n = parseInt(n)||16
+ Vue.nextTick(updateProjectResolution)
+ Project._texture_width = n;
+ },
+ set texture_height(n) {
+ n = parseInt(n)||16
+ Vue.nextTick(updateProjectResolution)
+ Project._texture_height = n;
+ },
+ _texture_width : 16,
+ _texture_height : 16,
+ ambientocclusion: true,
+ front_gui_light: false,
+ visible_box: [1, 1, 0], /*width, height, y*/
+ modded_entity_version: '1.15',
+ get optional_box_uv() {
+ return Format.optional_box_uv;
+ }
+}
+
+
+//New
+function resetProject() {
+ Blockbench.dispatchEvent('reset_project');
+ if (Toolbox.selected.id !== 'move_tool') BarItems.move_tool.select();
+ Format = 0;
+ elements.length = 0;
+ Outliner.root.purge();
+ Canvas.materials.length = 0;
+ textures.length = 0;
+ selected.length = 0;
+
+ Screencam.stopTimelapse();
+
+ Group.all.empty();
+ Group.selected = undefined;
+ Cube.all.empty();
+ Cube.selected.empty();
+ Locator.all.empty();
+ Locator.selected.empty();
+
+ Blockbench.display_settings = display = {};
+ Project.name = Project.parent = Project.geometry_name = Project.description = '';
+ Project.texture_width = Project.texture_height = 16;
+ Project.ambientocclusion = true;
+ Project.front_gui_light = false;
+ Project.modded_entity_version = '1.15';
+ Project.visible_box.splice(0, Infinity, ...[1, 1, 0])
+ ModelMeta.save_path = ModelMeta.export_path = ModelMeta.animation_path = ModelMeta.name = '';
+ ModelMeta.saved = true;
+ Prop.project_saved = true;
+ Prop.added_models = 0;
+ Canvas.updateAll();
+ Outliner.vue.$forceUpdate();
+ texturelist.$forceUpdate();
+ Undo.history.length = 0;
+ Undo.index = 0;
+ Undo.current_save = null;
+ Painter.current = {};
+ Animator.animations.purge();
+ Timeline.animators.purge();
+ Animator.selected = undefined;
+ $('#var_placeholder_area').val('');
+}
+function newProject(format, force) {
+ if (force || showSaveDialog()) {
+ resetProject();
+ Modes.options.edit.select();
+ if (format instanceof ModelFormat) {
+ format.select();
+ }
+ Blockbench.dispatchEvent('new_project');
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+BARS.defineActions(function() {
+
+ new Action('project_window', {
+ icon: 'featured_play_list',
+ category: 'file',
+ condition: () => Format,
+ click: function () {
+
+ let modded_entity_options = {}
+ for (var key in Codecs.modded_entity.templates) {
+ if (Codecs.modded_entity.templates[key] instanceof Function == false) {
+ modded_entity_options[key] = Codecs.modded_entity.templates[key].name;
+ }
+ }
+ var dialog = new Dialog({
+ id: 'project',
+ title: 'dialog.project.title',
+ width: 540,
+ form: {
+ format: {type: 'info', label: 'data.format', text: Format.name||'unknown'},
+ name: {label: 'dialog.project.name', value: Project.name},
+
+ parent: {label: 'dialog.project.parent', value: Project.parent, condition: !Format.bone_rig, list: ['paro', 'foo', 'bar']},
+ geometry_name: {label: 'dialog.project.geoname', value: Project.geometry_name, condition: Format.bone_rig},
+ modded_entity_version: {label: 'dialog.project.modded_entity_version', type: 'select', default: Project.modded_entity_version, options: modded_entity_options, condition: Format.id == 'modded_entity'},
+ ambientocclusion: {label: 'dialog.project.ao', type: 'checkbox', value: Project.ambientocclusion, condition: Format.id == 'java_block'},
+
+ box_uv: {label: 'dialog.project.box_uv', type: 'checkbox', value: Project.box_uv, condition: Format.optional_box_uv},
+ texture_width: {
+ label: 'dialog.project.width',
+ type: 'number',
+ value: Project.texture_width,
+ min: 1
+ },
+ texture_height: {
+ label: 'dialog.project.height',
+ type: 'number',
+ value: Project.texture_height,
+ min: 1
+ },
+ },
+ onConfirm: function(formResult) {
+ var save;
+ if (Project.box_uv != formResult.box_uv ||
+ Project.texture_width != formResult.texture_width ||
+ Project.texture_height != formResult.texture_height
+ ) {
+ if (!Project.box_uv && !formResult.box_uv
+ && (Project.texture_width != formResult.texture_width
+ || Project.texture_height != formResult.texture_height)
+ ) {
+ save = Undo.initEdit({uv_only: true, elements: Cube.all, uv_mode: true})
+ Cube.all.forEach(cube => {
+ for (var key in cube.faces) {
+ var uv = cube.faces[key].uv;
+ uv[0] *= formResult.texture_width / Project.texture_width;
+ uv[2] *= formResult.texture_width / Project.texture_width;
+ uv[1] *= formResult.texture_height / Project.texture_height;
+ uv[3] *= formResult.texture_height / Project.texture_height;
+ }
+ })
+ } else {
+ save = Undo.initEdit({uv_mode: true})
+ }
+ Project.texture_width = formResult.texture_width;
+ Project.texture_height = formResult.texture_height;
+
+ if (Format.optional_box_uv) Project.box_uv = formResult.box_uv;
+ Canvas.updateAllUVs()
+ updateSelection()
+ }
+
+ Project.name = formResult.name;
+ Project.parent = formResult.parent;
+ Project.geometry_name = formResult.geometry_name;
+ Project.ambientocclusion = formResult.ambientocclusion;
+ if (formResult.modded_entity_version) Project.modded_entity_version = formResult.modded_entity_version;
+
+ if (save) {
+ Undo.finishEdit('change global UV')
+ }
+
+ BARS.updateConditions()
+ if (EditSession.active) {
+ EditSession.sendAll('change_project_meta', JSON.stringify(Project));
+ }
+ dialog.hide()
+ }
+ })
+ dialog.show()
+ }
+ })
+ new Action('close_project', {
+ icon: 'cancel_presentation',
+ category: 'file',
+ condition: () => (!EditSession.active || EditSession.hosting) && Format,
+ click: function () {
+ if (showSaveDialog()) {
+ resetProject()
+ Modes.options.start.select()
+ Modes.vue.$forceUpdate()
+ Blockbench.dispatchEvent('close_project');
+ }
+ }
+ })
+ new Action('convert_project', {
+ icon: 'fas.fa-file-import',
+ category: 'file',
+ condition: () => (!EditSession.active || EditSession.hosting),
+ click: function () {
+
+ var options = {};
+ for (var key in Formats) {
+ if (key !== Format.id && key !== 'skin') {
+ options[key] = Formats[key].name;
+ }
+ }
+
+ var dialog = new Dialog({
+ id: 'convert_project',
+ title: 'dialog.convert_project.title',
+ width: 540,
+ form: {
+ text: {type: 'info', text: 'dialog.convert_project.text'},
+ format: {
+ label: 'data.format',
+ type: 'select',
+ default: Format.id,
+ options,
+ },
+ },
+ onConfirm: function(formResult) {
+ var format = Formats[formResult.format]
+ if (format && format != Format) {
+ format.convertTo()
+ }
+ dialog.hide()
+ }
+ })
+ dialog.show()
+ }
+ })
+})
\ No newline at end of file
diff --git a/js/io/skin.js b/js/io/skin.js
index 571ab1359..9612f4bff 100644
--- a/js/io/skin.js
+++ b/js/io/skin.js
@@ -74,7 +74,7 @@ const codec = new Codec('skin_model', {
this.dispatchEvent('compile', {model: entitymodel, options});
return entitymodel
},
- parse(data, resolution, texture_path) {
+ parse(data, resolution, texture_path, pose = true) {
this.dispatchEvent('parse', {model: data});
Project.geometry_name = data.name;
Project.texture_width = data.texturewidth || 64;
@@ -91,7 +91,7 @@ const codec = new Codec('skin_model', {
var group = new Group({
name: b.name,
origin: b.pivot,
- rotation: b.rotation
+ rotation: (pose && b.pose) ? b.pose : b.rotation
}).init()
group.isOpen = true;
bones[b.name] = group
@@ -176,7 +176,7 @@ const codec = new Codec('skin_model', {
EditSession.initNewModel()
},
})
-
+codec.compile = null;
const format = new ModelFormat({
@@ -211,7 +211,7 @@ function generateTemplate(width = 64, height = 64, name = 'name', eyes) {
canvas.height = height;
Cube.all.forEach(cube => {
- TextureGenerator.paintCubeBoxTemplate(cube, texture, canvas);
+ TextureGenerator.paintCubeBoxTemplate(cube, texture, canvas, null, !!cube.inflate);
})
if (eyes) {
var res_multiple = canvas.width/Project.texture_width;
@@ -256,7 +256,8 @@ const skin_dialog = new Dialog({
cod: 'Cod',
cow: 'Cow',
creeper: 'Creeper',
- dolphin: 'Dolphin',
+ dolphin_bedrock: 'Dolphin (Bedrock)',
+ dolphin_java: 'Dolphin (Java)',
enderdragon: 'Ender Dragon',
enderman: 'Enderman',
endermite: 'Endermite',
@@ -293,6 +294,7 @@ const skin_dialog = new Dialog({
snowgolem: 'Snowgolem',
spider: 'Spider',
squid: 'Squid',
+ strider: 'Strider',
tropicalfish_a: 'Tropicalfish A',
tropicalfish_b: 'Tropicalfish B',
turtle: 'Turtle',
@@ -319,13 +321,14 @@ const skin_dialog = new Dialog({
type: 'file',
extensions: ['png'],
filetype: 'PNG',
- }
+ },
+ pose: {type: 'checkbox', label: 'dialog.skin.pose', value: true}
},
draggable: true,
onConfirm(result) {
if (newProject(format)) {
var model = JSON.parse(skin_presets[result.model]);
- codec.parse(model, result.resolution/16, result.texture);
+ codec.parse(model, result.resolution/16, result.texture, result.pose);
}
this.hide();
},
@@ -374,7 +377,7 @@ skin_presets.steve = `{
"name": "Head",
"color": 1,
"pivot": [0, 24, 0],
- "rotation": [-6, 5, 0],
+ "pose": [-6, 5, 0],
"cubes": [
{"name": "Head", "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
{"name": "Hat Layer", "visibility": false, "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
@@ -393,7 +396,7 @@ skin_presets.steve = `{
"name": "Right Arm",
"color": 5,
"pivot": [-5, 22, 0],
- "rotation": [-10, 0, 0],
+ "pose": [-10, 0, 0],
"cubes": [
{"name": "Right Arm", "origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]},
{"name": "Right Arm Layer", "visibility": false, "origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 32], "inflate": 0.25}
@@ -403,7 +406,7 @@ skin_presets.steve = `{
"name": "Left Arm",
"color": 0,
"pivot": [5, 22, 0],
- "rotation": [12, 0, 0],
+ "pose": [12, 0, 0],
"cubes": [
{"name": "Left Arm", "origin": [4, 12, -2], "size": [4, 12, 4], "uv": [32, 48]},
{"name": "Left Arm Layer", "visibility": false, "origin": [4, 12, -2], "size": [4, 12, 4], "uv": [48, 48], "inflate": 0.25}
@@ -413,7 +416,7 @@ skin_presets.steve = `{
"name": "Right Leg",
"color": 6,
"pivot": [-1.9, 12, 0],
- "rotation": [11, 0, 2],
+ "pose": [11, 0, 2],
"cubes": [
{"name": "Right Leg", "origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 16]},
{"name": "Right Leg Layer", "visibility": false, "origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 32], "inflate": 0.25}
@@ -423,7 +426,7 @@ skin_presets.steve = `{
"name": "Left Leg",
"color": 7,
"pivot": [1.9, 12, 0],
- "rotation": [-10, 0, -2],
+ "pose": [-10, 0, -2],
"cubes": [
{"name": "Left Leg", "origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [16, 48]},
{"name": "Left Leg Layer", "visibility": false, "origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [0, 48], "inflate": 0.25}
@@ -444,7 +447,7 @@ skin_presets.alex = `{
"name": "Head",
"color": 1,
"pivot": [0, 24, 0],
- "rotation": [-6, 5, 0],
+ "pose": [-6, 5, 0],
"cubes": [
{"name": "Head", "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
{"name": "Hat Layer", "visibility": false, "origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
@@ -463,7 +466,7 @@ skin_presets.alex = `{
"name": "Right Arm",
"color": 5,
"pivot": [-5, 22, 0],
- "rotation": [-10, 0, 0],
+ "pose": [-10, 0, 0],
"cubes": [
{"name": "Right Arm", "origin": [-7, 12, -2], "size": [3, 12, 4], "uv": [40, 16]},
{"name": "Right Arm Layer", "visibility": false, "origin": [-7, 12, -2], "size": [3, 12, 4], "uv": [40, 32], "inflate": 0.25}
@@ -473,7 +476,7 @@ skin_presets.alex = `{
"name": "Left Arm",
"color": 0,
"pivot": [5, 22, 0],
- "rotation": [12, 0, 0],
+ "pose": [12, 0, 0],
"cubes": [
{"name": "Left Arm", "origin": [4, 12, -2], "size": [3, 12, 4], "uv": [32, 48]},
{"name": "Left Arm Layer", "visibility": false, "origin": [4, 12, -2], "size": [3, 12, 4], "uv": [48, 48], "inflate": 0.25}
@@ -483,7 +486,7 @@ skin_presets.alex = `{
"name": "Right Leg",
"color": 6,
"pivot": [-1.9, 12, 0],
- "rotation": [11, 0, 2],
+ "pose": [11, 0, 2],
"cubes": [
{"name": "Right Leg", "origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 16]},
{"name": "Right Leg Layer", "visibility": false, "origin": [-3.9, 0, -2], "size": [4, 12, 4], "uv": [0, 32], "inflate": 0.25}
@@ -493,7 +496,7 @@ skin_presets.alex = `{
"name": "Left Leg",
"color": 7,
"pivot": [1.9, 12, 0],
- "rotation": [-10, 0, -2],
+ "pose": [-10, 0, -2],
"cubes": [
{"name": "Left Leg", "origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [16, 48]},
{"name": "Left Leg Layer", "visibility": false, "origin": [-0.1, 0, -2], "size": [4, 12, 4], "uv": [0, 48], "inflate": 0.25}
@@ -615,7 +618,7 @@ skin_presets.bat = `{
"name": "rightWing",
"parent": "body",
"pivot": [0, 24, 0],
- "rotation": [0, -10, 0],
+ "pose": [0, -10, 0],
"cubes": [
{"name": "rightWing", "origin": [-12, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
]
@@ -624,7 +627,7 @@ skin_presets.bat = `{
"name": "rightWingTip",
"parent": "rightWing",
"pivot": [-12, 23, 1.5],
- "rotation": [0, -15, 0],
+ "pose": [0, -15, 0],
"cubes": [
{"name": "rightWingTip", "origin": [-20, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
]
@@ -633,7 +636,7 @@ skin_presets.bat = `{
"name": "leftWing",
"parent": "body",
"pivot": [0, 24, 0],
- "rotation": [0, 10, 0],
+ "pose": [0, 10, 0],
"mirror": true,
"cubes": [
{"name": "leftWing", "origin": [2, 7, 1.5], "size": [10, 16, 1], "uv": [42, 0]}
@@ -643,7 +646,7 @@ skin_presets.bat = `{
"name": "leftWingTip",
"parent": "leftWing",
"pivot": [12, 23, 1.5],
- "rotation": [0, 15, 0],
+ "pose": [0, 15, 0],
"mirror": true,
"cubes": [
{"name": "leftWingTip", "origin": [12, 10, 1.5], "size": [8, 12, 1], "uv": [24, 16]}
@@ -1284,7 +1287,7 @@ skin_presets.creeper = `{
}
]
}`;
-skin_presets.dolphin = `{
+skin_presets.dolphin_bedrock = `{
"name": "dolphin",
"texturewidth": 64,
"textureheight": 64,
@@ -1316,6 +1319,7 @@ skin_presets.dolphin = `{
"name": "tail",
"parent": "body",
"pivot": [0, 2.5, 11],
+ "pose": [-5, 0, 0],
"cubes": [
{"name": "tail", "origin": [-2, 0, 10], "size": [4, 5, 11], "uv": [0, 33]}
]
@@ -1324,6 +1328,7 @@ skin_presets.dolphin = `{
"name": "tail_fin",
"parent": "tail",
"pivot": [0, 2.5, 20],
+ "pose": [-8, 0, 0],
"cubes": [
{"name": "tail_fin", "origin": [-5, 2, 19], "size": [10, 1, 6], "uv": [0, 49]}
]
@@ -1357,6 +1362,81 @@ skin_presets.dolphin = `{
}
]
}`;
+skin_presets.dolphin_java = `{
+ "name": "dolphin",
+ "texturewidth": 64,
+ "textureheight": 64,
+ "bones": [
+ {
+ "name": "body",
+ "pivot": [0, 0, -3],
+ "cubes": [
+ {"name": "body", "origin": [-4, 0, -3], "size": [8, 7, 13], "uv": [22, 0]}
+ ]
+ },
+ {
+ "name": "head",
+ "parent": "body",
+ "pivot": [0, 0, -3],
+ "cubes": [
+ {"name": "head", "origin": [-4, 0, -9], "size": [8, 7, 6], "uv": [0, 0]}
+ ]
+ },
+ {
+ "name": "nose",
+ "parent": "head",
+ "pivot": [0, 0, -13],
+ "cubes": [
+ {"name": "nose", "origin": [-1, 0, -13], "size": [2, 2, 4], "uv": [0, 13]}
+ ]
+ },
+ {
+ "name": "tail",
+ "parent": "body",
+ "pivot": [0, 2.5, 11],
+ "pose": [-5, 0, 0],
+ "cubes": [
+ {"name": "tail", "origin": [-2, 0, 10], "size": [4, 5, 11], "uv": [0, 19]}
+ ]
+ },
+ {
+ "name": "tail_fin",
+ "parent": "tail",
+ "pivot": [0, 2.5, 20],
+ "pose": [-8, 0, 0],
+ "cubes": [
+ {"name": "tail_fin", "origin": [-5, 2, 19], "size": [10, 1, 6], "uv": [19, 20]}
+ ]
+ },
+ {
+ "name": "back_fin",
+ "parent": "body",
+ "pivot": [0, 7, 2],
+ "rotation": [60, 0, 0],
+ "cubes": [
+ {"name": "back_fin", "origin": [-0.5, 3.75, 1.5], "size": [1, 4, 5], "uv": [51, 0]}
+ ]
+ },
+ {
+ "name": "left_fin",
+ "parent": "body",
+ "pivot": [3, 2, 2],
+ "rotation": [55, 0, 107],
+ "cubes": [
+ {"name": "left_fin", "origin": [3, 2, 0.5], "size": [1, 4, 7], "uv": [48, 20]}
+ ]
+ },
+ {
+ "name": "right_fin",
+ "parent": "body",
+ "pivot": [-3, 2, 2],
+ "rotation": [55, 0, -107],
+ "cubes": [
+ {"name": "left_fin", "origin": [-4, 2, 0.5], "size": [1, 4, 7], "uv": [48, 20], "mirror": true}
+ ]
+ }
+ ]
+}`;
skin_presets.enderdragon = `{
"name": "enderdragon",
"texturewidth": 256,
@@ -1365,7 +1445,7 @@ skin_presets.enderdragon = `{
{
"name": "neck",
"pivot": [0, 7, -8],
- "rotation": [-5, 0, 0],
+ "pose": [-5, 0, 0],
"cubes": [
{"name": "neck", "origin": [-5, 2, -18], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "neck", "origin": [-1, 12, -16], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1375,7 +1455,7 @@ skin_presets.enderdragon = `{
"name": "neck2",
"parent": "neck",
"pivot": [0, 7, -18],
- "rotation": [5, 0, 0],
+ "pose": [5, 0, 0],
"cubes": [
{"name": "neck", "origin": [-5, 2, -28], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "neck", "origin": [-1, 12, -26], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1385,7 +1465,7 @@ skin_presets.enderdragon = `{
"name": "neck3",
"parent": "neck2",
"pivot": [0, 7, -28],
- "rotation": [5, 0, 0],
+ "pose": [5, 0, 0],
"cubes": [
{"name": "neck", "origin": [-5, 2, -38], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "neck", "origin": [-1, 12, -36], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1395,7 +1475,7 @@ skin_presets.enderdragon = `{
"name": "neck4",
"parent": "neck3",
"pivot": [0, 7, -38],
- "rotation": [5, 0, 0],
+ "pose": [5, 0, 0],
"cubes": [
{"name": "neck", "origin": [-5, 2, -48], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "neck", "origin": [-1, 12, -46], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1405,7 +1485,7 @@ skin_presets.enderdragon = `{
"name": "neck5",
"parent": "neck4",
"pivot": [0, 7, -48],
- "rotation": [5, 0, 0],
+ "pose": [5, 0, 0],
"cubes": [
{"name": "neck", "origin": [-5, 2, -58], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "neck", "origin": [-1, 12, -56], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1415,7 +1495,7 @@ skin_presets.enderdragon = `{
"name": "head",
"parent": "neck5",
"pivot": [0, 7, -58],
- "rotation": [5, 0, 0],
+ "pose": [5, 0, 0],
"cubes": [
{"name": "head", "origin": [-6, 3, -88], "size": [12, 5, 16], "uv": [176, 44]},
{"name": "head", "origin": [-8, -1, -74], "size": [16, 16, 16], "uv": [112, 30]},
@@ -1429,7 +1509,7 @@ skin_presets.enderdragon = `{
"name": "jaw",
"parent": "head",
"pivot": [0, 3, -71],
- "rotation": [15, 0, 0],
+ "pose": [15, 0, 0],
"cubes": [
{"name": "jaw", "origin": [-6, -1, -88], "size": [12, 4, 16], "uv": [176, 65]}
]
@@ -1447,41 +1527,41 @@ skin_presets.enderdragon = `{
{
"name": "wing",
"pivot": [-12, 19, 2],
- "rotation": [0, 10, 10],
+ "pose": [0, 10, 10],
"cubes": [
{"name": "wing", "origin": [-68, 15, -2], "size": [56, 8, 8], "uv": [112, 88]},
- {"name": "wing", "origin": [-68, 19, 4], "size": [56, 0, 56], "uv": [-56, 88]}
+ {"name": "wing", "origin": [-68, 19, 4], "size": [56, 0, 56], "uv": [-56, 88], "inflate": 0.01}
]
},
{
"name": "wingtip",
"parent": "wing",
"pivot": [-68, 19, 0],
- "rotation": [0, 0, -20],
+ "pose": [0, 0, -20],
"cubes": [
{"name": "wingtip", "origin": [-124, 17, 0], "size": [56, 4, 4], "uv": [112, 136]},
- {"name": "wingtip", "origin": [-124, 19, 4], "size": [56, 0, 56], "uv": [-56, 144]}
+ {"name": "wingtip", "origin": [-124, 19, 4], "size": [56, 0, 56], "uv": [-56, 144], "inflate": 0.01}
]
},
{
"name": "wing1",
"pivot": [12, 19, 2],
- "rotation": [0, -10, -10],
+ "pose": [0, -10, -10],
"mirror": true,
"cubes": [
{"name": "wing1", "origin": [12, 15, -2], "size": [56, 8, 8], "uv": [112, 88]},
- {"name": "wing1", "origin": [12, 19, 4], "size": [56, 0, 56], "uv": [-56, 88]}
+ {"name": "wing1", "origin": [12, 19, 4], "size": [56, 0, 56], "uv": [-56, 88], "inflate": 0.01}
]
},
{
"name": "wingtip1",
"parent": "wing1",
"pivot": [68, 19, 0],
- "rotation": [0, 0, 20],
+ "pose": [0, 0, 20],
"mirror": true,
"cubes": [
{"name": "wingtip1", "origin": [68, 17, 0], "size": [56, 4, 4], "uv": [112, 136]},
- {"name": "wingtip1", "origin": [68, 19, 4], "size": [56, 0, 56], "uv": [-56, 144]}
+ {"name": "wingtip1", "origin": [68, 19, 4], "size": [56, 0, 56], "uv": [-56, 144], "inflate": 0.01}
]
},
{
@@ -1606,7 +1686,7 @@ skin_presets.enderdragon = `{
"name": "tail2",
"parent": "tail",
"pivot": [0, 14, 66],
- "rotation": [1, 0, 0],
+ "pose": [1, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 66], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 68], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1616,7 +1696,7 @@ skin_presets.enderdragon = `{
"name": "tail3",
"parent": "tail2",
"pivot": [0, 14, 76],
- "rotation": [1, 0, 0],
+ "pose": [1, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 76], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 78], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1626,7 +1706,7 @@ skin_presets.enderdragon = `{
"name": "tail4",
"parent": "tail3",
"pivot": [0, 14, 86],
- "rotation": [1, 0, 0],
+ "pose": [1, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 86], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 88], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1636,7 +1716,7 @@ skin_presets.enderdragon = `{
"name": "tail5",
"parent": "tail4",
"pivot": [0, 14, 96],
- "rotation": [2, 0, 0],
+ "pose": [2, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 96], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 98], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1646,7 +1726,7 @@ skin_presets.enderdragon = `{
"name": "tail6",
"parent": "tail5",
"pivot": [0, 14, 106],
- "rotation": [3, 0, 0],
+ "pose": [3, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 106], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 108], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1656,7 +1736,7 @@ skin_presets.enderdragon = `{
"name": "tail7",
"parent": "tail6",
"pivot": [0, 14, 116],
- "rotation": [3, 0, 0],
+ "pose": [3, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 116], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 118], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1666,7 +1746,7 @@ skin_presets.enderdragon = `{
"name": "tail8",
"parent": "tail7",
"pivot": [0, 14, 126],
- "rotation": [1, 0, 0],
+ "pose": [1, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 126], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 128], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1676,7 +1756,7 @@ skin_presets.enderdragon = `{
"name": "tail9",
"parent": "tail8",
"pivot": [0, 14, 136],
- "rotation": [-1, 0, 0],
+ "pose": [-1, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 136], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 138], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1686,7 +1766,7 @@ skin_presets.enderdragon = `{
"name": "tail10",
"parent": "tail9",
"pivot": [0, 14, 146],
- "rotation": [-2, 0, 0],
+ "pose": [-2, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 146], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 148], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1696,7 +1776,7 @@ skin_presets.enderdragon = `{
"name": "tail11",
"parent": "tail10",
"pivot": [0, 14, 156],
- "rotation": [-3, 0, 0],
+ "pose": [-3, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 156], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 158], "size": [2, 4, 6], "uv": [48, 0]}
@@ -1706,7 +1786,7 @@ skin_presets.enderdragon = `{
"name": "tail12",
"parent": "tail11",
"pivot": [0, 14, 166],
- "rotation": [-3, 0, 0],
+ "pose": [-3, 0, 0],
"cubes": [
{"name": "tail", "origin": [-5, 9, 166], "size": [10, 10, 10], "uv": [192, 104]},
{"name": "tail", "origin": [-1, 19, 168], "size": [2, 4, 6], "uv": [48, 0]}
@@ -4310,6 +4390,96 @@ skin_presets.squid = `{
}
]
}`;
+skin_presets.strider = `{
+ "name": "strider",
+ "texturewidth": 64,
+ "textureheight": 128,
+ "eyes": [
+ [17, 25, 2, 1],
+ [29, 25, 2, 1]
+ ],
+ "bones": [
+ {
+ "name": "body",
+ "pivot": [0, 17, 0],
+ "cubes": [
+ {"name": "cube", "origin": [-8, 17, -8], "size": [16, 14, 16], "uv": [0, 0]}
+ ]
+ },
+ {
+ "name": "right_bristles_1",
+ "parent": "body",
+ "pivot": [-8, 30, 0],
+ "rotation": [0, 0, -60],
+ "mirror": true,
+ "cubes": [
+ {"name": "cube", "origin": [-20, 30, -8], "size": [12, 0, 16], "uv": [4, 33]}
+ ]
+ },
+ {
+ "name": "left_bristles_1",
+ "parent": "body",
+ "pivot": [8, 30, 0],
+ "rotation": [0, 0, 60],
+ "cubes": [
+ {"name": "cube", "origin": [8, 30, -8], "size": [12, 0, 16], "uv": [4, 33]}
+ ]
+ },
+ {
+ "name": "right_bristles_2",
+ "parent": "body",
+ "pivot": [-8, 26, 0],
+ "rotation": [0, 0, -60],
+ "mirror": true,
+ "cubes": [
+ {"name": "cube", "origin": [-20, 26, -8], "size": [12, 0, 16], "uv": [4, 49]}
+ ]
+ },
+ {
+ "name": "left_bristles_2",
+ "parent": "body",
+ "pivot": [8, 26, 0],
+ "rotation": [0, 0, 60],
+ "cubes": [
+ {"name": "cube", "origin": [8, 26, -8], "size": [12, 0, 16], "uv": [4, 49]}
+ ]
+ },
+ {
+ "name": "right_bristles_3",
+ "parent": "body",
+ "pivot": [-8, 21, 0],
+ "rotation": [0, 0, -60],
+ "mirror": true,
+ "cubes": [
+ {"name": "cube", "origin": [-20, 21, -8], "size": [12, 0, 16], "uv": [4, 65]}
+ ]
+ },
+ {
+ "name": "left_bristles_3",
+ "parent": "body",
+ "pivot": [8, 21, 0],
+ "rotation": [0, 0, 60],
+ "cubes": [
+ {"name": "cube", "origin": [8, 21, -8], "size": [12, 0, 16], "uv": [4, 65]}
+ ]
+ },
+ {
+ "name": "right_leg",
+ "pivot": [-4, 17, 0],
+ "cubes": [
+ {"name": "cube", "origin": [-6, 0, -2], "size": [4, 17, 4], "uv": [0, 32]}
+ ]
+ },
+ {
+ "name": "left_leg",
+ "pivot": [4, 17, 0],
+ "mirror": true,
+ "cubes": [
+ {"name": "cube", "origin": [2, 0, -2], "size": [4, 17, 4], "uv": [0, 32]}
+ ]
+ }
+ ]
+}`;
skin_presets.tropicalfish_a = `{
"name": "tropicalfish_a",
"texturewidth": 32,
diff --git a/js/outliner/cube.js b/js/outliner/cube.js
index fb500d7fb..22fb32ae1 100644
--- a/js/outliner/cube.js
+++ b/js/outliner/cube.js
@@ -109,7 +109,6 @@ class Cube extends NonGroup {
}
this.visibility = true;
this.autouv = 0
- this.export = true;
this.parent = 'root';
this.faces = {
@@ -133,6 +132,7 @@ class Cube extends NonGroup {
Merge.number(this, object, 'autouv')
Merge.number(this, object, 'color')
Merge.boolean(this, object, 'export')
+ Merge.boolean(this, object, 'locked')
Merge.boolean(this, object, 'visibility')
if (object.from) {
Merge.number(this.from, object.from, 0)
@@ -292,6 +292,7 @@ class Cube extends NonGroup {
autouv: this.autouv,
color: this.color
}
+ el.locked = this.locked;
if (!this.visibility) el.visibility = false;
if (!this.export) el.export = false;
if (!this.shade) el.shade = false;
@@ -666,40 +667,6 @@ class Cube extends NonGroup {
Canvas.updateUV(scope)
}
}
- move(val, axis, move_origin) {
-
- var size = this.size(axis);
- val+= this.from[axis];
- var in_box = val;
- val = limitToBox(limitToBox(val, -this.inflate) + size, this.inflate) - size;
- in_box = Math.abs(in_box - val) < 1e-4;
- val -= this.from[axis];
-
- //Move
- if (Blockbench.globalMovement && Format.bone_rig && !move_origin) {
- var m = new THREE.Vector3();
- m[getAxisLetter(axis)] = val;
-
- var rotation = new THREE.Quaternion();
- this.mesh.getWorldQuaternion(rotation);
- m.applyQuaternion(rotation.inverse());
-
- this.from.V3_add(m.x, m.y, m.z);
- this.to.V3_add(m.x, m.y, m.z);
-
- } else {
- this.to[axis] += val;
- this.from[axis] += val;
- }
- //Origin
- if (Blockbench.globalMovement && move_origin) {
- this.origin[axis] += val;
- }
- this.mapAutoUV()
- Canvas.adaptObjectPosition(this);
- TickUpdates.selection = true;
- return in_box;
- }
moveVector(arr, axis) {
if (typeof arr == 'number') {
var n = arr;
@@ -734,8 +701,8 @@ class Cube extends NonGroup {
if (!negative) {
var pos = limitToBox(this.from[axis] + modify(before), this.inflate);
- if (Format.integer_size && Project.box_uv) {
- pos = Math.round(pos);
+ if (Format.integer_size) {
+ pos = Math.round(pos-this.from[axis])+this.from[axis];
}
if (pos >= this.from[axis] || settings.negative_size.value || allow_negative) {
this.to[axis] = pos;
@@ -744,6 +711,9 @@ class Cube extends NonGroup {
}
} else {
var pos = limitToBox(this.to[axis] + modify(-before), this.inflate);
+ if (Format.integer_size) {
+ pos = Math.round(pos-this.to[axis])+this.to[axis];
+ }
if (pos <= this.to[axis] || settings.negative_size.value || allow_negative) {
this.from[axis] = pos;
} else {
@@ -811,8 +781,8 @@ class Cube extends NonGroup {
'delete'
]);
Cube.prototype.buttons = [
- Outliner.buttons.remove,
Outliner.buttons.visibility,
+ Outliner.buttons.locked,
Outliner.buttons.export,
Outliner.buttons.shading,
Outliner.buttons.autouv
diff --git a/js/outliner/group.js b/js/outliner/group.js
index 32ca652d8..ae692ef37 100644
--- a/js/outliner/group.js
+++ b/js/outliner/group.js
@@ -12,6 +12,7 @@ class Group extends OutlinerElement {
this.reset = false;
this.shade = true;
this.selected = false;
+ this.locked = false;
this.visibility = true;
this.export = true;
this.autouv = 0;
@@ -44,6 +45,7 @@ class Group extends OutlinerElement {
}
Merge.number(this, object, 'autouv')
Merge.boolean(this, object, 'export')
+ Merge.boolean(this, object, 'locked')
Merge.boolean(this, object, 'visibility')
return this;
}
@@ -69,7 +71,7 @@ class Group extends OutlinerElement {
}
select(event) {
var scope = this;
- if (Blockbench.hasFlag('renaming')) return this;
+ if (Blockbench.hasFlag('renaming') || this.locked) return this;
if (!event) event = true
var allSelected = Group.selected === this && selected.length && this.matchesSelection()
@@ -223,6 +225,7 @@ class Group extends OutlinerElement {
}
showContextMenu(event) {
Prop.active_panel = 'outliner'
+ if (this.locked) return this;
this.select(event)
this.menu.open(event, this)
return this;
@@ -305,6 +308,7 @@ class Group extends OutlinerElement {
base_group.rotation.V3_set(this.rotation);
base_group.shade = this.shade;
base_group.reset = this.reset;
+ base_group.locked = this.locked;
base_group.visibility = this.visibility;
base_group.export = this.export;
base_group.autouv = this.autouv;
@@ -321,6 +325,7 @@ class Group extends OutlinerElement {
obj.uuid = this.uuid;
obj.export = this.export;
obj.isOpen = this.isOpen === true;
+ obj.locked = this.locked;
obj.visibility = this.visibility;
obj.autouv = this.autouv;
}
@@ -383,10 +388,10 @@ class Group extends OutlinerElement {
Group.prototype.type = 'group';
Group.prototype.icon = 'fa fa-folder';
Group.prototype.isParent = true;
- Group.prototype.name_regex = () => Format.bone_rig ? 'a-zA-Z0-9_' : false;
+ Group.prototype.name_regex = () => Format.bone_rig ? 'a-z0-9_' : false;
Group.prototype.buttons = [
- Outliner.buttons.remove,
Outliner.buttons.visibility,
+ Outliner.buttons.locked,
Outliner.buttons.export,
Outliner.buttons.shading,
Outliner.buttons.autouv
diff --git a/js/outliner/locator.js b/js/outliner/locator.js
index 3c1b95cba..7125d6895 100644
--- a/js/outliner/locator.js
+++ b/js/outliner/locator.js
@@ -4,7 +4,6 @@ class Locator extends NonGroup {
super(data, uuid);
this.from = new Array().V3_set(0, 0, 0);
this.name = 'locator';
- this.export = true;
if (data) {
this.extend(data);
@@ -13,6 +12,7 @@ class Locator extends NonGroup {
extend(object) {
Merge.string(this, object, 'name');
this.sanitizeName();
+ Merge.boolean(this, object, 'locked')
Merge.boolean(this, object, 'export');
Merge.arrayVector(this, object, 'from');
return this;
@@ -28,6 +28,7 @@ class Locator extends NonGroup {
var el = {
name: this.name,
export: this.export ? undefined : false,
+ locked: this.locked,
from: this.from,
uuid: this.uuid,
type: 'locator'
@@ -61,32 +62,15 @@ class Locator extends NonGroup {
return pos;
}
- move(val, axis) {
-
- if (Blockbench.globalMovement) {
- var m = new THREE.Vector3();
- m[getAxisLetter(axis)] = val;
- if (this.parent instanceof Group) {
- var rotation = new THREE.Quaternion();
- this.parent.mesh.getWorldQuaternion(rotation);
- m.applyQuaternion(rotation.inverse());
- }
- this.from.V3_add(m);
- } else {
- this.from[axis] += val
- }
- TickUpdates.selection = true;
- return this;
- }
}
Locator.prototype.title = tl('data.locator');
Locator.prototype.type = 'locator';
Locator.prototype.icon = 'fa fa-anchor';
- Locator.prototype.name_regex = 'a-zA-Z0-9_'
+ Locator.prototype.name_regex = 'a-z0-9_'
Locator.prototype.movable = true;
Locator.prototype.visibility = true;
Locator.prototype.buttons = [
- Outliner.buttons.remove,
+ Outliner.buttons.locked,
Outliner.buttons.export
];
Locator.prototype.needsUniqueName = true;
diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js
index 5cdf4262e..75dde0489 100644
--- a/js/outliner/outliner.js
+++ b/js/outliner/outliner.js
@@ -4,6 +4,7 @@ const Outliner = {
elements: elements,
selected: selected,
buttons: {
+ /*
remove: {
id: 'remove',
title: tl('generic.delete'),
@@ -23,6 +24,7 @@ const Outliner = {
Undo.finishEdit('remove', {elements: [], outliner: true, selection: true})
}
},
+ */
visibility: {
id: 'visibility',
title: tl('switches.visibility'),
@@ -30,9 +32,22 @@ const Outliner = {
icon_off: ' fa fa-eye-slash',
advanced_option: false,
click: function(obj) {
+ if (obj.locked) return;
obj.toggle('visibility')
}
},
+ locked: {
+ id: 'locked',
+ title: tl('switches.lock'),
+ icon: ' fas fa-lock',
+ icon_off: ' fas fa-lock-open',
+ advanced_option: true,
+ click: function(obj) {
+ if (obj.locked && Format.force_lock) return;
+ obj.toggle('locked')
+ updateSelection()
+ }
+ },
export: {
id: 'export',
title: tl('switches.export'),
@@ -40,6 +55,7 @@ const Outliner = {
icon_off: ' far fa-window-close',
advanced_option: true,
click: function(obj) {
+ if (obj.locked) return;
obj.toggle('export')
}
},
@@ -50,8 +66,12 @@ const Outliner = {
get icon_off() {return Project.box_uv ? 'fas fa-star-half-alt' : 'far fa-star'},
advanced_option: true,
click: function(obj) {
+ if (obj.locked) return;
obj.toggle('shade')
Canvas.updateUVs()
+ if (obj instanceof Cube && obj.visibility && !obj.selected) {
+ Canvas.updateUV(obj);
+ }
}
},
autouv: {
@@ -62,6 +82,7 @@ const Outliner = {
icon_alt: ' fa fa-magic',
advanced_option: true,
click: function(obj) {
+ if (obj.locked) return;
var state = obj.autouv+1
if (state > 2) state = 0
@@ -84,6 +105,8 @@ var markerColors = [
class OutlinerElement {
constructor(uuid) {
this.uuid = uuid || guid()
+ this.export = true;
+ this.locked = false;
}
init() {
this.constructor.all.safePush(this);
@@ -298,6 +321,9 @@ class OutlinerElement {
case 'export':
return this.export
break;
+ case 'locked':
+ return this.locked
+ break;
case 'shading':
return this.shade
break;
@@ -352,6 +378,7 @@ class NonGroup extends OutlinerElement {
}
showContextMenu(event) {
Prop.active_panel = 'outliner'
+ if (this.locked) return this;
if (!this.selected) {
this.select()
}
@@ -381,8 +408,8 @@ class NonGroup extends OutlinerElement {
if (val === undefined) {
var val = !this[key]
}
- this.forSelected((cube) => {
- cube[key] = val
+ this.forSelected((el) => {
+ el[key] = val
}, 'toggle '+key)
if (key === 'visibility') {
Canvas.updateVisibility()
@@ -640,13 +667,10 @@ function parseGroups(array, importGroup, startIndex) {
}
//Outliner
function loadOutlinerDraggable() {
- if (Blockbench.isMobile) {
- return;
- }
function getOrder(loc, obj) {
if (!obj) {
return;
- } else if (obj.type === 'group') {
+ } else if (obj instanceof Group) {
if (loc < 8) return -1;
if (loc > 24) return 1;
} else {
@@ -663,6 +687,12 @@ function loadOutlinerDraggable() {
appendTo: 'body',
zIndex: 19,
cursorAt: {left: 5},
+ start(event, ui) {
+ if (event.target && event.target.parentNode) {
+ var element = Outliner.root.findRecursive('uuid', event.target.parentNode.id)
+ if (!element || element.locked) return false;
+ }
+ },
helper: function() {
var item = Outliner.root.findRecursive('uuid', $(this).attr('id'))
var helper = $(this).clone()
@@ -682,7 +712,7 @@ function loadOutlinerDraggable() {
var tar = $('#cubes_list li .drag_hover.outliner_node').last()
var element = Outliner.root.findRecursive('uuid', tar.attr('id'))
if (element) {
- var location = event.clientY - tar.position().top
+ var location = event.clientY - tar.offset().top
var order = getOrder(location, element)
tar.attr('order', order)
}
@@ -701,7 +731,7 @@ function loadOutlinerDraggable() {
addClasses: false,
drop: function(event, ui) {
$('.outliner_node[order]').attr('order', null)
- var location = event.clientY - $(event.target).position().top
+ var location = event.clientY - $(event.target).offset().top
$('.drag_hover').removeClass('drag_hover')
var target = Outliner.root.findRecursive('uuid', $(event.target).attr('id'))
@@ -954,11 +984,11 @@ BARS.defineActions(function() {
width: 300,
singleButton: true,
form: {
- cubes: {type: 'text', label: tl('dialog.model_stats.cubes'), text: ''+Cube.all.length },
- locators: {type: 'text', label: tl('dialog.model_stats.locators'), text: ''+Locator.all.length, condition: Format.locators },
- groups: {type: 'text', label: tl('dialog.model_stats.groups'), text: ''+Group.all.length },
- vertices: {type: 'text', label: tl('dialog.model_stats.vertices'), text: ''+Cube.all.length*8 },
- faces: {type: 'text', label: tl('dialog.model_stats.faces'), text: ''+face_count },
+ cubes: {type: 'info', label: tl('dialog.model_stats.cubes'), text: ''+Cube.all.length },
+ locators: {type: 'info', label: tl('dialog.model_stats.locators'), text: ''+Locator.all.length, condition: Format.locators },
+ groups: {type: 'info', label: tl('dialog.model_stats.groups'), text: ''+Group.all.length },
+ vertices: {type: 'info', label: tl('dialog.model_stats.vertices'), text: ''+Cube.all.length*8 },
+ faces: {type: 'info', label: tl('dialog.model_stats.faces'), text: ''+face_count },
}
})
dialog.show()
@@ -989,6 +1019,21 @@ BARS.defineActions(function() {
Undo.finishEdit('sort_outliner')
}
})
+ new Action('unlock_everything', {
+ icon: 'fas.fa-key',
+ category: 'edit',
+ click: function () {
+ let locked = Outliner.elements.filter(el => el.locked);
+ let locked_groups = Group.all.filter(group => group.locked)
+ if (locked.length + locked_groups.length == 0) return;
+
+ Undo.initEdit({outliner: locked_groups.length > 0, elements: locked});
+ [...locked, ...locked_groups].forEach(el => {
+ el.locked = false;
+ })
+ Undo.finishEdit('unlock_everything')
+ }
+ })
new Action('element_colors', {
icon: 'check_box',
category: 'edit',
diff --git a/js/outliner/tree.vue.js b/js/outliner/tree.vue.js
index 08bd8f619..81e8c1740 100644
--- a/js/outliner/tree.vue.js
+++ b/js/outliner/tree.vue.js
@@ -6,8 +6,8 @@
`' +
//Other Entries
'' +
'',
props: {
@@ -51,6 +52,9 @@
if (typeof btn.click === 'function') {
btn.click(node);
}
+ },
+ getIndentation(node) {
+ return node.getDepth ? (limitNumber(node.getDepth(), 0, (Interface.Panels.outliner.width-124) / 16) * 16) : 0;
}
},
watch: {
diff --git a/js/plugin_loader.js b/js/plugin_loader.js
index ce011a58c..cbb256867 100644
--- a/js/plugin_loader.js
+++ b/js/plugin_loader.js
@@ -14,7 +14,7 @@ const Plugins = {
devReload() {
var reloads = 0;
for (var i = Plugins.all.length-1; i >= 0; i--) {
- if (Plugins.all[i].fromFile) {
+ if (Plugins.all[i].source == 'file') {
Plugins.all[i].reload()
reloads++;
}
@@ -28,6 +28,8 @@ const Plugins = {
});
}
}
+StateMemory.init('installed_plugins', 'array')
+Plugins.installed = StateMemory.installed_plugins;
class Plugin {
constructor(id, data) {
@@ -41,6 +43,7 @@ class Plugin {
this.icon = '';
this.variant = '';
this.min_version = '';
+ this.source = 'store'
this.extend(data)
@@ -79,7 +82,7 @@ class Plugin {
scope.uninstall()
}
})
- Plugins.installed.safePush(scope.id)
+ this.remember()
scope.installed = true;
return scope;
}
@@ -127,9 +130,9 @@ class Plugin {
localStorage.setItem('plugin_dev_path', file.path)
Plugins.all.safePush(this)
- scope.fromFile = true
+ scope.source = 'file'
if (isApp) {
- $.getScript(file.path, function() {
+ $.getScript(file.path, () => {
if (window.plugin_data) {
scope.id = (plugin_data && plugin_data.id)||pathToName(file.path)
scope.extend(plugin_data)
@@ -137,8 +140,7 @@ class Plugin {
}
scope.installed = true
scope.path = file.path
- Plugins.installed.safePush(scope.path)
- saveInstalledPlugins()
+ this.remember()
Plugins.sort()
})
} else {
@@ -154,14 +156,52 @@ class Plugin {
scope.bindGlobalData()
}
scope.installed = true
- Plugins.installed.safePush(scope.path)
- saveInstalledPlugins()
+ this.remember()
Plugins.sort()
}
return this;
}
+ loadFromURL(url, first) {
+ if (first) {
+ if (isApp) {
+ if (!confirm(tl('message.load_plugin_app'))) return;
+ } else {
+ if (!confirm(tl('message.load_plugin_web'))) return;
+ }
+ }
+
+ this.id = pathToName(url)
+ Plugins.registered[this.id] = this;
+ localStorage.setItem('plugin_dev_path', url)
+ Plugins.all.safePush(this)
+
+ this.source = 'url';
+ $.getScript(url, () => {
+ if (window.plugin_data) {
+ this.id = (plugin_data && plugin_data.id)||pathToName(url)
+ this.extend(plugin_data)
+ this.bindGlobalData()
+ }
+ this.installed = true
+ this.path = url
+ this.remember()
+ Plugins.sort()
+ })
+ return this;
+ }
+ remember(id = this.id, path = this.path) {
+ if (Plugins.installed.find(plugin => plugin.id == this.id)) {
+ return this;
+ }
+ Plugins.installed.push({
+ id: id,
+ path: path,
+ source: this.source
+ })
+ StateMemory.save('installed_plugins')
+ return this;
+ }
uninstall() {
- var scope = this;
try {
this.unload();
if (this.onuninstall) {
@@ -171,14 +211,16 @@ class Plugin {
console.log('Error in unload or uninstall method: ', err);
}
delete Plugins.registered[this.id];
- Plugins.installed.remove(this.fromFile ? this.path : this.id);
+ let in_installed = Plugins.installed.find(plugin => plugin.id == this.id);
+ Plugins.installed.remove(in_installed);
+ StateMemory.save('installed_plugins')
this.installed = false;
- if (isApp && this.fromFile) {
+ if (isApp && this.source !== 'store') {
Plugins.all.remove(this)
} else if (isApp) {
- var filepath = Plugins.path + scope.id + '.js'
+ var filepath = Plugins.path + this.id + '.js'
if (fs.existsSync(filepath)) {
fs.unlink(filepath, (err) => {
if (err) {
@@ -187,7 +229,7 @@ class Plugin {
});
}
}
- saveInstalledPlugins()
+ StateMemory.save('installed_plugins')
return this;
}
unload() {
@@ -197,13 +239,22 @@ class Plugin {
return this;
}
reload() {
- if (!isApp) return this;
+ if (!isApp && this.source == 'file') return this;
+
this.unload()
Plugins.all.remove(this)
- //---------------
- this.loadFromFile({path: this.path}, false)
+
+ if (this.source == 'file') {
+ this.loadFromFile({path: this.path}, false)
+
+ } else if (this.source == 'url') {
+ this.loadFromURL(this.path, false)
+ }
return this;
}
+ isReloadable() {
+ return (this.source == 'file' && isApp) || (this.source == 'url')
+ }
isInstallable() {
var scope = this;
var result =
@@ -281,26 +332,47 @@ function loadInstalledPlugins() {
Plugins.loadingStep = true
return;
}
- var storage_data = localStorage.getItem('installed_plugins')
- if (storage_data !== null) {
- Plugins.installed = JSON.parse(storage_data)
+ if (localStorage.getItem('installed_plugins')) {
+ var legacy_plugins = JSON.parse(localStorage.getItem('installed_plugins'))
+ if (legacy_plugins instanceof Array) {
+ legacy_plugins.forEach((string, i) => {
+ if (typeof string == 'string') {
+ if (string.match(/\.js$/)) {
+ Plugins.installed[i] = {
+ id: string.split(/[\\/]/).last().replace(/\.js$/, ''),
+ path: string,
+ source: 'file'
+ }
+ } else {
+ Plugins.installed[i] = {
+ id: string,
+ source: 'store'
+ }
+ }
+ }
+ })
+ }
+ StateMemory.save('installed_plugins')
+ localStorage.removeItem('installed_plugins')
}
- if (Plugins.json !== undefined) {
+
+ if (Plugins.json instanceof Object) {
//From Store
for (var id in Plugins.json) {
var plugin = new Plugin(id, Plugins.json[id])
- if (Plugins.installed.includes(id)) {
+ if (Plugins.installed.find(plugin => {
+ return plugin.id == id && plugin.source == 'store'
+ })) {
plugin.download()
}
}
Plugins.sort();
} else if (Plugins.installed.length > 0 && isApp) {
//Offline
- Plugins.installed.forEach(function(id) {
+ Plugins.installed.forEach(function(plugin) {
- if (id.substr(-3) !== '.js') {
- //downloaded public plugin
- var plugin = new Plugin(id).install(false, function() {
+ if (plugin.source == 'store') {
+ var plugin = new Plugin(plugin.id).install(false, function() {
this.extend(window.plugin_data)
Plugins.sort()
})
@@ -309,22 +381,28 @@ function loadInstalledPlugins() {
}
if (Plugins.installed.length > 0) {
var loaded = []
- Plugins.installed.forEach(function(id) {
+ Plugins.installed.forEachReverse(function(plugin) {
- if (id && id.substr(-3) === '.js') {
+ if (plugin.source == 'file') {
//Dev Plugins
- if (isApp && fs.existsSync(id)) {
- var plugin = new Plugin(id).loadFromFile({path: id}, false)
- loaded.push(pathToName(id))
+ if (isApp && fs.existsSync(plugin.path)) {
+ var plugin = new Plugin(plugin.id).loadFromFile({path: plugin.path}, false)
+ loaded.push('Local: '+ plugin.id || plugin.path)
} else {
- Plugins.installed.remove(id)
+ Plugins.installed.remove(plugin)
}
- } else if (id) {
- loaded.push(id)
+
+ } else if (plugin.source == 'url') {
+ var plugin = new Plugin(plugin.id).loadFromFile({path: plugin.path}, false)
+ loaded.push('URL: '+ plugin.id || plugin.path)
+
+ } else {
+ loaded.push('Store: '+ plugin.id)
}
})
console.log(`Loaded ${loaded.length} plugin${pluralS(loaded.length)}`, loaded)
}
+ StateMemory.save('installed_plugins')
Plugins.Vue = new Vue({
el: '#plugin_list',
@@ -353,12 +431,6 @@ function loadInstalledPlugins() {
}
})
}
-function saveInstalledPlugins() {
- localStorage.setItem('installed_plugins', JSON.stringify(Plugins.installed))
-}
-function loadPluginFromFile(file) {
- var plugin = new Plugin().loadFromFile(file, true)
-}
function switchPluginTabs(installed) {
$('#plugins .tab_bar > .open').removeClass('open')
if (installed) {
@@ -391,13 +463,21 @@ BARS.defineActions(function() {
icon: 'fa-file-code',
category: 'blockbench',
click: function () {
- var startpath = localStorage.getItem('plugin_dev_path') || undefined;
Blockbench.import({
+ resource_id: 'dev_plugin',
extensions: ['js'],
type: 'Blockbench Plugin',
- startpath
}, function(files) {
- loadPluginFromFile(files[0])
+ new Plugin().loadFromFile(files[0], true)
+ })
+ }
+ })
+ new Action('load_plugin_from_url', {
+ icon: 'cloud_download',
+ category: 'blockbench',
+ click: function () {
+ Blockbench.textPrompt('URL', '', url => {
+ new Plugin().loadFromURL(url, true)
})
}
})
diff --git a/js/preview/canvas.js b/js/preview/canvas.js
index 98d0996c0..e6a0d70fd 100644
--- a/js/preview/canvas.js
+++ b/js/preview/canvas.js
@@ -489,10 +489,64 @@ const Canvas = {
}
iterate(el, elmesh)
},
+ getLayeredMaterial() {
+ var vertShader = `
+ varying vec2 vUv;
+
+ void main()
+ {
+ vUv = uv;
+ vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+ gl_Position = projectionMatrix * mvPosition;
+ }`
+ var fragShader = `
+ #ifdef GL_ES
+ precision highp float;
+ #endif
+
+ uniform sampler2D tOne;
+ uniform sampler2D tSec;
+
+ varying vec2 vUv;
+
+ void main(void)
+ {
+ vec3 c;
+ vec4 Ca = texture2D(tOne, vUv);
+ vec4 Cb = texture2D(tSec, vUv);
+ c = Ca.rgb * Ca.a + Cb.rgb * Cb.a * (1.0 - Ca.a); // blending equation
+ gl_FragColor= vec4(c, 1.0);
+ }`
+
+ var uniforms = {
+ tOne: { type: "t", value: textures[1].getMaterial().map },
+ tSec: { type: "t", value: textures[0].getMaterial().map }
+ };
+
+ var material_shh = new THREE.ShaderMaterial({
+ uniforms: uniforms,
+ vertexShader: vertShader,
+ fragmentShader: fragShader
+ });
+ return material_shh;
+ /*
+ todo:
+ Issues:
+ Painting is one pixel delayed
+ Painting doesn't occur on selected texture
+ needs setting
+ needs to work with 0-3+ textures
+ */
+ },
adaptObjectFaces(cube, mesh) {
if (!mesh) mesh = cube.mesh
if (!mesh) return;
- if (!Prop.wireframe) {
+ if (Prop.wireframe) {
+ mesh.material = Canvas.wireframeMaterial
+ /*} else if (settings.layered_textures.value && Format && Format.id.includes('bedrock')) {
+ mesh.material = Canvas.getLayeredMaterial();*/
+
+ } else {
var materials = []
Canvas.face_order.forEach(function(face) {
@@ -509,8 +563,6 @@ const Canvas = {
}
})
mesh.material = materials
- } else {
- mesh.material = Canvas.wireframeMaterial
}
},
updateUV(obj, animation) {
diff --git a/js/preview/preview.js b/js/preview/preview.js
index ecc6f51a6..3313fc70d 100644
--- a/js/preview/preview.js
+++ b/js/preview/preview.js
@@ -86,6 +86,22 @@ const DefaultCameraPresets = [
zoom: 0.5,
locked_angle: 5,
default: true
+ },
+ {
+ name: 'camera_angle.isometric_right',
+ projection: 'orthographic',
+ position: [-64, 64*0.8165, -64],
+ target: [0, 0, 0],
+ zoom: 0.5,
+ default: true
+ },
+ {
+ name: 'camera_angle.isometric_left',
+ projection: 'orthographic',
+ position: [64, 64*0.8165, -64],
+ target: [0, 0, 0],
+ zoom: 0.5,
+ default: true
}
]
@@ -155,12 +171,30 @@ class Preview {
this.controls.mouseButtons.ZOOM = undefined;
//Renderer
- this.renderer = new THREE.WebGLRenderer({
- canvas: this.canvas,
- antialias: true,
- alpha: true,
- preserveDrawingBuffer: true
- });
+ try {
+ this.renderer = new THREE.WebGLRenderer({
+ canvas: this.canvas,
+ antialias: true,
+ alpha: true,
+ preserveDrawingBuffer: true
+ });
+ } catch (err) {
+ document.querySelector('#loading_error_detail').innerHTML = 'Error creating WebGL context. Try to update your graphics drivers.';
+ if (isApp) {
+ var {BrowserWindow} = require('electron').remote
+ new BrowserWindow({
+ icon:'icon.ico',
+ backgroundColor: '#ffffff',
+ title: 'Blockbench GPU Information',
+ webPreferences: {
+ webgl: true,
+ webSecurity: true,
+ nodeIntegration: true
+ }
+ }).loadURL('chrome://gpu')
+ }
+ throw err;
+ }
this.renderer.setClearColor( 0x000000, 0 )
this.renderer.setSize(500, 400);
@@ -243,7 +277,18 @@ class Preview {
}
var intersects = this.raycaster.intersectObjects( objects );
if (intersects.length > 0) {
- var intersect = intersects[0].object
+ if (intersects.length > 1 && Toolbox.selected.id == 'vertex_snap_tool') {
+ var intersect;
+ for (var sct of intersects) {
+ if (sct.object.isVertex) {
+ intersect = sct.object;
+ break;
+ }
+ }
+ if (!intersect) intersect = intersects[0].object;
+ } else {
+ var intersect = intersects[0].object
+ }
if (intersect.isElement) {
this.controls.hasMoved = true
var obj = elements.findInArray('uuid', intersects[0].object.name)
@@ -400,6 +445,14 @@ class Preview {
}
newAnglePreset() {
let scope = this;
+ let position = scope.camera.position.toArray();
+ let target = scope.controls.target.toArray();
+ position.forEach((v, i) => {
+ position[i] = Math.round(v*100)/100
+ })
+ target.forEach((v, i) => {
+ target[i] = Math.round(v*100)/100
+ })
let dialog = new Dialog({
id: 'save_angle',
@@ -411,7 +464,9 @@ class Preview {
unset: 'generic.unset',
perspective: 'dialog.save_angle.projection.perspective',
orthographic: 'dialog.save_angle.projection.orthographic'
- }}
+ }},
+ position: {label: 'dialog.save_angle.position', type: 'vector', dimensions: 3, value: position},
+ target: {label: 'dialog.save_angle.target', type: 'vector', dimensions: 3, value: target},
},
onConfirm: function(formResult) {
@@ -420,8 +475,8 @@ class Preview {
let preset = {
name: formResult.name,
projection: formResult.projection,
- position: scope.camera.position.toArray(),
- target: scope.controls.target.toArray(),
+ position: formResult.position,
+ target: formResult.target,
}
if (this.isOrtho) preset.zoom = this.camOrtho.zoom;
@@ -488,7 +543,15 @@ class Preview {
var data = this.raycast(event);
if (data) {
//this.static_rclick = false
- if (Toolbox.selected.selectCubes && Modes.selected.selectCubes && data.type === 'cube') {
+ if (data.cube && data.cube.locked) {
+ $('#preview').css('cursor', 'not-allowed')
+ function resetCursor() {
+ $('#preview').css('cursor', (Toolbox.selected.cursor ? Toolbox.selected.cursor : 'default'))
+ removeEventListeners(document, 'mouseup touchend', resetCursor, false)
+ }
+ addEventListeners(document, 'mouseup touchend', resetCursor, false)
+
+ } else if (Toolbox.selected.selectCubes && Modes.selected.selectCubes && data.type === 'cube') {
if (Toolbox.selected.selectFace) {
main_uv.setFace(data.face, false)
}
@@ -524,8 +587,10 @@ class Preview {
}
}
mousemove(event) {
- var data = this.raycast(event);
- if (Settings.get('highlight_cubes')) updateCubeHighlights(data && data.cube);
+ if (Settings.get('highlight_cubes')) {
+ var data = this.raycast(event);
+ updateCubeHighlights(data && data.cube);
+ }
}
raycastMouseCoords(x,y) {
var scope = this;
@@ -888,6 +953,7 @@ class Preview {
return [
{icon: 'folder', name: 'menu.preview.background.load', click: function(preview) {
Blockbench.import({
+ resource_id: 'preview_background',
extensions: ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'tif', 'gif'],
type: 'Image',
readtype: 'image'
@@ -1044,13 +1110,23 @@ const Screencam = {
cancel: 0
}, function(result) {
if (result === 1) {
- Blockbench.export()
- ElecDialogs.showSaveDialog(currentwindow, {filters: [ {name: tl('data.image'), extensions: [is_gif ? 'gif' : 'png']} ]}, function (fileName) {
- if (fileName === undefined) {
- return;
- }
- fs.writeFile(fileName, Buffer(dataUrl.split(',')[1], 'base64'), err => {})
- })
+ if (is_gif) {
+ Blockbench.export({
+ resource_id: 'screenshot',
+ extensions: ['gif'],
+ type: tl('data.image'),
+ savetype: 'binary',
+ content: Buffer(dataUrl.split(',')[1], 'base64')
+ })
+ } else {
+ Blockbench.export({
+ resource_id: 'screenshot',
+ extensions: ['png'],
+ type: tl('data.image'),
+ savetype: 'image',
+ content: dataUrl
+ })
+ }
} else if (result === 2) {
clipboard.writeImage(screenshot)
}
@@ -1318,7 +1394,7 @@ function initCanvas() {
Sun = new THREE.AmbientLight( 0xffffff );
Sun.name = 'sun'
scene.add(Sun);
- Sun.intensity = 0.44
+ Sun.intensity = 0.5
lights = new THREE.Object3D()
lights.name = 'lights'
@@ -1328,7 +1404,14 @@ function initCanvas() {
light_top.position.set(8, 100, 8)
lights.add(light_top);
- light_top.intensity = 0.66
+ light_top.intensity = 0.45
+
+ var light_bottom = new THREE.DirectionalLight();
+ light_bottom.name = 'light_bottom'
+ light_bottom.position.set(8, 100, 8)
+ lights.add(light_bottom);
+
+ light_bottom.intensity = 0.11
var light_north = new THREE.DirectionalLight();
light_north.name = 'light_north'
@@ -1340,7 +1423,7 @@ function initCanvas() {
light_south.position.set(8, 8, 100)
lights.add(light_south);
- light_north.intensity = light_south.intensity = 0.44
+ light_north.intensity = light_south.intensity = 0.33
var light_west = new THREE.DirectionalLight();
light_west.name = 'light_west'
@@ -1491,7 +1574,6 @@ function buildGrid() {
three_grid.add(line)
}
//Axis Lines
- if (settings.base_grid.value || settings.full_grid.value)
if (Format.centered_grid || !settings.full_grid.value) {
var length = Format.centered_grid
? (settings.full_grid.value ? 24 : 8)
@@ -1508,7 +1590,8 @@ function buildGrid() {
if (settings.full_grid.value === true) {
//Grid
- var grid = new THREE.GridHelper(48, 48/canvasGridSize(), gizmo_colors.grid)
+ let size = settings.large_grid_size.value*16;
+ var grid = new THREE.GridHelper(size, size/canvasGridSize(), gizmo_colors.grid)
if (Format.centered_grid) {
grid.position.set(0,0,0)
} else {
@@ -1522,9 +1605,9 @@ function buildGrid() {
geometry = new THREE.PlaneGeometry(5, 5)
var north_mark = new THREE.Mesh(geometry, Canvas.northMarkMaterial)
if (Format.centered_grid) {
- north_mark.position.set(0,0,-27)
+ north_mark.position.set(0,0, -3 - size/2)
} else {
- north_mark.position.set(8,0,-19)
+ north_mark.position.set(8, 0, 5 - size/2)
}
north_mark.rotation.x = Math.PI / -2
three_grid.add(north_mark)
@@ -1532,7 +1615,8 @@ function buildGrid() {
} else {
if (settings.large_grid.value === true) {
//Grid
- var grid = new THREE.GridHelper(48, 3, gizmo_colors.grid)
+ let size = settings.large_grid_size.value
+ var grid = new THREE.GridHelper(size*16, size, gizmo_colors.grid)
if (Format.centered_grid) {
grid.position.set(0,0,0)
} else {
diff --git a/js/preview/transformer.js b/js/preview/transformer.js
index ba943cc98..f41659429 100644
--- a/js/preview/transformer.js
+++ b/js/preview/transformer.js
@@ -917,7 +917,7 @@
Transformer.rotation_ref = rotation_object.mesh.parent;
} else if (space === 2 || Toolbox.selected.id == 'resize_tool') {
- Transformer.rotation_ref = selected[0].mesh;
+ Transformer.rotation_ref = selected[0] && selected[0].mesh;
} else if (space instanceof Group) {
Transformer.rotation_ref = space.mesh;
@@ -1273,19 +1273,6 @@
moveElementsInSpace(difference, axisNumber)
-
- /*
- if (_has_groups && Blockbench.globalMovement) {
- Group.selected.forEachChild(g => {
- g.origin[axisNumber] += difference
- }, Group, true)
- }
- selected.forEach(function(obj, i) {
- if (obj.movable) {
- obj.move(difference, axisNumber, _has_groups||!Format.bone_rig)
- }
- })
- */
scope.updateSelection()
}
previousValue = point[axis]
diff --git a/js/texturing/color.js b/js/texturing/color.js
index 3314eea89..a868b4d85 100644
--- a/js/texturing/color.js
+++ b/js/texturing/color.js
@@ -5,310 +5,46 @@ function colorDistance(color1, color2) {
Math.pow(color2._b - color1._b, 2)
);
}
-
-onVueSetup(() => {
+(function() {
var palettes = {
default: [
- '#1a1a1b',
- '#353637',
- '#464849',
- '#5d5f60',
- '#757677',
- '#868788',
- '#979b9d',
- '#b8bdbe',
- '#dadedf',
- '#ffffff',
- '#9a080f',
- '#b40a1a',
- '#d21129',
- '#ef2142',
- '#ff5774',
- '#bb7907',
- '#cc9104',
- '#edb508',
- '#fcd720',
- '#fef364',
- '#0d7e36',
- '#12933d',
- '#11aa38',
- '#1cc93d',
- '#29e64d',
- '#044b8f',
- '#0955a8',
- '#126bc3',
- '#1782db',
- '#339afc',
- '#cd3e00',
- '#e65b00',
- '#f37800',
- '#f89520',
- '#fdaf40',
- '#02a8c1',
- '#0cc3ca',
- '#17d1c7',
- '#38debd',
- '#5be9b7',
+ '#1a1a1b','#353637','#464849','#5d5f60','#757677','#868788','#979b9d','#b8bdbe','#dadedf','#ffffff',
+ '#9a080f','#b40a1a','#d21129','#ef2142','#ff5774','#bb7907','#cc9104','#edb508','#fcd720','#fef364',
+ '#0d7e36','#12933d','#11aa38','#1cc93d','#29e64d','#044b8f','#0955a8','#126bc3','#1782db','#339afc',
+ '#cd3e00','#e65b00','#f37800','#f89520','#fdaf40','#02a8c1','#0cc3ca','#17d1c7','#38debd','#5be9b7',
],
material: [
- '#FFEBEE',
- '#FFCDD2',
- '#EF9A9A',
- '#E57373',
- '#EF5350',
- '#F44336',
- '#E53935',
- '#D32F2F',
- '#C62828',
- '#B71C1C',
- '#FCE4EC',
- '#F8BBD0',
- '#F48FB1',
- '#F06292',
- '#EC407A',
- '#E91E63',
- '#D81B60',
- '#C2185B',
- '#AD1457',
- '#880E4F',
- '#F3E5F5',
- '#E1BEE7',
- '#CE93D8',
- '#BA68C8',
- '#AB47BC',
- '#9C27B0',
- '#8E24AA',
- '#7B1FA2',
- '#6A1B9A',
- '#4A148C',
- '#EDE7F6',
- '#D1C4E9',
- '#B39DDB',
- '#9575CD',
- '#7E57C2',
- '#673AB7',
- '#5E35B1',
- '#512DA8',
- '#4527A0',
- '#311B92',
- '#E8EAF6',
- '#C5CAE9',
- '#9FA8DA',
- '#7986CB',
- '#5C6BC0',
- '#3F51B5',
- '#3949AB',
- '#303F9F',
- '#283593',
- '#1A237E',
- '#E3F2FD',
- '#BBDEFB',
- '#90CAF9',
- '#64B5F6',
- '#42A5F5',
- '#2196F3',
- '#1E88E5',
- '#1976D2',
- '#1565C0',
- '#0D47A1',
- '#E1F5FE',
- '#B3E5FC',
- '#81D4FA',
- '#4FC3F7',
- '#29B6F6',
- '#03A9F4',
- '#039BE5',
- '#0288D1',
- '#0277BD',
- '#01579B',
- '#E0F7FA',
- '#B2EBF2',
- '#80DEEA',
- '#4DD0E1',
- '#26C6DA',
- '#00BCD4',
- '#00ACC1',
- '#0097A7',
- '#00838F',
- '#006064',
- '#E0F2F1',
- '#B2DFDB',
- '#80CBC4',
- '#4DB6AC',
- '#26A69A',
- '#009688',
- '#00897B',
- '#00796B',
- '#00695C',
- '#004D40',
- '#E8F5E9',
- '#C8E6C9',
- '#A5D6A7',
- '#81C784',
- '#66BB6A',
- '#4CAF50',
- '#43A047',
- '#388E3C',
- '#2E7D32',
- '#1B5E20',
- '#F1F8E9',
- '#DCEDC8',
- '#C5E1A5',
- '#AED581',
- '#9CCC65',
- '#8BC34A',
- '#7CB342',
- '#689F38',
- '#558B2F',
- '#33691E',
- '#F9FBE7',
- '#F0F4C3',
- '#E6EE9C',
- '#DCE775',
- '#D4E157',
- '#CDDC39',
- '#C0CA33',
- '#AFB42B',
- '#9E9D24',
- '#827717',
- '#FFFDE7',
- '#FFF9C4',
- '#FFF59D',
- '#FFF176',
- '#FFEE58',
- '#FFEB3B',
- '#FDD835',
- '#FBC02D',
- '#F9A825',
- '#F57F17',
- '#FFF8E1',
- '#FFECB3',
- '#FFE082',
- '#FFD54F',
- '#FFCA28',
- '#FFC107',
- '#FFB300',
- '#FFA000',
- '#FF8F00',
- '#FF6F00',
- '#FFF3E0',
- '#FFE0B2',
- '#FFCC80',
- '#FFB74D',
- '#FFA726',
- '#FF9800',
- '#FB8C00',
- '#F57C00',
- '#EF6C00',
- '#E65100',
- '#FBE9E7',
- '#FFCCBC',
- '#FFAB91',
- '#FF8A65',
- '#FF7043',
- '#FF5722',
- '#F4511E',
- '#E64A19',
- '#D84315',
- '#BF360C',
- '#EFEBE9',
- '#D7CCC8',
- '#BCAAA4',
- '#A1887F',
- '#8D6E63',
- '#795548',
- '#6D4C41',
- '#5D4037',
- '#4E342E',
- '#3E2723',
- '#FAFAFA',
- '#F5F5F5',
- '#EEEEEE',
- '#E0E0E0',
- '#BDBDBD',
- '#9E9E9E',
- '#757575',
- '#616161',
- '#424242',
- '#212121',
- '#ECEFF1',
- '#CFD8DC',
- '#B0BEC5',
- '#90A4AE',
- '#78909C',
- '#607D8B',
- '#546E7A',
- '#455A64',
- '#37474F',
- '#263238',
+ '#ffebee','#ffcdd2','#ef9a9a','#e57373','#ef5350','#f44336','#e53935','#d32f2f','#c62828','#b71c1c','#ff5252','#ff1744',
+ '#fce4ec','#f8bbd0','#f48fb1','#f06292','#ec407a','#e91e63','#d81b60','#c2185b','#ad1457','#880e4f','#ff4081','#f50057',
+ '#f3e5f5','#e1bee7','#ce93d8','#ba68c8','#ab47bc','#9c27b0','#8e24aa','#7b1fa2','#6a1b9a','#4a148c','#e040fb','#d500f9',
+ '#ede7f6','#d1c4e9','#b39ddb','#9575cd','#7e57c2','#673ab7','#5e35b1','#512da8','#4527a0','#311b92','#7c4dff','#651fff',
+ '#e8eaf6','#c5cae9','#9fa8da','#7986cb','#5c6bc0','#3f51b5','#3949ab','#303f9f','#283593','#1a237e','#536dfe','#3d5afe',
+ '#e3f2fd','#bbdefb','#90caf9','#64b5f6','#42a5f5','#2196f3','#1e88e5','#1976d2','#1565c0','#0d47a1','#448aff','#2979ff',
+ '#e1f5fe','#b3e5fc','#81d4fa','#4fc3f7','#29b6f6','#03a9f4','#039be5','#0288d1','#0277bd','#01579b','#40c4ff','#00b0ff',
+ '#e0f7fa','#b2ebf2','#80deea','#4dd0e1','#26c6da','#00bcd4','#00acc1','#0097a7','#00838f','#006064','#18ffff','#00e5ff',
+ '#e0f2f1','#b2dfdb','#80cbc4','#4db6ac','#26a69a','#009688','#00897b','#00796b','#00695c','#004d40','#64ffda','#1de9b6',
+ '#e8f5e9','#c8e6c9','#a5d6a7','#81c784','#66bb6a','#4caf50','#43a047','#388e3c','#2e7d32','#1b5e20','#69f0ae','#00e676',
+ '#f1f8e9','#dcedc8','#c5e1a5','#aed581','#9ccc65','#8bc34a','#7cb342','#689f38','#558b2f','#33691e','#b2ff59','#76ff03',
+ '#f9fbe7','#f0f4c3','#e6ee9c','#dce775','#d4e157','#cddc39','#c0ca33','#afb42b','#9e9d24','#827717','#eeff41','#c6ff00',
+ '#fffde7','#fff9c4','#fff59d','#fff176','#ffee58','#ffeb3b','#fdd835','#fbc02d','#f9a825','#f57f17','#ffff00','#ffea00',
+ '#fff8e1','#ffecb3','#ffe082','#ffd54f','#ffca28','#ffc107','#ffb300','#ffa000','#ff8f00','#ff6f00','#ffd740','#ffc400',
+ '#fff3e0','#ffe0b2','#ffcc80','#ffb74d','#ffa726','#ff9800','#fb8c00','#f57c00','#ef6c00','#e65100','#ffab40','#ff9100',
+ '#fbe9e7','#ffccbc','#ffab91','#ff8a65','#ff7043','#ff5722','#f4511e','#e64a19','#d84315','#bf360c','#ff6e40','#ff3d00',
+ '#efebe9','#d7ccc8','#bcaaa4','#a1887f','#8d6e63','#795548','#6d4c41','#5d4037','#4e342e','#3e2723','#6d422d','#593022',
+ '#fafafa','#f5f5f5','#eeeeee','#e0e0e0','#bdbdbd','#9e9e9e','#757575','#616161','#424242','#212121','#ffffff','#000000',
+ '#eceff1','#cfd8dc','#b0bec5','#90a4ae','#78909c','#607d8b','#546e7a','#455a64','#37474f','#263238',
],
endesga64: [
- '#ff0040',
- '#131313',
- '#1b1b1b',
- '#272727',
- '#3d3d3d',
- '#5d5d5d',
- '#858585',
- '#b4b4b4',
- '#ffffff',
- '#c7cfdd',
- '#92a1b9',
- '#657392',
- '#424c6e',
- '#2a2f4e',
- '#1a1932',
- '#0e071b',
- '#1c121c',
- '#391f21',
- '#5d2c28',
- '#8a4836',
- '#bf6f4a',
- '#e69c69',
- '#f6ca9f',
- '#f9e6cf',
- '#edab50',
- '#e07438',
- '#c64524',
- '#8e251d',
- '#ff5000',
- '#ed7614',
- '#ffa214',
- '#ffc825',
- '#ffeb57',
- '#d3fc7e',
- '#99e65f',
- '#5ac54f',
- '#33984b',
- '#1e6f50',
- '#134c4c',
- '#0c2e44',
- '#00396d',
- '#0069aa',
- '#0098dc',
- '#00cdf9',
- '#0cf1ff',
- '#94fdff',
- '#fdd2ed',
- '#f389f5',
- '#db3ffd',
- '#7a09fa',
- '#3003d9',
- '#0c0293',
- '#03193f',
- '#3b1443',
- '#622461',
- '#93388f',
- '#ca52c9',
- '#c85086',
- '#f68187',
- '#f5555d',
- '#ea323c',
- '#c42430',
- '#891e2b',
- '#571c27',
+ '#ff0040','#131313','#1b1b1b','#272727','#3d3d3d','#5d5d5d','#858585','#b4b4b4','#ffffff','#c7cfdd',
+ '#92a1b9','#657392','#424c6e','#2a2f4e','#1a1932','#0e071b','#1c121c','#391f21','#5d2c28','#8a4836',
+ '#bf6f4a','#e69c69','#f6ca9f','#f9e6cf','#edab50','#e07438','#c64524','#8e251d','#ff5000','#ed7614',
+ '#ffa214','#ffc825','#ffeb57','#d3fc7e','#99e65f','#5ac54f','#33984b','#1e6f50','#134c4c','#0c2e44',
+ '#00396d','#0069aa','#0098dc','#00cdf9','#0cf1ff','#94fdff','#fdd2ed','#f389f5','#db3ffd','#7a09fa',
+ '#3003d9','#0c0293','#03193f','#3b1443','#622461','#93388f','#ca52c9','#c85086','#f68187','#f5555d',
+ '#ea323c','#c42430','#891e2b','#571c27',
]
}
+onVueSetup(() => {
ColorPanel = Interface.Panels.color = new Panel({
id: 'color',
condition: () => Modes.id === 'paint',
@@ -321,18 +57,7 @@ onVueSetup(() => {
},
menu: new Menu([
'sort_palette',
- 'clear_palette',
- {name: 'menu.palette.load', id: 'load', icon: 'fa-tasks', children: [
- {name: 'menu.palette.load.default', icon: 'bubble_chart', id: 'default', click: () => {
- ColorPanel.palette.splice(0, Infinity, ...palettes.default);
- }},
- {name: 'Endesga 64', description: 'Pixel art palette created by lospec.com/endesga', icon: 'bubble_chart', id: 'endesga64', click: () => {
- ColorPanel.palette.splice(0, Infinity, ...palettes.endesga64);
- }},
- {name: 'Material', icon: 'bubble_chart', id: 'material', click: () => {
- ColorPanel.palette.splice(0, Infinity, ...palettes.material);
- }},
- ]}
+ 'load_palette'
])
})
var saved_colors = localStorage.getItem('colors');
@@ -398,6 +123,20 @@ onVueSetup(() => {
if (!color.match(/^#[0-9a-f]{6}$/)) {
this.main_color = tinycolor(color).toHexString();
}
+ },
+ isDarkColor(hex) {
+ if (hex) {
+ hex = hex.replace('#', '')
+ let dgts = hex.substr(0, 1) + hex.substr(2, 1) + hex.substr(4, 1)
+ let bg = parseInt(CustomTheme.data.colors.back.substr(1, 1), 16);
+ let color_range = (bg).toString(16)
+ + (Math.isBetween(bg+2, 0, 15) ? (bg+2).toString(16) : '')
+ + (Math.isBetween(bg+1, 0, 15) ? (bg+1).toString(16) : '')
+ + (Math.isBetween(bg-1, 0, 15) ? (bg-1).toString(16) : '')
+ + (Math.isBetween(bg-2, 0, 15) ? (bg-2).toString(16) : '')
+ let regex = new RegExp(`[^${color_range}]`)
+ return dgts.search(regex) == -1
+ }
}
},
watch: {
@@ -854,6 +593,7 @@ BARS.defineActions(function() {
category: 'color',
click: function () {
Blockbench.import({
+ resource_id: 'palette',
extensions: ['gpl', 'css', 'txt', 'hex', 'png', 'aco', 'act', 'ase', 'bbpalette'],
type: 'Blockbench Palette',
readtype: (path) => {
@@ -881,6 +621,7 @@ BARS.defineActions(function() {
content += `${t._r}\t${t._g}\t${t._b}\t${color}\n`;
})
Blockbench.export({
+ resource_id: 'palette',
extensions: ['gpl'],
type: 'GPL Palette',
content
@@ -936,12 +677,28 @@ BARS.defineActions(function() {
}
}
})
- new Action('clear_palette', {
- icon: 'clear',
+
+ new Action('load_palette', {
+ icon: 'fa-tasks',
category: 'color',
- click: function () {
- ColorPanel.palette.purge();
- }
+ click: function (e) {
+ new Menu(this.children).open(e.target)
+ },
+ children: [
+ {name: 'menu.palette.load.default', icon: 'bubble_chart', id: 'default', click: () => {
+ ColorPanel.palette.splice(0, Infinity, ...palettes.default);
+ }},
+ {name: 'Endesga 64', description: 'Pixel art palette created by lospec.com/endesga', icon: 'bubble_chart', id: 'endesga64', click: () => {
+ ColorPanel.palette.splice(0, Infinity, ...palettes.endesga64);
+ }},
+ {name: 'Material', icon: 'bubble_chart', id: 'material', click: () => {
+ ColorPanel.palette.splice(0, Infinity, ...palettes.material);
+ }},
+ '_',
+ {name: 'menu.palette.load.empty', icon: 'clear', id: 'empty', click: () => {
+ ColorPanel.palette.splice(0, Infinity);
+ }},
+ ]
})
@@ -1005,4 +762,5 @@ BARS.defineActions(function() {
ColorPanel.updateFromHsv();
}
})
-})
\ No newline at end of file
+})
+})()
diff --git a/js/texturing/painter.js b/js/texturing/painter.js
index 087e03620..0fb00a498 100644
--- a/js/texturing/painter.js
+++ b/js/texturing/painter.js
@@ -112,7 +112,7 @@ const Painter = {
}
}
}
- if (!data) return;
+ if (!data || (data.cube && data.cube.locked)) return;
var texture = data.cube.faces[data.face].getTexture()
if (!texture || (texture.error && texture.error !== 2)) {
Blockbench.showQuickMessage('message.untextured')
@@ -131,7 +131,7 @@ const Painter = {
movePaintToolCanvas(event) {
convertTouchEvent(event);
var data = Canvas.raycast(event)
- if (data) {
+ if (data && data.cube && !data.cube.locked) {
var texture = data.cube.faces[data.face].getTexture()
if (texture) {
var x, y, new_face;
@@ -204,7 +204,7 @@ const Painter = {
Painter.drawBrushLine(texture, x, y, event, false, uvTag);
} else {
Painter.current.x = Painter.current.y = 0
- Painter.useBrush(texture, x, y, event, uvTag)
+ Painter.useBrushlike(texture, x, y, event, uvTag)
}
Painter.current.x = x;
Painter.current.y = y;
@@ -233,7 +233,7 @@ const Painter = {
Painter.currentPixel = [-1, -1];
},
// Tools
- useBrush(texture, x, y, event, uvTag, no_update, is_opposite) {
+ useBrushlike(texture, x, y, event, uvTag, no_update, is_opposite) {
if (Painter.currentPixel[0] === x && Painter.currentPixel[1] === y) return;
Painter.currentPixel = [x, y]
Painter.brushChanges = true;
@@ -248,12 +248,6 @@ const Painter = {
var ctx = canvas.getContext('2d')
ctx.save()
- var color = tinycolor(ColorPanel.get()).toRgb();
- var size = BarItems.slider_brush_size.get();
- let softness = BarItems.slider_brush_softness.get()/100;
- let b_opacity = BarItems.slider_brush_opacity.get()/100;
- let tool = Toolbox.selected.id;
-
ctx.beginPath();
if (uvTag) {
var rect = Painter.editing_area = [
@@ -275,128 +269,158 @@ const Painter = {
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]]
ctx.rect(rect[0], rect[1], w, h)
- if (tool === 'fill_tool') {
-
- ctx.fillStyle = tinycolor(ColorPanel.get()).setAlpha(b_opacity).toRgbString();
-
- var fill_mode = BarItems.fill_mode.get()
- var cube = Painter.current.cube;
- if (cube && fill_mode === 'cube') {
- for (var face in cube.faces) {
- var tag = cube.faces[face]
- if (tag.texture === Painter.current.texture.uuid) {
- var rect = getRectangle(
- Math.floor(tag.uv[0] * uvFactorX),
- Math.floor(tag.uv[1] * uvFactorY),
- Math.ceil(tag.uv[2] * uvFactorX),
- Math.ceil(tag.uv[3] * uvFactorY)
- )
- ctx.rect(rect.ax, rect.ay, rect.x, rect.y)
- ctx.fill()
- }
- }
+ if (Toolbox.selected.id === 'fill_tool') {
+ Painter.useFilltool(texture, ctx, x, y, { rect, uvFactorX, uvFactorY, w, h })
+ } else {
+ Painter.useBrush(texture, ctx, x, y, event)
+ }
+ Painter.editing_area = undefined;
- } else if (fill_mode === 'face') {
- ctx.fill()
- } else {
- var pxcol = [];
- var map = {}
- Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => {
- px.forEach((val, i) => {
- pxcol[i] = val
- })
- })
- Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
- if (pxcol.equals(px)) {
- if (!map[x]) map[x] = {}
- map[x][y] = true
- }
- })
- var scan_value = true;
- if (fill_mode === 'color_connected') {
- function checkPx(x, y) {
- if (map[x] && map[x][y]) {
- map[x][y] = false;
-
- checkPx(x+1, y)
- checkPx(x-1, y)
- checkPx(x, y+1)
- checkPx(x, y-1)
- }
- }
- checkPx(x, y)
- scan_value = false;
- }
- Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
- if (map[x] && map[x][y] === scan_value) {
- var pxcolor = {
- r: px[0],
- g: px[1],
- b: px[2],
- a: px[3]/255
- }
- var result_color = Painter.combineColors(pxcolor, color, b_opacity);
- px[0] = result_color.r
- px[1] = result_color.g
- px[2] = result_color.b
- if (!Painter.lock_alpha) px[3] = result_color.a*255
- }
- })
+ }, {no_undo: true, use_cache: true, no_update});
+ },
+ useBrush(texture, ctx, x, y, event) {
+
+ var color = tinycolor(ColorPanel.get()).toRgb();
+ var size = BarItems.slider_brush_size.get();
+ let softness = BarItems.slider_brush_softness.get()/100;
+ let b_opacity = BarItems.slider_brush_opacity.get()/100;
+ let tool = Toolbox.selected.id;
+
+ ctx.clip()
+ if (event.touches && event.touches[0] && event.touches[0].force) {
+
+ // Stylus
+ var touch = event.touches[0];
+ if (touch.force == 1) touch.force == Painter.current.force || 0;
+ Painter.current.force = touch.force;
+
+ if (settings.brush_opacity_modifier.value == 'pressure' && touch.force) {
+ b_opacity = Math.clamp(b_opacity * touch.force*1.25, 0, 100);
+
+ } else if (settings.brush_opacity_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
+ var modifier = Math.clamp(1.5 / (touch.altitudeAngle + 0.3), 1, 4)/2;
+ b_opacity = Math.clamp(b_opacity * modifier, 0, 100);
+ }
+ if (settings.brush_size_modifier.value == 'pressure' && touch.force) {
+ size = Math.clamp(touch.force * size * 2, 1, 20);
+
+ } else if (settings.brush_size_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
+ size *= Math.clamp(1.5 / (touch.altitudeAngle + 0.3), 1, 4);
+ }
+ }
+
+ if (tool === 'brush_tool') {
+ Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity, px, py) {
+ var a = b_opacity * opacity;
+ var before = Painter.getAlphaMatrix(texture, px, py)
+ Painter.setAlphaMatrix(texture, px, py, a);
+ if (a > before) {
+ a = (a - before) / (1 - before);
+ } else if (before) {
+ a = 0;
}
- } else {
- ctx.clip()
- if (event.touches && event.touches[0] && event.touches[0].force) {
- var touch = event.touches[0];
- if (touch.force == 1) touch.force == Painter.current.force || 0;
- Painter.current.force = touch.force;
-
- if (settings.brush_opacity_modifier.value == 'pressure' && touch.force) {
- b_opacity = Math.clamp(b_opacity * touch.force*1.25, 0, 100);
- //m_opacity = Math.clamp(m_opacity * touch.force*1.25, 0, 100);
-
- } else if (settings.brush_opacity_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
- var modifier = Math.clamp(1.5 / (touch.altitudeAngle + 0.3), 1, 4)/2;
- b_opacity = Math.clamp(b_opacity * modifier, 0, 100);
- //m_opacity = Math.clamp(m_opacity * modifier, 0, 100);
- }
- if (settings.brush_size_modifier.value == 'pressure' && touch.force) {
- size = Math.clamp(touch.force * size * 2, 1, 20);
+ var result_color = Painter.combineColors(pxcolor, color, a);
+ if (Painter.lock_alpha) result_color.a = pxcolor.a
+ return result_color;
+ })
+ } else if (tool === 'eraser') {
+ Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity, px, py) {
+ if (Painter.lock_alpha) return pxcolor;
- } else if (settings.brush_size_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
- size *= Math.clamp(1.5 / (touch.altitudeAngle + 0.3), 1, 4);
- }
+ var a = b_opacity * opacity;
+
+ var before = Painter.getAlphaMatrix(texture, px, py)
+ Painter.setAlphaMatrix(texture, px, py, a);
+
+ if (a > before) {
+ a = (a - before) / (1 - before);
+ } else if (before) {
+ a = 0;
}
+ pxcolor.a = Math.clamp(pxcolor.a * (1-a), 0, 1);
+ return pxcolor;
- if (tool === 'brush_tool') {
- Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity, px, py) {
- var a = b_opacity * opacity;
- var before = Painter.getAlphaMatrix(texture, px, py)
- Painter.setAlphaMatrix(texture, px, py, a);
- if (a > before) {
- a = (a - before) / (1 - before);
- } else if (before) {
- a = 0;
- }
- var result_color = Painter.combineColors(pxcolor, color, a);
- if (Painter.lock_alpha) result_color.a = pxcolor.a
- return result_color;
- })
- } else if (tool === 'eraser') {
- Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity) {
- return {
- r: pxcolor.r,
- g: pxcolor.g,
- b: pxcolor.b,
- a: Painter.lock_alpha ? pxcolor.a : (pxcolor.a*(1-b_opacity))
- };
- })
+ })
+ }
+ ctx.restore();
+ },
+ useFilltool(texture, ctx, x, y, area) {
+
+ var color = tinycolor(ColorPanel.get()).toRgb();
+ let b_opacity = BarItems.slider_brush_opacity.get()/100;
+ let tool = Toolbox.selected.id;
+
+ let {rect, uvFactorX, uvFactorY, w, h} = area;
+
+ ctx.fillStyle = tinycolor(ColorPanel.get()).setAlpha(b_opacity).toRgbString();
+
+ var fill_mode = BarItems.fill_mode.get()
+ var cube = Painter.current.cube;
+ if (cube && fill_mode === 'cube') {
+ for (var face in cube.faces) {
+ var tag = cube.faces[face]
+ ctx.beginPath();
+ if (tag.texture === Painter.current.texture.uuid) {
+ var face_rect = getRectangle(
+ Math.floor(tag.uv[0] * uvFactorX),
+ Math.floor(tag.uv[1] * uvFactorY),
+ Math.ceil(tag.uv[2] * uvFactorX),
+ Math.ceil(tag.uv[3] * uvFactorY)
+ )
+ ctx.rect(face_rect.ax, face_rect.ay, face_rect.x, face_rect.y)
+ ctx.fill()
}
- ctx.restore();
}
- Painter.editing_area = undefined;
- }, {no_undo: true, use_cache: true, no_update});
+ } else if (fill_mode === 'face') {
+ ctx.fill()
+ } else {
+
+ var pxcol = [];
+ var map = {}
+ Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => {
+ px.forEach((val, i) => {
+ pxcol[i] = val
+ })
+ })
+ Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
+ if (pxcol.equals(px)) {
+ if (!map[x]) map[x] = {}
+ map[x][y] = true
+ }
+ })
+ var scan_value = true;
+ if (fill_mode === 'color_connected') {
+ function checkPx(x, y) {
+ if (map[x] && map[x][y]) {
+ map[x][y] = false;
+
+ checkPx(x+1, y)
+ checkPx(x-1, y)
+ checkPx(x, y+1)
+ checkPx(x, y-1)
+ }
+ }
+ checkPx(x, y)
+ scan_value = false;
+ }
+ Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
+ if (map[x] && map[x][y] === scan_value) {
+ var pxcolor = {
+ r: px[0],
+ g: px[1],
+ b: px[2],
+ a: px[3]/255
+ }
+ var result_color = Painter.combineColors(pxcolor, color, b_opacity);
+ px[0] = result_color.r
+ px[1] = result_color.g
+ px[2] = result_color.b
+ if (!Painter.lock_alpha) px[3] = result_color.a*255
+ }
+ })
+ }
},
runMirrorBrush(texture, x, y, event, uvTag) {
if (uvTag && Painter.current.cube) {
@@ -436,7 +460,7 @@ const Painter = {
let cube = Painter.current.cube;
Painter.current.cube = mirror_cube;
- Painter.useBrush(texture, ...point_on_uv, event, face.uv, true, true);
+ Painter.useBrushlike(texture, ...point_on_uv, event, face.uv, true, true);
Painter.current.cube = cube;
}
}
@@ -457,7 +481,7 @@ const Painter = {
while (i <= length) {
x = Math.round(start_x + diff_x / length * i)
y = Math.round(start_y + diff_y / length * i)
- Painter.useBrush(texture, x, y, event, uv, i < length-1);
+ Painter.useBrushlike(texture, x, y, event, uv, i < length-1);
i++;
}
},
@@ -617,15 +641,16 @@ const Painter = {
},
getMirrorCube(cube) {
let center = Format.centered_grid ? 0 : 8;
+ let e = 0.002
if (cube.from[0]-center === center-cube.to[0] && !cube.rotation[1] && !cube.rotation[2]) {
return cube;
} else {
for (var cube2 of Cube.all) {
if (
cube.inflate === cube2.inflate &&
- cube.from[2] === cube2.from[2] && cube.to[2] === cube2.to[2] &&
- cube.from[1] === cube2.from[1] && cube.to[1] === cube2.to[1] &&
- cube.size(0) === cube2.size(0) && cube.to[0]-center === center-cube2.from[0]
+ Math.epsilon(cube.from[2], cube2.from[2], e) && Math.epsilon(cube.to[2], cube2.to[2], e) &&
+ Math.epsilon(cube.from[1], cube2.from[1], e) && Math.epsilon(cube.to[1], cube2.to[1], e) &&
+ Math.epsilon(cube.size(0), cube2.size(0), e) && Math.epsilon(cube.to[0]-center, center-cube2.from[0], e)
) {
return cube2;
}
@@ -957,7 +982,7 @@ BARS.defineActions(function() {
condition: () => (Toolbox && ['brush_tool', 'eraser', 'draw_shape_tool'].includes(Toolbox.selected.id)),
category: 'paint',
settings: {
- min: 1, max: 20, interval: 1, default: 1,
+ min: 1, max: 50, interval: 1, default: 1,
}
})
new NumSlider('slider_brush_softness', {
diff --git a/js/texturing/texture_generator.js b/js/texturing/texture_generator.js
index 0599fcd78..2d08809ee 100644
--- a/js/texturing/texture_generator.js
+++ b/js/texturing/texture_generator.js
@@ -371,14 +371,23 @@ const TextureGenerator = {
coords.w*res_multiple,
coords.h*res_multiple
)
- if (coords.w*res_multiple > 2 && coords.h*res_multiple > 2 && color) {
- ctx.fillStyle = color
- ctx.fillRect(
- coords.x * res_multiple + 1,
- coords.y * res_multiple + 1,
- coords.w * res_multiple - 2,
- coords.h * res_multiple - 2
- )
+ if (coords.w*res_multiple > 2 && coords.h*res_multiple > 2) {
+ if (color == null) {
+ ctx.clearRect(
+ coords.x * res_multiple + 1,
+ coords.y * res_multiple + 1,
+ coords.w * res_multiple - 2,
+ coords.h * res_multiple - 2
+ )
+ } else if (color) {
+ ctx.fillStyle = color
+ ctx.fillRect(
+ coords.x * res_multiple + 1,
+ coords.y * res_multiple + 1,
+ coords.w * res_multiple - 2,
+ coords.h * res_multiple - 2
+ )
+ }
}
},
boxUVdrawTexture(face, coords, texture, canvas) {
@@ -423,9 +432,9 @@ const TextureGenerator = {
ctx.drawImage(
texture.img,
src.ax/Project.texture_width * texture.img.naturalWidth,
- src.ay/Project.texture_height * texture.img.naturalHeight,
+ src.ay/Project.texture_width * texture.img.naturalWidth,
src.x /Project.texture_width * texture.img.naturalWidth,
- src.y /Project.texture_height * texture.img.naturalHeight,
+ src.y /Project.texture_width * texture.img.naturalWidth,
coords.x*res_multiple*flip[0],
coords.y*res_multiple*flip[1],
coords.w*res_multiple*flip[0],
@@ -434,7 +443,7 @@ const TextureGenerator = {
ctx.restore()
return true;
},
- paintCubeBoxTemplate(cube, texture, canvas, template) {
+ paintCubeBoxTemplate(cube, texture, canvas, template, transparent) {
if (!template) {
template = new TextureGenerator.boxUVCubeTemplate(cube, Project.box_uv ? 0 : 1);
@@ -446,7 +455,7 @@ const TextureGenerator = {
if (!cube.faces[face].texture ||
!TextureGenerator.boxUVdrawTexture(cube.faces[face], d.place(template), texture, canvas)
) {
- TextureGenerator.boxUVdrawTemplateRectangle(d.c1, d.c2, cube.faces[face], d.place(template), texture, canvas)
+ TextureGenerator.boxUVdrawTemplateRectangle(d.c1, transparent ? null : d.c2, cube.faces[face], d.place(template), texture, canvas)
}
}
@@ -688,13 +697,15 @@ const TextureGenerator = {
w: ftemp.width,
h: ftemp.height
}
- var d = TextureGenerator.face_data[ftemp.face_key];
+ var d = TextureGenerator.face_data[ftemp.face_key];
+ var flip_rotation = false
if (!ftemp.texture ||
!drawTexture(ftemp.face, pos)
) {
drawTemplateRectangle(d.c1, d.c2, ftemp.face, pos)
+ } else {
+ flip_rotation = ftemp.face.rotation % 180 != 0;
}
- var flip_rotation = ftemp.face.rotation % 180 != 0;
ftemp.face.extend({
rotation: 0,
uv: flip_rotation ? [pos.y, pos.x] : [pos.x, pos.y]
diff --git a/js/texturing/textures.js b/js/texturing/textures.js
index bfef6879d..7c1375397 100644
--- a/js/texturing/textures.js
+++ b/js/texturing/textures.js
@@ -80,15 +80,10 @@ class Texture {
//Width / Animation
if (img.naturalWidth !== img.naturalHeight && Format.id == 'java_block') {
- if (img.naturalHeight % img.naturalWidth !== 0) {
- scope.error = 2;
- Blockbench.showQuickMessage('message.square_textures')
- } else {
- BARS.updateConditions()
- }
+ BARS.updateConditions()
}
- if (Project.box_uv && Format.single_texture) {
+ if (Project.box_uv && Format.single_texture && !scope.error) {
if (!scope.keep_size) {
let pw = Project.texture_width;
@@ -101,13 +96,12 @@ class Texture {
//Resolution of this texture has changed
var changed = size_control.old_width && (size_control.old_width != nw || size_control.old_height != nh);
//Resolution could be a multiple of project size
- var multi = !(
- (pw%nw || ph%nh) &&
- (nw%pw || nh%ph)
+ var multi = (
+ (pw%nw == 0 || nw%pw == 0) &&
+ (ph%nh == 0 || nh%ph == 0)
)
-
- if (unlike && changed) {
+ if (unlike && changed && !multi) {
Blockbench.showMessageBox({
translateKey: 'update_res',
icon: 'photo_size_select_small',
@@ -129,11 +123,6 @@ class Texture {
size_control.old_height = img.naturalHeight
}
-
-
- if ($('.dialog#texture_edit:visible').length > 0 && scope.selected === true) {
- scope.openMenu()
- }
TextureAnimator.updateButton()
Canvas.updateAllFaces(scope)
if (typeof scope.load_callback === 'function') {
@@ -141,7 +130,7 @@ class Texture {
delete scope.load_callback;
}
}
- this.img.onerror = function() {
+ this.img.onerror = function(error) {
if (isApp &&
!scope.isDefault &&
scope.mode !== 'bitmap' &&
@@ -166,7 +155,7 @@ class Texture {
case 0: return ''; break;
case 1: return tl('texture.error.file'); break;
//case 1: return tl('texture.error.invalid'); break;
- case 2: return tl('texture.error.ratio'); break;
+ //case 2: return tl('texture.error.ratio'); break;
case 3: return tl('texture.error.parent'); break;
}
}
@@ -402,6 +391,7 @@ class Texture {
function _replace() {
Blockbench.import({
+ resource_id: 'texture',
extensions: ['png', 'tga'],
type: 'PNG Texture',
readtype: 'image',
@@ -453,21 +443,21 @@ class Texture {
var scope = this;
this.stopWatcher();
- fs.watchFile(scope.path, {interval: 50}, function(curr, prev) {
- if (curr.mtime !== prev.mtime) {
- if (fs.existsSync(scope.path)) {
+ let timeout;
+ this.watcher = fs.watch(scope.path, (eventType) => {
+ if (eventType == 'change') {
+ if (timeout) clearTimeout(timeout)
+ timeout = setTimeout(() => {
scope.reloadTexture();
- } else {
- scope.stopWatcher();
- }
+ }, 60)
}
})
}
stopWatcher() {
- if (this.mode !== 'link' || !isApp || !fs.existsSync(this.path)) {
- return;
+ if (isApp && this.watcher) {
+ this.watcher.close()
}
- fs.unwatchFile(this.path)
+ return this;
}
generateFolder(path) {
var scope = this
@@ -499,6 +489,9 @@ class Texture {
}
return this;
}
+ getMaterial() {
+ return Canvas.materials[this.uuid]
+ }
//Management
select(event) {
textures.forEach(s => {
@@ -509,6 +502,7 @@ class Texture {
}
this.selected = true
textures.selected = this
+ this.scrollTo()
return this;
}
add(undo) {
@@ -655,31 +649,54 @@ class Texture {
openMenu() {
var scope = this
scope.select()
- showDialog('texture_edit')
+
+ let title = `${scope.name} (${scope.width} x ${scope.height})`;
+ var path = '';
if (scope.path) {
var arr = scope.path.split(osfs)
arr.splice(-1)
- var path = arr.join('/') + '/' + scope.name + ''
- $('#texture_edit #te_path').html(path)
- } else {
- $('#texture_edit #te_path').html('')
+ path = arr.join('/') + '/' + scope.name + ''
}
- $('#texture_edit #te_title').text(scope.name + ' ('+scope.img.naturalWidth+' x '+scope.img.naturalHeight+')')
- $('#texture_edit input#te_variable').val(scope.id)
- $('#texture_edit input#te_name').val(scope.name)
- $('#texture_edit input#te_folder').val(scope.folder)
- $('#texture_edit input#te_namespace').val(scope.namespace)
- $('#texture_menu_thumbnail').html(scope.img)
+ var dialog = new Dialog({
+ id: 'texture_edit',
+ title,
+ lines: [
+ ``
+ ],
+ form: {
+ name: {label: 'generic.name', value: scope.name},
+ variable: {label: 'dialog.texture.variable', value: scope.id, condition: () => Format.id === 'java_block'},
+ folder: {label: 'dialog.texture.folder', value: scope.folder, condition: () => Format.id === 'java_block'},
+ namespace: {label: 'dialog.texture.namespace', value: scope.namespace, condition: () => Format.id === 'java_block'},
+ },
+ onConfirm: function(results) {
- if (scope.mode === 'link') {
- $('#texture_edit .tool.link_only').show()
- $('#texture_edit .tool.bitmap_only').hide()
- } else {
- $('#texture_edit .tool.link_only').hide()
- $('#texture_edit .tool.bitmap_only').show()
- }
+ dialog.hide();
+ if (
+ (scope.name === results.name) &&
+ (results.variable === undefined || scope.id === results.variable) &&
+ (results.folder === undefined || scope.folder === results.folder) &&
+ (results.namespace === undefined || scope.namespace === results.namespace)
+ ) {
+ return;
+ }
+
+ Undo.initEdit({textures: [scope]})
+
+ scope.name = results.name;
+ if (results.variable !== undefined) scope.id = results.variable;
+ if (results.folder !== undefined) scope.folder = results.folder;
+ if (results.namespace !== undefined) scope.namespace = results.namespace;
+
+
+ Undo.finishEdit('texture_edit')
+ }
+ }).show()
}
resizeDialog() {
let scope = this;
@@ -776,6 +793,22 @@ class Texture {
dialog.show()
return this;
}
+ scrollTo() {
+ var el = $(`#texture_list > li[texid=${this.uuid}]`)
+ if (el.length === 0 || textures.length < 2) return;
+
+ var outliner_pos = $('#texture_list').offset().top
+ var el_pos = el.offset().top
+ if (el_pos > outliner_pos && el_pos + 48 < $('#texture_list').height() + outliner_pos) return;
+
+ var multiple = el_pos > outliner_pos ? 0.5 : 0.2
+ var scroll_amount = el_pos + $('#texture_list').scrollTop() - outliner_pos - 20
+ scroll_amount -= $('#texture_list').height()*multiple - 15
+
+ $('#texture_list').animate({
+ scrollTop: scroll_amount
+ }, 200);
+ }
//Export
javaTextureLink() {
var link = this.name.replace(/\.png$/, '')
@@ -819,6 +852,7 @@ class Texture {
find_path = arr.join(osfs)
}
Blockbench.export({
+ resource_id: 'texture',
type: 'PNG Texture',
extensions: ['png'],
name: scope.name,
@@ -968,29 +1002,6 @@ function saveTextures() {
}
})
}
-function saveTextureMenu() {
- hideDialog()
- var tex = textures.selected
-
- var name = $('#texture_edit input#te_name').val(),
- id = $('#texture_edit input#te_variable').val(),
- folder = $('#texture_edit input#te_folder').val(),
- namespace = $('#texture_edit input#te_namespace').val();
-
- if (!(
- tex.name === name &&
- tex.id === id &&
- tex.folder === folder &&
- tex.namespace === namespace)
- ) {
- Undo.initEdit({textures})
- tex.name = name;
- tex.id = id;
- tex.folder = folder;
- tex.namespace = namespace;
- Undo.finishEdit('texture_edit')
- }
-}
function loadTextureDraggable() {
Vue.nextTick(function() {
setTimeout(function() {
@@ -1046,14 +1057,6 @@ function unselectTextures() {
})
textures.selected = false
}
-function getTextureById(id) {
- if (id === undefined || id === false) return;
- if (id == null) {
- return {material: transparentMaterial};
- }
- id = id.replace('#', '');
- return $.grep(textures, function(e) {return e.id == id})[0];
-}
function getTexturesById(id) {
if (id === undefined) return;
id = id.replace('#', '');
@@ -1183,6 +1186,7 @@ BARS.defineActions(function() {
start_path = arr.join(osfs)
}
Blockbench.import({
+ resource_id: 'texture',
readtype: 'image',
type: 'PNG Texture',
extensions: ['png', 'tga'],
diff --git a/js/texturing/uv.js b/js/texturing/uv.js
index 9ecc7e177..68e35759c 100644
--- a/js/texturing/uv.js
+++ b/js/texturing/uv.js
@@ -20,6 +20,12 @@ class UVEditor {
this.setGrid(BarItems.uv_grid.get().replace(/x/, ''));
}
}
+ get width() {
+ return this.size;
+ }
+ set width(v) {
+ this.size = v;
+ }
buildDom(toolbar) {
var scope = this
if (this.jquery.main) {
@@ -47,6 +53,7 @@ class UVEditor {
`);
this.img = new Image();
+ this.img.style.objectFit = Format.animated_textures ? 'cover' : 'fill';
this.jquery.frame.append(this.img)
this.jquery.size = this.jquery.frame.find('div#uv_size')
this.jquery.viewport.append(this.jquery.frame)
@@ -56,6 +63,7 @@ class UVEditor {
if (Toolbox.selected.paintTool) {
this.jquery.size.hide()
}
+ this.jquery.main.toggleClass('checkerboard_trigger', settings.uv_checkerboard.value);
this.jquery.sliders = $('
')
@@ -271,13 +279,17 @@ class UVEditor {
var n = (event.deltaY < 0) ? 0.1 : -0.1;
n *= scope.zoom
var number = limitNumber(scope.zoom + n, 1, scope.max_zoom)
- if (Math.abs(number - scope.zoom) > 0.001) {
- this.scrollLeft += (scope.inner_width * n / 2) * (event.offsetX / scope.jquery.frame.width());
- this.scrollTop += (scope.inner_width * n / 2) * (event.offsetY / scope.jquery.frame.height());
- }
+ let old_zoom = scope.zoom;
+
+
scope.setZoom(number)
event.preventDefault()
e.preventDefault()
+
+ let zoom_diff = scope.zoom - old_zoom;
+ this.scrollLeft += ((this.scrollLeft + event.offsetX) * zoom_diff) / old_zoom
+ this.scrollTop += ((this.scrollTop + event.offsetY) * zoom_diff) / old_zoom
+
return false;
}
})
@@ -306,25 +318,29 @@ class UVEditor {
//Paint brush outline
this.brush_outline = $('
');
scope.jquery.frame.on('mouseenter mousemove', e => {
- if (Modes.paint && Toolbox.selected.brushTool) {
- scope.jquery.frame.append(this.brush_outline);
- var pixel_size = scope.inner_width / (scope.texture ? scope.texture.width : Project.texture_width);
- //pos
- let offset = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brushTool ? 0.5 : 0;
- let left = (0.5 - offset + Math.floor(e.offsetX / pixel_size + offset)) * pixel_size;
- let top = (0.5 - offset + Math.floor(e.offsetY / pixel_size + offset)) * pixel_size;
- this.brush_outline.css('left', left+'px').css('top', top+'px');
- //size
- var radius = (BarItems.slider_brush_size.get()/2) * pixel_size;
- this.brush_outline.css('padding', radius+'px').css('margin', (-radius)+'px');
- }
+ this.updateBrushOutline(e)
})
scope.jquery.frame.on('mouseleave', e => {
- this.brush_outline.detach();
})
this.setSize(this.size)
return this;
}
+ updateBrushOutline(e) {
+ if (Modes.paint && Toolbox.selected.brushTool) {
+ this.jquery.frame.append(this.brush_outline);
+ var pixel_size = this.inner_width / (this.texture ? this.texture.width : Project.texture_width);
+ //pos
+ let offset = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brushTool ? 0.5 : 0;
+ let left = (0.5 - offset + Math.floor(e.offsetX / pixel_size + offset)) * pixel_size;
+ let top = (0.5 - offset + Math.floor(e.offsetY / pixel_size + offset)) * pixel_size;
+ this.brush_outline.css('left', left+'px').css('top', top+'px');
+ //size
+ var radius = (BarItems.slider_brush_size.get()/2) * pixel_size;
+ this.brush_outline.css('padding', radius+'px').css('margin', (-radius)+'px');
+ } else {
+ this.brush_outline.detach();
+ }
+ }
message(msg, vars) {
msg = tl(msg, vars)
var box = $('
' + msg + '
')
@@ -703,12 +719,12 @@ class UVEditor {
this.jquery.viewport.css('overflow', 'hidden')
}
}
- setFace(face, update) {
+ setFace(face, update = true) {
this.face = face
if (this.id === 'main_uv') {
$('input#'+face+'_radio').prop("checked", true)
}
- if (update !== false) {
+ if (update) {
this.loadData()
}
return this;
@@ -867,7 +883,7 @@ class UVEditor {
}
var tex = face ? face.getTexture() : textures[0];
if (!tex || typeof tex !== 'object' || (tex.error && tex.error != 2)) {
- main_uv.img.src = '';
+ this.img.src = '';
this.img.style.display = 'none';
this.texture = false;
} else {
@@ -1795,9 +1811,9 @@ const uv_dialog = {
}
},
updateSelection: function() {
- $('#uv_dialog_all .UVEditor .uv_headline').removeClass('selected')
+ $('#uv_dialog_all .UVEditor').removeClass('selected')
uv_dialog.selection.forEach(function(id) {
- $('#uv_dialog_all #UVEditor_'+id+' .uv_headline').addClass('selected')
+ $('#uv_dialog_all #UVEditor_'+id).addClass('selected')
})
},
openDialog: function() {
diff --git a/js/transform.js b/js/transform.js
index 2c1845ea4..9bf32cb79 100644
--- a/js/transform.js
+++ b/js/transform.js
@@ -62,42 +62,6 @@ function getSelectionCenter() {
}
return center;
}
-function isMovementGlobal() {
- if (selected.length === 0 || (Toolbox.selected.id !== 'resize_tool')) {
- return true;
- }
- if (Format.rotate_cubes) {
- if (Cube.selected.length > 1) {
- if (Cube.selected[0].rotation.equals([0,0,0])) return true;
- var i = 0;
- while (i < Cube.selected.length) {
- if (!Cube.selected[0].rotation.equals(Cube.selected[i].rotation)) {
- return true;
- }
- i++;
- }
- }
- return Format.bone_rig && Group.selected;
- }
- if (Format.bone_rig) {
- if (Cube.selected[0] && Cube.selected[0].parent.type === 'group') {
- var ref_group = Cube.selected[0].parent
- var i = 0;
- while (i < Cube.selected.length) {
- var obj = Cube.selected[i]
- if (
- obj.parent.type !== 'group' ||
- !obj.parent.rotation.equals(ref_group.rotation)
- ) {
- return true;
- }
- i++;
- }
- return false
- }
- return true;
- }
-}
//Canvas Restriction
function isInBox(val) {
return !(Format.canvas_limit && !settings.deactivate_size_limit.value) || (val < 32 && val > -16)
@@ -229,7 +193,29 @@ const Vertexsnap = {
vertexes: new THREE.Object3D(),
vertexed_cubes: [],
hovering: false,
- addVertexes: function(cube) {
+ createVertexGizmo(cube, vec, id) {
+ //Each vertex needs it's own material for hovering
+ let outline_color = '0x'+CustomTheme.data.colors.accent.replace('#', '')
+ let material = new THREE.MeshBasicMaterial({color: parseInt(outline_color)})
+ let mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
+
+ let pos = mesh.position.copy(vec)
+ pos.applyMatrix4(cube.mesh.matrixWorld)
+ if (!Format.centered_grid) {
+ pos.addScalar(8)
+ }
+ mesh.rotation.copy(cube.mesh.rotation)
+ if (id == 100) {
+ mesh.rotation.y += Math.PI/4;
+ }
+ mesh.cube = cube
+ mesh.isVertex = true
+ mesh.vertex_id = id
+ mesh.material.transparent = true;
+ mesh.renderOrder = 999;
+ Vertexsnap.vertexes.add(mesh)
+ },
+ addVertices: function(cube) {
if (Vertexsnap.vertexed_cubes.includes(cube)) return;
if (cube.visibility === false) return;
@@ -239,21 +225,9 @@ const Vertexsnap = {
var o_vertices = cube.mesh.geometry.vertices
cube.mesh.updateMatrixWorld()
o_vertices.forEach(function(v, id) {
- var outline_color = '0x'+CustomTheme.data.colors.accent.replace('#', '')
- //Each vertex needs it's own material for hovering
- var material = new THREE.MeshBasicMaterial({color: parseInt(outline_color)})
- var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
- var pos = mesh.position.copy(v)
- pos.applyMatrix4(cube.mesh.matrixWorld)
- if (!Format.centered_grid) {
- pos.addScalar(8)
- }
- mesh.rotation.copy(cube.mesh.rotation)
- mesh.cube = cube
- mesh.isVertex = true
- mesh.vertex_id = id
- Vertexsnap.vertexes.add(mesh)
+ Vertexsnap.createVertexGizmo(cube, v, id)
})
+ Vertexsnap.createVertexGizmo(cube, new THREE.Vector3(), 100)
Vertexsnap.vertexed_cubes.push(cube)
Vertexsnap.updateVertexSize()
},
@@ -273,6 +247,7 @@ const Vertexsnap = {
Vertexsnap.vertexes.remove(v)
} else {
v.material.color.set(parseInt('0x'+CustomTheme.data.colors.accent.replace('#', '')))
+ v.material.depthTest = true;
}
})
}
@@ -285,6 +260,8 @@ const Vertexsnap = {
vertex.material.color.g = 1
Vertexsnap.hovering = true
+ vertex.material.depthTest = false;
+
if (Vertexsnap.step1 === false) {
//Line
var geometry = new THREE.Geometry();
@@ -302,7 +279,7 @@ const Vertexsnap = {
select: function() {
Vertexsnap.removeVertexes()
Cube.selected.forEach(function(obj) {
- Vertexsnap.addVertexes(obj)
+ Vertexsnap.addVertices(obj)
})
if (Cube.selected.length) {
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
@@ -320,6 +297,7 @@ const Vertexsnap = {
Vertexsnap.cubes = Cube.selected.slice()
Vertexsnap.removeVertexes()
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
+
} else {
Vertexsnap.snap(data)
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
@@ -329,55 +307,73 @@ const Vertexsnap = {
snap: function(data) {
Undo.initEdit({elements: Vertexsnap.cubes})
- var global_delta = data.vertex.position
- global_delta.sub(Vertexsnap.vertex_pos)
-
- if (BarItems.vertex_snap_mode.get() === 'scale') {
- //Scale
-
- var m;
- switch (Vertexsnap.vertex_id) {
- case 0: m=[ 1,1,1 ]; break;
- case 1: m=[ 1,1,0 ]; break;
- case 2: m=[ 1,0,1 ]; break;
- case 3: m=[ 1,0,0 ]; break;
- case 4: m=[ 0,1,0 ]; break;
- case 5: m=[ 0,1,1 ]; break;
- case 6: m=[ 0,0,0 ]; break;
- case 7: m=[ 0,0,1 ]; break;
- }
+ let mode = BarItems.vertex_snap_mode.get()
- Vertexsnap.cubes.forEach(function(obj) {
- var q = obj.mesh.getWorldQuaternion(new THREE.Quaternion()).inverse()
- var cube_pos = new THREE.Vector3().copy(global_delta).applyQuaternion(q)
+ if (Vertexsnap.vertex_id === 100) {
- for (i=0; i<3; i++) {
- if (m[i] === 1) {
- obj.to[i] = limitToBox(obj.to[i] + cube_pos.getComponent(i), obj.inflate)
- } else {
- obj.from[i] = limitToBox(obj.from[i] + cube_pos.getComponent(i), -obj.inflate)
- }
- }
- if (Project.box_uv && obj.visibility) {
- Canvas.updateUV(obj)
+ Vertexsnap.cubes.forEach(function(cube) {
+ let vec = new THREE.Vector3().copy(data.vertex.position)
+
+ if (Format.bone_rig && cube.parent instanceof Group && cube.mesh.parent) {
+ cube.mesh.parent.worldToLocal(vec);
}
+ let vec_array = vec.toArray()
+ vec_array.V3_add(cube.parent.origin);
+ cube.transferOrigin(vec_array)
})
} else {
- Vertexsnap.cubes.forEach(function(obj) {
- var cube_pos = new THREE.Vector3().copy(global_delta)
- if (Format.bone_rig && obj.parent instanceof Group && obj.mesh.parent) {
- var q = obj.mesh.parent.getWorldQuaternion(new THREE.Quaternion()).inverse();
- cube_pos.applyQuaternion(q);
- }
- if (Format.rotate_cubes) {
- obj.origin.V3_add(cube_pos);
- }
- var in_box = obj.moveVector(cube_pos.toArray());
- if (!in_box && Format.canvas_limit && !settings.deactivate_size_limit.value) {
- Blockbench.showMessageBox({translateKey: 'canvas_limit_error'})
+ var global_delta = data.vertex.position
+ global_delta.sub(Vertexsnap.vertex_pos)
+
+ if (mode === 'scale') {
+ //Scale
+
+ var m;
+ switch (Vertexsnap.vertex_id) {
+ case 0: m=[ 1,1,1 ]; break;
+ case 1: m=[ 1,1,0 ]; break;
+ case 2: m=[ 1,0,1 ]; break;
+ case 3: m=[ 1,0,0 ]; break;
+ case 4: m=[ 0,1,0 ]; break;
+ case 5: m=[ 0,1,1 ]; break;
+ case 6: m=[ 0,0,0 ]; break;
+ case 7: m=[ 0,0,1 ]; break;
}
- })
+
+ Vertexsnap.cubes.forEach(function(obj) {
+ var q = obj.mesh.getWorldQuaternion(new THREE.Quaternion()).inverse()
+ var cube_pos = new THREE.Vector3().copy(global_delta).applyQuaternion(q)
+
+ for (i=0; i<3; i++) {
+ if (m[i] === 1) {
+ obj.to[i] = limitToBox(obj.to[i] + cube_pos.getComponent(i), obj.inflate)
+ } else {
+ obj.from[i] = limitToBox(obj.from[i] + cube_pos.getComponent(i), -obj.inflate)
+ }
+ }
+ if (Project.box_uv && obj.visibility) {
+ Canvas.updateUV(obj)
+ }
+ })
+ } else if (mode === 'move') {
+ Vertexsnap.cubes.forEach(function(obj) {
+ var cube_pos = new THREE.Vector3().copy(global_delta)
+
+ if (Format.bone_rig && obj.parent instanceof Group && obj.mesh.parent) {
+ var q = obj.mesh.parent.getWorldQuaternion(new THREE.Quaternion()).inverse();
+ cube_pos.applyQuaternion(q);
+ }
+ if (Format.rotate_cubes) {
+ obj.origin.V3_add(cube_pos);
+ }
+ var in_box = obj.moveVector(cube_pos.toArray());
+ if (!in_box && Format.canvas_limit && !settings.deactivate_size_limit.value) {
+ Blockbench.showMessageBox({translateKey: 'canvas_limit_error'})
+ }
+ })
+ }
+
}
Vertexsnap.removeVertexes()
@@ -518,7 +514,7 @@ function setScaleAllPivot(mode) {
}
function scaleAllSelectOverflow() {
cancelScaleAll()
- selected.length = 0;
+ selected.empty();
scaleAll.overflow.forEach(obj => {
obj.selectLow()
})
@@ -601,24 +597,36 @@ function moveElementsInSpace(difference, axis) {
if (el.resizable) el.to[axis] += difference;
if (el.rotatable) el.origin[axis] += difference;
} else {
+ let move_origin = !!group;
if (group_m) {
var m = group_m
} else {
var m = new THREE.Vector3();
m[getAxisLetter(axis)] = difference;
-
- var rotation = new THREE.Quaternion();
- if (el.mesh) {
- el.mesh.getWorldQuaternion(rotation);
- } else if (el.parent instanceof Group) {
- el.parent.mesh.getWorldQuaternion(rotation);
+
+ let parent = el.parent;
+ while (parent instanceof Group) {
+ if (!parent.rotation.allEqual(0)) break;
+ parent = parent.parent;
+ }
+
+ if (parent == 'root') {
+ // If none of the parent groups are rotated, move origin.
+ move_origin = true;
+ } else {
+ var rotation = new THREE.Quaternion();
+ if (el.mesh) {
+ el.mesh.getWorldQuaternion(rotation);
+ } else if (el.parent instanceof Group) {
+ el.parent.mesh.getWorldQuaternion(rotation);
+ }
+ m.applyQuaternion(rotation.inverse());
}
- m.applyQuaternion(rotation.inverse());
}
if (el.movable) el.from.V3_add(m.x, m.y, m.z);
if (el.resizable) el.to.V3_add(m.x, m.y, m.z);
- if (group) {
+ if (move_origin) {
if (el.rotatable) el.origin.V3_add(m.x, m.y, m.z);
}
}
@@ -1315,13 +1323,16 @@ BARS.defineActions(function() {
new Action('toggle_visibility', {
icon: 'visibility',
category: 'transform',
- condition: {modes: ['edit']},
click: function () {toggleCubeProperty('visibility')}
})
+ new Action('toggle_locked', {
+ icon: 'fas.fa-lock',
+ category: 'transform',
+ click: function () {toggleCubeProperty('locked')}
+ })
new Action('toggle_export', {
icon: 'save',
category: 'transform',
- condition: {modes: ['edit']},
click: function () {toggleCubeProperty('export')}
})
new Action('toggle_autouv', {
@@ -1339,7 +1350,7 @@ BARS.defineActions(function() {
new Action('toggle_mirror_uv', {
icon: 'icon-mirror_x',
category: 'transform',
- condition: () => Project.box_uv && Modes.edit,
+ condition: () => Project.box_uv && (Modes.edit || Modes.paint),
click: function () {toggleCubeProperty('shade')}
})
new Action('update_autouv', {
diff --git a/js/util.js b/js/util.js
index a77eaf9ec..a34262353 100644
--- a/js/util.js
+++ b/js/util.js
@@ -162,6 +162,9 @@ Math.lerp = function(a,b,m) {
Math.isBetween = function(n, a, b) {
return (n - a) * (n - b) <= 0
}
+Math.epsilon = function(a, b, epsilon) {
+ return Math.abs(b - a) < epsilon
+}
Math.trimDeg = function(a) {
return (a+180*15)%360-180
}
@@ -181,13 +184,23 @@ Math.areMultiples = function(n1, n2) {
(n2/n1)%1 === 0
)
}
-Math.getNextPower =function(num, min) {
+Math.getNextPower = function(num, min) {
var i = min ? min : 2
while (i < num && i < 4000) {
i *= 2
}
return i;
}
+Math.snapToValues = function(val, snap_points, epsilon = 12) {
+ let snaps = snap_points.slice().sort((a, b) => {
+ return Math.abs(val-a) - Math.abs(val-b)
+ })
+ if (Math.abs(snaps[0] - val) < epsilon) {
+ return snaps[0]
+ } else {
+ return val
+ }
+}
function trimFloatNumber(val) {
if (val == '') return val;
var string = val.toFixed(4)
@@ -332,13 +345,17 @@ Array.prototype.remove = function (item) { {
}
}
Array.prototype.empty = function() {
- this.length = 0;
+ this.splice(0, Infinity);
return this;
}
Array.prototype.purge = function() {
this.splice(0, Infinity);
return this;
}
+Array.prototype.replace = function(items) {
+ this.splice(0, Infinity, ...items);
+ return this;
+}
Array.prototype.findInArray = function(key, value) {
for (var i = 0; i < this.length; i++) {
if (this[i][key] === value) return this[i]
diff --git a/js/web.js b/js/web.js
index 9d1592433..6985d738d 100644
--- a/js/web.js
+++ b/js/web.js
@@ -33,6 +33,9 @@ function initializeWebApp() {
}
})
}
+ if (Blockbench.browser == 'firefox') {
+ document.body.style.imageRendering = 'crisp-edges'
+ }
}
setInterval(function() {
diff --git a/lang/de.json b/lang/de.json
index 3f90de2af..15ef88859 100644
--- a/lang/de.json
+++ b/lang/de.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Abbrechen",
"action.slider_inflate": "Aufblasen",
"action.slider_inflate.desc": "Vergrößert Elemente in alle Richtungen, ohne dabei die Textur zu verändern",
- "action.brush_mode": "Pinselmodus",
"action.slider_brush_size": "Radius",
"action.slider_brush_size.desc": "Radius des Pinsels in Pixeln",
"action.slider_brush_opacity": "Deckkraft",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Aktuelle Version",
"dialog.update.installed": "Installierte Version",
"dialog.update.update": "Aktualisieren",
- "action.brush_mode.brush": "Rund",
- "action.brush_mode.noise": "Rauschen",
"action.vertex_snap_mode.move": "Verschieben",
"action.vertex_snap_mode.scale": "Größe ändern",
"action.open_model_folder": "Modellordner öffnen",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Beeinflusse die Größe des Pinsels bei Benutzung von einem Stylus",
"settings.brush_modifier.pressure": "Druck",
"settings.brush_modifier.tilt": "Neigung",
- "settings.class_export_version": "Mod-Entity Exportversopm",
- "settings.class_export_version.desc": "Die Spielversion, für die Entitymodelle für Mods exportiert werden",
"category.color": "Farbe",
"action.import_theme": "Theme importieren",
"action.export_theme": "Theme exportieren",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Erstellt eine Palette aus einer Textur",
"action.sort_palette": "Palette sortieren",
"action.sort_palette.desc": "Sortiert alle Farben der Palette nach Farbton und Helligkeit",
- "action.clear_palette": "Palette leeren",
- "action.clear_palette.desc": "Alle Farben aus der Palette entfernen",
"action.timelapse": "Zeitraffer...",
"action.timelapse.desc": "Zeichnet eine Zeitrafferaufnahme von der Benutzung von Blockbench auf",
"action.add_keyframe": "Keyframe hinzufügen",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Ausschneiden",
"uv_editor.copy_paste_tool.mirror_x": "Spiegeln (X)",
"uv_editor.copy_paste_tool.mirror_y": "Spiegeln (Y)",
- "uv_editor.copy_paste_tool.rotate": "Um 90 Grad drehen"
+ "uv_editor.copy_paste_tool.rotate": "Um 90 Grad drehen",
+ "dialog.project.modded_entity_version": "Exportversion",
+ "dialog.save_angle.position": "Kameraposition",
+ "dialog.save_angle.target": "Zielpunkt",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Fensterrahmen",
+ "layout.color.frame.desc": "Rand und Titelleiste des Fensters",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Palette laden",
+ "action.load_palette.desc": "Eine der mitgelieferten Paletten laden",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "Verwendet",
+ "menu.palette.load.empty": "Leer",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/en.json b/lang/en.json
index f9a990b5f..04ae90842 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -230,6 +230,7 @@
"dialog.project.parent": "Parent Model",
"dialog.project.geoname": "Model Identifier",
"dialog.project.openparent": "Open Parent",
+ "dialog.project.modded_entity_version": "Export Version",
"dialog.project.ao": "Ambient Occlusion",
"dialog.project.box_uv": "Box UV",
"dialog.project.width": "Texture Width",
@@ -328,6 +329,8 @@
"dialog.save_angle.projection": "Projection",
"dialog.save_angle.projection.perspective": "Perspective",
"dialog.save_angle.projection.orthographic": "Orthographic",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
"dialog.input.title": "Input",
@@ -335,7 +338,6 @@
"dialog.update.refresh": "Refresh",
"dialog.update.up_to_date": "Blockbench is up to date!",
"dialog.update.connecting": "Connecting to server",
- "dialog.update.refresh": "Retry",
"dialog.update.no_connection": "No internet connection",
"dialog.update.latest": "Latest Version",
"dialog.update.installed": "Installed Version",
@@ -360,8 +362,9 @@
"dialog.model_stats.faces": "Faces",
"dialog.skin.title": "Create Skin",
- "dialog.skin.model": "Skin",
+ "dialog.skin.model": "Model",
"dialog.skin.texture": "Texture (Optional)",
+ "dialog.skin.pose": "Pose",
"dialog.settings.settings": "Settings",
"dialog.settings.keybinds": "Keybindings",
@@ -390,6 +393,8 @@
"layout.color.border.desc": "Border of buttons and inputs",
"layout.color.accent": "Accent",
"layout.color.accent.desc": "Slider thumb and other details",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
"layout.color.grid": "Grid",
"layout.color.grid.desc": "3D preview grid",
"layout.color.wireframe": "Wireframe",
@@ -465,10 +470,12 @@
"settings.base_grid": "Small Grid",
"settings.base_grid.desc": "Show small grid and axes",
- "settings.large_grid": "Large Grid",
- "settings.large_grid.desc": "Show 3x3 block grid",
- "settings.full_grid": "Full Large Grid",
- "settings.full_grid.desc": "Show 3x3 precise grid",
+ "settings.large_grid": "Block Grid",
+ "settings.large_grid.desc": "Show 16x16 block-size grid",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "settings.full_grid": "Precise Block Grid",
+ "settings.full_grid.desc": "Show pixel-precise block grid",
"settings.large_box": "Large Box",
"settings.large_box.desc": "Show 3x3 block boundaries",
"settings.display_grid": "Display Mode",
@@ -516,8 +523,6 @@
"settings.brush_modifier.tilt": "Tilt",
"settings.brush_modifier.none": "None",
- "settings.dialog_unsaved_textures": "Unsaved Textures",
- "settings.dialog_unsaved_textures.desc": "Show \"Unsaved Textures\" dialog",
"settings.dialog_larger_cubes": "Model Too Large",
"settings.dialog_larger_cubes.desc": "Show \"Model Too Large\" dialog",
"settings.dialog_rotation_limit": "Rotation Limits",
@@ -527,8 +532,6 @@
"settings.minifiedout.desc": "Write JSON file in one line",
"settings.export_groups": "Export Groups",
"settings.export_groups.desc": "Save groups in blockmodel files",
- "settings.class_export_version": "Modded Entity Export Version",
- "settings.class_export_version.desc": "The format version for modded entity models",
"settings.credit": "Credit Comment",
"settings.credit.desc": "Add a credit comment to exported files",
"settings.sketchfab_token": "Sketchfab Token",
@@ -698,6 +701,8 @@
"action.reset_keybindings.desc": "Reset all keybindings to Blockbench's defaults",
"action.load_plugin": "Load Plugin from File",
"action.load_plugin.desc": "Load a plugin by importing the source file",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
"action.reload_plugins": "Reload Plugins",
"action.reload_plugins.desc": "Reload all development plugins",
"action.reset_layout": "Reset Layout",
@@ -732,7 +737,7 @@
"action.outliner_toggle": "Toggle More Options",
"action.outliner_toggle.desc": "Toggles switches for more options in the outliner",
"action.cube_counter": "Cube Counter",
- "action.cube_counter": "Displays the current number of cubes and other statistics",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
"action.duplicate": "Duplicate",
"action.duplicate.desc": "Duplicates the selected cubes or group",
@@ -740,6 +745,8 @@
"action.delete.desc": "Deletes the selected cubes or group",
"action.sort_outliner": "Sort Outliner",
"action.sort_outliner.desc": "Sort the outliner alphabetically",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
"action.select_window": "Select...",
@@ -764,8 +771,8 @@
"action.generate_palette.desc": "Generate palette from a texture",
"action.sort_palette": "Sort Palette",
"action.sort_palette.desc": "Sort all colors on the palette by color and brightness",
- "action.clear_palette": "Clear Palette",
- "action.clear_palette.desc": "Remove all colors from the palette",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
"action.transform_space": "Transform Space",
"action.transform_space.desc": "Default transform space for elements and bones",
@@ -788,6 +795,8 @@
"action.toggle_visibility.desc": "Toggle the visibility of the selected cubes",
"action.toggle_export": "Toggle Export",
"action.toggle_export.desc": "Toggle the export setting of the selected cubes",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
"action.toggle_autouv": "Toggle Auto UV",
"action.toggle_autouv.desc": "Toggle the auto UV setting of the selected cubes",
"action.toggle_shade": "Toggle Shading",
@@ -801,6 +810,10 @@
"action.add_display_preset": "New Preset",
"action.add_display_preset.desc": "Add a new display setting preset",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
"action.gui_light": "GUI Light",
"action.gui_light.desc": "Select the way the item is lit in the inventory",
"action.gui_light.side": "Side Light",
@@ -924,6 +937,8 @@
"action.change_keyframe_file.desc": "Select an audio file to preview a sound effect.",
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
"action.reverse_keyframes": "Reverse Keyframes",
"action.reverse_keyframes.desc": "Reverse the order of the selected keyframes",
"action.select_all_keyframes": "Select All Keyframes",
@@ -942,8 +957,9 @@
"action.previous_keyframe.desc": "Jump to the previous keyframe",
"action.next_keyframe": "Next Keyframe",
"action.next_keyframe.desc": "Jump to the next keyframe",
- "action.bring_up_all_animations": "Bring Up All Animations",
+ "action.bring_up_all_animations": "Bring Up All Animators",
"action.bring_up_all_animations.desc": "Brings all modified animators into the timeline",
+ "action.fold_all_animations": "Fold All Animators",
"action.clear_timeline": "Clear Timeline",
"action.clear_timeline.desc": "Clear all unselected bones from the timeline",
"action.select_effect_animator": "Animate Effects",
@@ -951,6 +967,7 @@
"action.timeline_focus": "Channel",
"action.timeline_focus.desc": "Select the animation channels to display in the timeline",
"action.timeline_focus.all": "All",
+ "action.timeline_focus.used": "In Use",
"timeline.rotation": "Rotation",
@@ -1007,8 +1024,8 @@
"menu.group.sort": "Sort",
"menu.group.resolve": "Resolve",
- "menu.palette.load": "Load Palette",
"menu.palette.load.default": "Default",
+ "menu.palette.load.empty": "Blank",
"menu.texture.face": "Apply to Face",
"menu.texture.blank": "Apply to Untextured Faces",
@@ -1077,6 +1094,7 @@
"cube.color.lime": "Lime",
"switches.visibility": "Visibility",
+ "switches.lock": "Lock",
"switches.export": "Export",
"switches.shading": "Shade",
"switches.mirror": "Mirror UV",
@@ -1152,6 +1170,9 @@
"direction.top": "Top",
"direction.bottom": "Bottom",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left",
+
"dialog.edit_session.title": "Edit Session",
"edit_session.username": "Username",
"edit_session.token": "Token",
diff --git a/lang/es.json b/lang/es.json
index 7821e7771..2669bb8c5 100644
--- a/lang/es.json
+++ b/lang/es.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Cancelar",
"action.slider_inflate": "Inflar",
"action.slider_inflate.desc": "Inflar cubos en todas las direcciones sin cambiar el UV",
- "action.brush_mode": "Modo Pincel",
"action.slider_brush_size": "Tamaño",
"action.slider_brush_size.desc": "Radio del pincel en píxeles",
"action.slider_brush_opacity": "Opacidad",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Última version",
"dialog.update.installed": "Versión instalada",
"dialog.update.update": "Actualizar",
- "action.brush_mode.brush": "Redondo",
- "action.brush_mode.noise": "Ruido",
"action.vertex_snap_mode.move": "Mover",
"action.vertex_snap_mode.scale": "Reescalar",
"action.open_model_folder": "Abrir la Carpeta del Modelo",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Modifica el tamaño del pincel al usar un lápiz",
"settings.brush_modifier.pressure": "Presión",
"settings.brush_modifier.tilt": "Inclinar",
- "settings.class_export_version": "Versión de Exportación de Entidad de Mod",
- "settings.class_export_version.desc": "La versión de formato para modelos de entidades de mod",
"category.color": "Color",
"action.import_theme": "Importar Tema",
"action.export_theme": "Exportar Tema",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Genera la paleta a partir de una textura",
"action.sort_palette": "Organizar Paleta",
"action.sort_palette.desc": "Organiza todos los colores de la paleta por color y brillo",
- "action.clear_palette": "Limpiar Paleta",
- "action.clear_palette.desc": "Elimina todos los colores de la Paleta",
"action.timelapse": "Timelapse...",
"action.timelapse.desc": "Graba un timelapse de tu proceso de modelaje",
"action.add_keyframe": "Añadir Frame Clave",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cortar",
"uv_editor.copy_paste_tool.mirror_x": "Invertir X",
"uv_editor.copy_paste_tool.mirror_y": "Invertir Y",
- "uv_editor.copy_paste_tool.rotate": "Rotar 90 Grados"
+ "uv_editor.copy_paste_tool.rotate": "Rotar 90 Grados",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/fr.json b/lang/fr.json
index fd5fb0fe4..59ed976d5 100644
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Annuler",
"action.slider_inflate": "Gonflement",
"action.slider_inflate.desc": "Grossir les cubes dans toutes les directions sans changer les UV.",
- "action.brush_mode": "Mode pinceau",
"action.slider_brush_size": "Taille",
"action.slider_brush_size.desc": "Rayon du pinceau en pixels",
"action.slider_brush_opacity": "Opacité",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Dernière version",
"dialog.update.installed": "Version installée",
"dialog.update.update": "Mettre à jour",
- "action.brush_mode.brush": "Rond",
- "action.brush_mode.noise": "Bruit",
"action.vertex_snap_mode.move": "Déplacer",
"action.vertex_snap_mode.scale": "Redimensionner",
"action.open_model_folder": "Ouvrir le dossier du modèle",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Modifier la taille du pinceau avec un stylet",
"settings.brush_modifier.pressure": "Pression",
"settings.brush_modifier.tilt": "Inclinaison",
- "settings.class_export_version": "Version d'export d'entité moddée",
- "settings.class_export_version.desc": "Version de format pour des modèles d'entités moddées",
"category.color": "Couleur",
"action.import_theme": "Thème d'importation",
"action.export_theme": "Thème d'exportation",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Générer une palette à partir d'une texture",
"action.sort_palette": "Trier palette",
"action.sort_palette.desc": "Trier toutes les couleurs par teinte et luminosité",
- "action.clear_palette": "Vider la palette",
- "action.clear_palette.desc": "Retire toutes les couleurs de la palette",
"action.timelapse": "Timelapse…",
"action.timelapse.desc": "Enregistrer un timelapse de la modélisation",
"action.add_keyframe": "Ajouter une keyframe",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Couper",
"uv_editor.copy_paste_tool.mirror_x": "Miroir X",
"uv_editor.copy_paste_tool.mirror_y": "Miroir Y",
- "uv_editor.copy_paste_tool.rotate": "Rotation de 90 degrés"
+ "uv_editor.copy_paste_tool.rotate": "Rotation de 90 degrés",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/it.json b/lang/it.json
index ac215d749..32c74a16f 100644
--- a/lang/it.json
+++ b/lang/it.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Annulla",
"action.slider_inflate": "Gonfiare",
"action.slider_inflate.desc": "Gonfiare cubi in tutte le direzioni non modificando l'UV",
- "action.brush_mode": "Modalità Pennello",
"action.slider_brush_size": "Dimensione",
"action.slider_brush_size.desc": "Raggio del pennello in pixel",
"action.slider_brush_opacity": "Opacità",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Versione più Recente",
"dialog.update.installed": "Versione Installata",
"dialog.update.update": "Aggiornamento",
- "action.brush_mode.brush": "Pennello",
- "action.brush_mode.noise": "Sfumatura",
"action.vertex_snap_mode.move": "Muovi",
"action.vertex_snap_mode.scale": "Scala",
"action.open_model_folder": "Apri Cartella Modello",
@@ -875,7 +872,7 @@
"panel.element.origin": "Origine",
"panel.element.rotation": "Rotazione",
"message.canvas_limit_error.title": "Errore di Limite della Tela",
- "message.canvas_limit_error.message": "The action could not be performed correctly because the format limits the canvas to 48 units. Shift the pivot point to prevent this.",
+ "message.canvas_limit_error.message": "L'azione non può essere eseguita correttamente perché il formato limita la tela a 48 unità. Spostare il punto di articolazione per impedirlo.",
"data.effect": "Effetto",
"generic.name": "Nome",
"settings.recent_projects": "Limite Modelli Recenti",
@@ -929,7 +926,7 @@
"message.timelapse_start": "Timelapse avviato",
"message.timelapse_stop": "Timelapse interrotto",
"message.import_palette.replace_palette": "Sostituisci vecchia tavolozza",
- "message.import_palette.threshold": "Merge Threshold",
+ "message.import_palette.threshold": "Unisci soglia",
"dialog.timelapse.interval": "Intervallo (Secondi)",
"dialog.timelapse.source": "Sorgente",
"dialog.timelapse.source.interface": "Interfaccia",
@@ -941,15 +938,13 @@
"layout.css": "CSS Personalizzato",
"settings.category.paint": "Dipingere",
"settings.deactivate_size_limit": "Disattiva Limite Dimensioni",
- "settings.deactivate_size_limit.desc": "Deactivate the size limit for specific model formats. WARNING: This can cause invalid models.",
+ "settings.deactivate_size_limit.desc": "Disattiva il limite di dimensioni per formati di modelli specifici. ATTENZIONE: ciò può causare modelli non validi.",
"settings.brush_opacity_modifier": "Modificatore Opacità Pennello",
"settings.brush_opacity_modifier.desc": "Modifica l'opacità del pennello quando viene utilizzato uno stilo",
"settings.brush_size_modifier": "Modificatore Dimensioni Pennello",
- "settings.brush_size_modifier.desc": "Modify the brush size when using a stylus",
+ "settings.brush_size_modifier.desc": "Modifica le dimensioni del pennello quando si utilizza uno stilo",
"settings.brush_modifier.pressure": "Pressione",
"settings.brush_modifier.tilt": "Inclinazione",
- "settings.class_export_version": "Versione Entity Export per Mod",
- "settings.class_export_version.desc": "La versione del formato per modelli entity per i mod",
"category.color": "Colore",
"action.import_theme": "Importa Tema",
"action.export_theme": "Esporta Tema",
@@ -969,13 +964,11 @@
"action.generate_palette.desc": "Genera una tavolozza da una texture",
"action.sort_palette": "Ordina Tavolozza",
"action.sort_palette.desc": "Ordina tutti i colori della tavolozza per colore e luminosità",
- "action.clear_palette": "Pulisci Tavolozza",
- "action.clear_palette.desc": "Rimuovi tutti i colori dalla tavolozza",
"action.timelapse": "Timelapse...",
"action.timelapse.desc": "Registra un timelapse del tuo processo di modellazione",
"action.add_keyframe": "Aggiungi Fotogramma",
"action.add_keyframe.desc": "Aggiungi un fotogramma automaticamente. Premi shift per forzare valori predefinti",
- "action.bring_up_all_animations.desc": "Brings all modified animators into the timeline",
+ "action.bring_up_all_animations.desc": "Porta tutti gli animatori modificati nella sequenza temporale",
"timeline.timeline": "Istruzioni",
"menu.palette.load": "Carica Tavolozza",
"menu.palette.load.default": "Predefinita",
@@ -1012,21 +1005,21 @@
"menu.help.donate": "Dona",
"menu.help.about": "Informazioni",
"menu.preview.background.clipboard": "Carica dagli Appunti",
- "dialog.ignore": "Ignore",
- "generic.unset": "Unset",
+ "dialog.ignore": "Ignora",
+ "generic.unset": "Non settato",
"message.invalid_builtin_parent.title": "Invalid Built-in Parent",
- "message.invalid_builtin_parent.message": "The link to the invalid parent model '%0' was removed in order to export a valid model.",
- "dialog.resize_texture.fill": "Fill with",
- "dialog.resize_texture.fill.transparent": "Transparent",
- "dialog.resize_texture.fill.color": "Color",
- "dialog.resize_texture.fill.repeat": "Repeat",
- "dialog.resize_texture.fill.stretch": "Stretch",
+ "message.invalid_builtin_parent.message": "Il collegamento al modello principale non valido '% 0' è stato rimosso per esportare un modello valido.",
+ "dialog.resize_texture.fill": "Riempire con",
+ "dialog.resize_texture.fill.transparent": "Trasparente",
+ "dialog.resize_texture.fill.color": "Colore",
+ "dialog.resize_texture.fill.repeat": "Ripeti",
+ "dialog.resize_texture.fill.stretch": "Allunga",
"dialog.scale.element_pivot": "Element Pivot",
- "dialog.scale.selection_center": "Selection Center",
- "dialog.create_gif.length_mode": "Length Mode",
- "dialog.create_gif.length_mode.seconds": "Seconds",
- "dialog.create_gif.length_mode.frames": "Frames",
- "dialog.create_gif.length_mode.animation": "Animation Length",
+ "dialog.scale.selection_center": "Centro di Selezione",
+ "dialog.create_gif.length_mode": "Modalità lunghezza",
+ "dialog.create_gif.length_mode.seconds": "Secondi",
+ "dialog.create_gif.length_mode.frames": "Montatura",
+ "dialog.create_gif.length_mode.animation": "Lunghezza della'animazione",
"dialog.create_gif.length_mode.turntable": "Turntable Rotation",
"dialog.save_angle.projection": "Projection",
"dialog.save_angle.projection.perspective": "Perspective",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/ja.json b/lang/ja.json
index 7f4806b31..82a2ba3aa 100644
--- a/lang/ja.json
+++ b/lang/ja.json
@@ -280,7 +280,6 @@
"keybind.cancel": "キャンセル",
"action.slider_inflate": "Inflate",
"action.slider_inflate.desc": "UVを変えずにキューブを膨張させる",
- "action.brush_mode": "Brush Mode",
"action.slider_brush_size": "Size",
"action.slider_brush_size.desc": "ブラシの半径",
"action.slider_brush_opacity": "Opacity",
@@ -541,8 +540,6 @@
"dialog.update.latest": "最新のバージョン",
"dialog.update.installed": "インストールされたバージョン",
"dialog.update.update": "更新する",
- "action.brush_mode.brush": "Round",
- "action.brush_mode.noise": "Noise",
"action.vertex_snap_mode.move": "Move",
"action.vertex_snap_mode.scale": "Scale",
"action.open_model_folder": "モデルフォルダを開く",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "ブラシの大きさを変更します",
"settings.brush_modifier.pressure": "筆圧",
"settings.brush_modifier.tilt": "チルト",
- "settings.class_export_version": "エンティティエクスポートバージョン",
- "settings.class_export_version.desc": "modエンティティモデルのバージョンを変更します",
"category.color": "カラー",
"action.import_theme": "テーマをインポート",
"action.export_theme": "テーマをエクスポート",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "テクスチャからパレットを生成します",
"action.sort_palette": "ソート",
"action.sort_palette.desc": "パレット上のすべてのカラーを色と明るさで並べ替えます",
- "action.clear_palette": "クリアー",
- "action.clear_palette.desc": "パレット上のすべてのカラーを削除します",
"action.timelapse": "タイムラプス…",
"action.timelapse.desc": "モデリングプロセスのタイムラプスを記録します",
"action.add_keyframe": "キーフレームを追加",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "カット",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90°"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90°",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/nl.json b/lang/nl.json
index fc6870212..b31c75bab 100644
--- a/lang/nl.json
+++ b/lang/nl.json
@@ -83,7 +83,7 @@
"message.default_textures.select": "Selecteer de standaard \"textures\"-map",
"message.image_editor.title": "Selecteer een afbeelding-bewerker",
"message.image_editor.file": "Selecteer Bestand...",
- "message.image_editor.exe": "Selecteer een foto-editor uitvoerbaar bestand",
+ "message.image_editor.exe": "Selecteer een foto-bewerker uitvoerbaar bestand",
"message.display_skin.title": "Toon Skin",
"message.display_skin.message": "Selecteer een skin bestand van je computer of vul een spelernaam in",
"message.display_skin.upload": "Upload Skin",
@@ -206,8 +206,8 @@
"settings.show_actions.desc": "Toon alle acties in de status balk",
"settings.backup_interval": "Backup Frequentie",
"settings.backup_interval.desc": "Frequentie van de automatische backups in minuten",
- "settings.origin_size": "Oorsprong Markeerder",
- "settings.origin_size.desc": "Grootte van de rotatie oorsprong",
+ "settings.origin_size": "Draaipunt Markeerder",
+ "settings.origin_size.desc": "Grootte van rotatie draaipunt markeerder",
"settings.control_size": "As Controle Grootte",
"settings.control_size.desc": "Grootte van de 3 assen controle gereedschap",
"settings.display_skin": "Toon Skin",
@@ -280,7 +280,6 @@
"keybind.cancel": "Annuleer",
"action.slider_inflate": "Opblazen",
"action.slider_inflate.desc": "Kubussen alle kanten op opblazen zonder de UV te veranderen",
- "action.brush_mode": "Mode",
"action.slider_brush_size": "Grootte",
"action.slider_brush_size.desc": "Formaat van de kwast",
"action.slider_brush_opacity": "Doorzichtigheid",
@@ -302,7 +301,7 @@
"action.resize_tool": "Formaat veranderen",
"action.resize_tool.desc": "Gereedschap om elementen te selecteren en te vergrootten",
"action.brush_tool": "Verfkwast",
- "action.brush_tool.desc": "Gereedschap om te tekenen op bitmap texturen op oppervlakken of in de UV editor.",
+ "action.brush_tool.desc": "Gereedschap om te tekenen op bitmap texturen op oppervlakken of in de UV bewerker.",
"action.vertex_snap_tool": "Hoeken Snap",
"action.vertex_snap_tool.desc": "Beweeg een kubus naar een ander kubus door twee hoeken te verbinden",
"action.swap_tools": "Verwissel Gereedschap",
@@ -413,8 +412,8 @@
"action.save_textures.desc": "Sla alle niet-opgeslagen texturen op",
"action.animated_textures": "Start Geanimeerde Texturen",
"action.animated_textures.desc": "Start en pauzeer de weergave van geanimeerde texturen",
- "action.origin_to_geometry": "Centreer Oorsprong",
- "action.origin_to_geometry.desc": "Zet de oorsprong naar het midden van de geometrie",
+ "action.origin_to_geometry": "Centreer Draaipunt",
+ "action.origin_to_geometry.desc": "Zet het draaipunt naar het midden van de geometrie",
"action.rescale_toggle": "Zet Herschaal Aan/Uit",
"action.rescale_toggle.desc": "Herschaal kubussen gebaseerd op hun huidige rotatie",
"action.bone_reset_toggle": "Herstel bot",
@@ -488,7 +487,7 @@
"panel.display": "Toning",
"panel.textures": "Afbeeldingen",
"panel.outliner": "Omlijning",
- "uv_editor.title": "UV Editor",
+ "uv_editor.title": "UV Bewerker",
"uv_editor.all_faces": "Alle",
"uv_editor.no_faces": "Geen",
"face.north": "Noord",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Laatste Versie",
"dialog.update.installed": "Geïnstalleerde Versie",
"dialog.update.update": "Update",
- "action.brush_mode.brush": "Penseel",
- "action.brush_mode.noise": "Lawaai ",
"action.vertex_snap_mode.move": "Beweeg",
"action.vertex_snap_mode.scale": "Schaal",
"action.open_model_folder": "Open Model Map",
@@ -707,7 +704,7 @@
"language_name": "Nederlands",
"message.plugin_reload": "%0 lokale plugins herlaadden",
"settings.brightness": "Helderheid",
- "settings.brightness.desc": "Helderheid van de ",
+ "settings.brightness.desc": "Helderheid van de voorvertoning. Standaard is 50",
"menu.preview.perspective.reset": "Herstel Camera",
"action.fill_mode": "Vul Modus",
"action.fill_mode.face": "Oppervlak",
@@ -725,7 +722,7 @@
"keybindings.recording": "Neem Toetsbinding Op",
"keybindings.press": "Druk op een knop of een combinatie van knoppen of klik ergens op het scherm om toetsbinding op te nemen",
"action.pivot_tool": "Draai Gereedschap",
- "action.pivot_tool.desc": "Gereedschap om het pivot punt van de kubussen en botten te veranderen",
+ "action.pivot_tool.desc": "Gereedschap om het Draaipunt van de kubussen en botten te veranderen",
"action.slider_animation_speed": "Terugspeelsnelheid",
"action.slider_animation_speed.desc": "Terugspeelsnelheid van de tijdlijn in percent",
"action.previous_keyframe": "Vorige Keyframe",
@@ -747,7 +744,7 @@
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token om blockbench te autoriseren om te uploaden naar u Sketchfab account",
"panel.color": "Kleur",
- "data.origin": "Rotatie",
+ "data.origin": "Draaipunt",
"message.sketchfab.success": "Model succesvol geüpload",
"message.sketchfab.error": "Gefaald om model up te loaden naar Sketchfab",
"settings.outliner_colors": "Omlijning Kleuren",
@@ -872,10 +869,10 @@
"panel.element": "Element",
"panel.element.position": "Positie",
"panel.element.size": "Grootte",
- "panel.element.origin": "Rotatie Punt",
+ "panel.element.origin": "Draaipunt",
"panel.element.rotation": "Rotatie",
"message.canvas_limit_error.title": "Canvas limiet Error",
- "message.canvas_limit_error.message": "De actie kon niet correct worden uitgevoerd omdat het format het canvas limiteert tot 48 eenheden. Verschuif de rotatie oorsprong om dit te voorkomen.",
+ "message.canvas_limit_error.message": "De actie kon niet correct worden uitgevoerd omdat het format het canvas limiteert tot 48 eenheden. Verschuif het draaipunt om dit te voorkomen.",
"data.effect": "Effect",
"generic.name": "Naam",
"settings.recent_projects": "Recent Model Limiet",
@@ -908,8 +905,8 @@
"action.slider_size.desc": "Herschaal kubussen op de %0 as",
"action.slider_rotation": "Roteer %0",
"action.slider_rotation.desc": "Roteer kubussen op de %0 as",
- "action.slider_origin": "Oorsprong %0",
- "action.slider_origin.desc": "Verplaats oorsprong op de %0 as",
+ "action.slider_origin": "Draaipunt %0",
+ "action.slider_origin.desc": "Verplaats draaipunt op de %0 as",
"action.rotate_cw": "Roteer %0 +90",
"action.rotate_cw.desc": "Roteer de geselecteerde kubussen 90° om de %0 as",
"action.rotate_ccw": "Roteer %0 -90",
@@ -935,7 +932,7 @@
"dialog.timelapse.source.interface": "Deactiveer het grootte limiet voor specifieke model formaten. WARNING: Dit kan ongeldige modellen veroorzaken.",
"dialog.timelapse.source.locked": "Vastgezette Hoek",
"dialog.timelapse.destination": "Bestemmingsmap",
- "layout.color.checkerboard": "Schaakbord",
+ "layout.color.checkerboard": "Dambord",
"layout.color.checkerboard.desc": "Achtergrond van canvas en UV editor",
"layout.font.code": "Code Font",
"layout.css": "Custom CSS",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Bewerk de kwast grootte wanneer een pen wordt gebruikt",
"settings.brush_modifier.pressure": "Druk",
"settings.brush_modifier.tilt": "Tilt",
- "settings.class_export_version": "Modded Entity Exporteer Versie",
- "settings.class_export_version.desc": "De formaat versie voor modded entity modellen",
"category.color": "Kleur",
"action.import_theme": "Importeer Thema",
"action.export_theme": "Exporteer Thema",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Genereer palette van een textuur",
"action.sort_palette": "Sorteer Palet",
"action.sort_palette.desc": "Sorteer alle kleuren op het palet op basis van kleur en helderheid",
- "action.clear_palette": "Maak Palet Schoon",
- "action.clear_palette.desc": "Verwijder alle kleuren van het palet",
"action.timelapse": "Timelapse...",
"action.timelapse.desc": "Neem een timelapse van u modeleer proces op",
"action.add_keyframe": "Voeg Keyframe toe",
@@ -1012,55 +1005,55 @@
"menu.help.donate": "Doneer",
"menu.help.about": "Over...",
"menu.preview.background.clipboard": "Laad van Klembord",
- "dialog.ignore": "Ignore",
- "generic.unset": "Unset",
- "message.invalid_builtin_parent.title": "Invalid Built-in Parent",
- "message.invalid_builtin_parent.message": "The link to the invalid parent model '%0' was removed in order to export a valid model.",
- "dialog.resize_texture.fill": "Fill with",
- "dialog.resize_texture.fill.transparent": "Transparent",
- "dialog.resize_texture.fill.color": "Color",
- "dialog.resize_texture.fill.repeat": "Repeat",
+ "dialog.ignore": "Negeer",
+ "generic.unset": "Ongezet",
+ "message.invalid_builtin_parent.title": "Ongeldige Ingebouwde Ouder",
+ "message.invalid_builtin_parent.message": "De link naar het ongeldige ouder model '%0' was verwijderd om een geldig model te exporteren",
+ "dialog.resize_texture.fill": "Vul met",
+ "dialog.resize_texture.fill.transparent": "Transparant",
+ "dialog.resize_texture.fill.color": "Kleur",
+ "dialog.resize_texture.fill.repeat": "Herhaal",
"dialog.resize_texture.fill.stretch": "Stretch",
- "dialog.scale.element_pivot": "Element Pivot",
- "dialog.scale.selection_center": "Selection Center",
- "dialog.create_gif.length_mode": "Length Mode",
- "dialog.create_gif.length_mode.seconds": "Seconds",
- "dialog.create_gif.length_mode.frames": "Frames",
- "dialog.create_gif.length_mode.animation": "Animation Length",
- "dialog.create_gif.length_mode.turntable": "Turntable Rotation",
- "dialog.save_angle.projection": "Projection",
- "dialog.save_angle.projection.perspective": "Perspective",
- "dialog.save_angle.projection.orthographic": "Orthographic",
- "dialog.sketchfab_uploader.animations": "Animations",
- "dialog.settings.theme": "Theme",
+ "dialog.scale.element_pivot": "Element Draaipunt",
+ "dialog.scale.selection_center": "Selectie Midden",
+ "dialog.create_gif.length_mode": "Lengte Modus",
+ "dialog.create_gif.length_mode.seconds": "Seconden",
+ "dialog.create_gif.length_mode.frames": "Beelden",
+ "dialog.create_gif.length_mode.animation": "Animatie Lengte",
+ "dialog.create_gif.length_mode.turntable": "Draaitafel Rotatie",
+ "dialog.save_angle.projection": "Projectie",
+ "dialog.save_angle.projection.perspective": "Perspectief",
+ "dialog.save_angle.projection.orthographic": "Orthografisch",
+ "dialog.sketchfab_uploader.animations": "Animaties",
+ "dialog.settings.theme": "Thema",
"settings.category.interface": "Interface",
- "settings.preview_checkerboard": "Preview Checkerboard",
- "settings.preview_checkerboard.desc": "Toggle the checkerboard background behind the preview",
- "settings.uv_checkerboard": "UV Editor Checkerboard",
- "settings.uv_checkerboard.desc": "Toggle the checkerboard background behind the UV editor",
- "category.paint": "Paint",
- "action.fill_mode.color_connected": "Connected Colors",
- "action.draw_shape_type": "Shape Type",
- "action.draw_shape_type.rectangle": "Rectangle",
- "action.draw_shape_type.rectangle_h": "Rectangle (Hollow)",
- "action.draw_shape_type.ellipse": "Ellipse",
- "action.draw_shape_type.ellipse_h": "Ellipse (Hollow)",
- "action.draw_shape_type.line": "Line",
- "action.mirror_painting": "Mirror Painting",
- "action.mirror_painting.description": "Mirror your paint strokes to the other side of the model",
- "action.lock_alpha": "Lock Alpha Channel",
- "action.lock_alpha.description": "Lock the transparency of all pixels",
- "action.draw_shape_tool": "Draw Shape",
- "action.draw_shape_tool.desc": "Tool to draw simple shapes on textures",
- "action.copy_paste_tool": "Copy Paste Tool",
- "action.copy_paste_tool.desc": "Tool to copy and paste selections of textures",
- "action.export_gltf": "Export As glTF",
- "action.export_gltf.desc": "Export model and animations as glTF file to use in other 3D applications",
- "action.transform_space": "Transform Space",
- "action.transform_space.desc": "Default transform space for elements and bones",
- "action.transform_space.global": "Global",
- "action.transform_space.bone": "Bone",
- "action.transform_space.local": "Local",
+ "settings.preview_checkerboard": "Preview Dambord",
+ "settings.preview_checkerboard.desc": "Zet de dambord achtergrond achter de voorvertoning",
+ "settings.uv_checkerboard": "UV Bewerker Dambord",
+ "settings.uv_checkerboard.desc": "Zet de dambord achtergrond achter de UV bewerker",
+ "category.paint": "Verf",
+ "action.fill_mode.color_connected": "Verbonden Kleuren",
+ "action.draw_shape_type": "Vorm Type",
+ "action.draw_shape_type.rectangle": "Rechthoek",
+ "action.draw_shape_type.rectangle_h": "Rechthoek (Hol)",
+ "action.draw_shape_type.ellipse": "Elipse",
+ "action.draw_shape_type.ellipse_h": "Elipse (Hol)",
+ "action.draw_shape_type.line": "Lijn",
+ "action.mirror_painting": "Spiegel Schilderij",
+ "action.mirror_painting.description": "Spiegel je verfstreken naar de andere kant van het model",
+ "action.lock_alpha": "Zet Alpha Kanaal Vast",
+ "action.lock_alpha.description": "Zet de doorzichtigheid van alle pixels vast",
+ "action.draw_shape_tool": "Teken Vorm",
+ "action.draw_shape_tool.desc": "Gereedschap om simpele vormen op texturen tekenen",
+ "action.copy_paste_tool": "Kopieer Plak Gereedschap",
+ "action.copy_paste_tool.desc": "Gereedschap om selecties van texturen te kopiëren en te plakken",
+ "action.export_gltf": "Exporteer Als glTF",
+ "action.export_gltf.desc": "Exporteer model en animaties als glTF bestand om te gebruiken in andere 3D applicaties",
+ "action.transform_space": "Transformeer Ruimte",
+ "action.transform_space.desc": "Standaard transformeer ruimte voor elementen en botten",
+ "action.transform_space.global": "Globaal",
+ "action.transform_space.bone": "Bot",
+ "action.transform_space.local": "Locaal",
"action.toggle_camera_projection": "Toggle Camera Projection",
"action.toggle_camera_projection.desc": "Toggle the camera projection between perspective and orthographic",
"action.load_camera_angle": "Camera Angle: %0",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/pl.json b/lang/pl.json
index 3e4b87892..88d7c24e1 100644
--- a/lang/pl.json
+++ b/lang/pl.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Anuluj",
"action.slider_inflate": "Nadmuchaj",
"action.slider_inflate.desc": "Nadmuchaj kostki we wszystkich kierunkach, bez zmieniania UV.",
- "action.brush_mode": "Tryb pędzla",
"action.slider_brush_size": "Wielkość",
"action.slider_brush_size.desc": "Promień pędzla w pikselach",
"action.slider_brush_opacity": "Nieprzezroczystość",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Najnowsza wersja",
"dialog.update.installed": "Zainstalowana wersja",
"dialog.update.update": "Aktualizacja",
- "action.brush_mode.brush": "Szczotka",
- "action.brush_mode.noise": "Mieszanie",
"action.vertex_snap_mode.move": "Przesuń",
"action.vertex_snap_mode.scale": "Skaluj",
"action.open_model_folder": "Otwórz folder modelu",
@@ -705,24 +702,24 @@
"action.open_backup_folder.desc": "Otwórz folder kopii zapasowej BlockBench",
"switches.mirror": "Lustro UV",
"language_name": "Polski",
- "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.face": "Face",
- "action.fill_mode.color": "Colors",
- "action.fill_mode.cube": "Cube",
- "action.toggle_mirror_uv": "Mirror UV",
+ "message.plugin_reload": "Przeładowano %0 lokalnych pluginów",
+ "settings.brightness": "Jasność",
+ "settings.brightness.desc": "Jasność podglądu. Domyślnie 50.",
+ "menu.preview.perspective.reset": "Zresetuj kamerę",
+ "action.fill_mode": "Tryb wypełniania",
+ "action.fill_mode.face": "Przód",
+ "action.fill_mode.color": "Kolory",
+ "action.fill_mode.cube": "Sześcian",
+ "action.toggle_mirror_uv": "Odbicie 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": "Przełącz pokrycie UV",
"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": "Skompresuj Szablon",
"action.action_control": "Action Control",
"action.action_control.desc": "Search and execute any available action",
- "keybindings.recording": "Recording Keybinding",
+ "keybindings.recording": "Nagrywanie skrótów klawiszowych",
"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",
@@ -739,25 +736,25 @@
"action.upload_sketchfab": "Sketchfab Upload",
"message.sketchfab.name_or_token": "Please enter your Sketchfab token and a name",
"dialog.sketchfab_uploader.title": "Upload Sketchfab Model",
- "dialog.sketchfab_uploader.token": "API Token",
+ "dialog.sketchfab_uploader.token": "Token API",
"dialog.sketchfab_uploader.about_token": "The token is used to connect Blockbench to your Sketchfab account. You can find it on %0",
- "dialog.sketchfab_uploader.name": "Model Name",
- "dialog.sketchfab_uploader.description": "Description",
- "dialog.sketchfab_uploader.tags": "Tags",
+ "dialog.sketchfab_uploader.name": "Nazwa modelu",
+ "dialog.sketchfab_uploader.description": "Opis",
+ "dialog.sketchfab_uploader.tags": "Tagi",
"settings.sketchfab_token": "Sketchfab Token",
"settings.sketchfab_token.desc": "Token to authorize Blockbench to upload to your Sketchfab account",
- "panel.color": "Color",
+ "panel.color": "Kolor",
"data.origin": "Pivot",
- "message.sketchfab.success": "Uploaded model successfully",
- "message.sketchfab.error": "Failed to upload model to Sketchfab",
+ "message.sketchfab.success": "Model został wysłany",
+ "message.sketchfab.error": "Wystąpił problem podczas udostępniania modelu na Sketchfab",
"settings.outliner_colors": "Outliner Colors",
"settings.outliner_colors.desc": "Display cube colors in the outliner",
- "action.upload_sketchfab.desc": "Upload your model to Sketchfab",
+ "action.upload_sketchfab.desc": "Udostępnij model na Sketchfab",
"action.element_colors": "Cube Colors",
"action.element_colors.desc": "Show cube colors in the outliner",
- "texture.error.file": "File not found",
- "texture.error.invalid": "Invalid file",
- "texture.error.ratio": "Invalid aspect ratio",
+ "texture.error.file": "Plik nieznaleziony",
+ "texture.error.invalid": "Niepoprawny plik",
+ "texture.error.ratio": "Niepoprawny format",
"texture.error.parent": "Texture file provided by parent model",
"message.recover_backup.title": "Recover Model",
"message.recover_backup.message": "Blockbench was closed without saving. Do you want to recover the model?",
@@ -771,23 +768,23 @@
"action.reset_keyframe": "Reset Keyframe",
"action.reset_keyframe.desc": "Reset all values of the selected keyframes",
"dialog.edit_session.title": "Edit Session",
- "edit_session.username": "Username",
+ "edit_session.username": "Nazwa użytkownika",
"edit_session.token": "Token",
"edit_session.about": "Edit Sessions can be used to collaborate on models across the internet. Create a session and copy the token and send it to friends, who can then use it to join.",
- "edit_session.join": "Join Session",
- "edit_session.create": "Create Session",
- "edit_session.quit": "Quit Session",
- "edit_session.joined": "%0 joined the session",
- "edit_session.left": "%0 left the session",
- "edit_session.quit_session": "Left current session",
+ "edit_session.join": "Dołącz do sesji",
+ "edit_session.create": "Stwórz sesję",
+ "edit_session.quit": "Wyjdź z sesji",
+ "edit_session.joined": "%0 dołączył do sesji",
+ "edit_session.left": "%0 opuścił sesję",
+ "edit_session.quit_session": "Opuść aktualną sesję",
"edit_session.status": "Status",
"edit_session.hosting": "Hosting",
- "edit_session.connected": "Connected to a session",
- "dialog.sketchfab_uploader.private": "Private (Pro)",
- "dialog.sketchfab_uploader.password": "Password (Pro)",
- "action.toggle_chat": "Toggle Chat",
- "action.toggle_chat.desc": "Toggle the visibility of the chat history",
- "action.uv_select_all": "Select All",
+ "edit_session.connected": "Połączono z sesją",
+ "dialog.sketchfab_uploader.private": "Prywatne (Pro)",
+ "dialog.sketchfab_uploader.password": "Hasło (Pro)",
+ "action.toggle_chat": "Przełącz czat",
+ "action.toggle_chat.desc": "Przełącz widoczność historii czatu",
+ "action.uv_select_all": "Zaznacz wszystkie",
"action.uv_select_all.desc": "Select all faces in the UV dialog",
"panel.chat": "Chat",
"edit_session.welcome": "Welcome to this session by %0",
@@ -805,10 +802,10 @@
"mode.start.recent": "Recent",
"format.free": "Free Model",
"format.free.desc": "Model without restrictions for Unity etc.",
- "format.java_block": "Java Block/Item",
- "format.java_block.desc": "Block model for Java Edition. Size and rotations are limited.",
+ "format.java_block": "Java Blok/Przedmiot",
+ "format.java_block.desc": "Model bloku na Java Edition. Rozmiar i kąty obrotu są ograniczone.",
"format.bedrock": "Bedrock Model",
- "format.bedrock.desc": "Model for Bedrock Edition",
+ "format.bedrock.desc": "Model na Bedrock Edition",
"format.bedrock_old": "Bedrock Legacy Model",
"format.bedrock_old.desc": "Pre-1.12 Bedrock Edition entity model",
"format.modded_entity": "Modded Entity",
@@ -829,24 +826,24 @@
"dialog.model_stats.locators": "Locators",
"dialog.model_stats.groups": "Groups",
"dialog.model_stats.vertices": "Vertices",
- "dialog.model_stats.faces": "Faces",
- "settings.username": "Username",
+ "dialog.model_stats.faces": "Powierzchnie",
+ "settings.username": "Nazwa użytkownika",
"settings.username.desc": "Username for edit sessions",
"settings.painting_grid": "Painting Grid",
"settings.painting_grid.desc": "Show grid on textured cubes in paint mode",
"action.slider_brush_min_opacity": "Minimum Opacity",
"action.slider_brush_min_opacity.desc": "Minimum opacity of the noise brush in percent",
- "action.convert_project": "Convert Project",
+ "action.convert_project": "Konwertuj projekt",
"action.convert_project.desc": "Converts the current project to a project for another model format",
- "action.close_project": "Close Project",
+ "action.close_project": "Zamknij projekt",
"action.close_project.desc": "Closes the currently open project",
"action.export_bedrock": "Export Bedrock Geometry",
"action.export_bedrock.desc": "Export the model as a bedrock edition geometry file.",
- "action.save_project": "Save Project",
+ "action.save_project": "Zapisz projekt",
"action.save_project.desc": "Saves the current model as a project file",
- "action.save_project_as": "Save Project As",
+ "action.save_project_as": "Zapisz projekt jako",
"action.save_project_as.desc": "Saves the current model as a project file at a new location",
- "action.export_over": "Save Model",
+ "action.export_over": "Zapisz model",
"action.export_over.desc": "Saves the model, textures and animations by overwriting the files",
"action.add_locator": "Add Locator",
"action.add_locator.desc": "Adds a new locator to control positions of particles, leashes etc",
@@ -856,13 +853,13 @@
"action.sidebar_right.desc": "Open the interface to edit elements",
"action.uv_turn_mapping": "Turn Mapping",
"action.uv_turn_mapping.desc": "Turn the UV mapping around 90 degrees",
- "action.remove_blank_faces": "Remove Blank Faces",
- "action.remove_blank_faces.desc": "Deletes all untextured faces of the selection",
- "menu.uv.select": "Select Cubes",
- "web.download_app": "Download App",
+ "action.remove_blank_faces": "Usuń puste powierzchnie",
+ "action.remove_blank_faces.desc": "Usuwa wszystkie nieoteksturowane powierzchnie z zaznaczenia",
+ "menu.uv.select": "Wybierz sześcian",
+ "web.download_app": "Pobierz aplikację",
"uv_editor.turned": "Turned Mapping",
"display.reference.crossbow": "Crossbow",
- "dialog.settings.search_results": "Search Results",
+ "dialog.settings.search_results": "Wyniki wyszukiwania",
"settings.animation_snap": "Animation Snap",
"settings.animation_snap.desc": "Snap interval for keyframes in the animation timeline in steps per second",
"action.import_optifine_part": "Import OptiFine Part",
@@ -870,10 +867,10 @@
"data.locator": "Locator",
"mode.start.no_recents": "No recently opened models",
"panel.element": "Element",
- "panel.element.position": "Position",
- "panel.element.size": "Size",
+ "panel.element.position": "Pozycja",
+ "panel.element.size": "Rozmiar",
"panel.element.origin": "Pivot Point",
- "panel.element.rotation": "Rotation",
+ "panel.element.rotation": "Obrót",
"message.canvas_limit_error.title": "Canvas Limit Error",
"message.canvas_limit_error.message": "The action could not be performed correctly because the format limits the canvas to 48 units. Shift the pivot point to prevent this.",
"data.effect": "Effect",
@@ -890,19 +887,19 @@
"action.select_effect_animator.desc": "Opens timeline to add sound and particle effects",
"action.timeline_focus": "Channel",
"action.timeline_focus.desc": "Select the animation channels to display in the timeline",
- "action.timeline_focus.all": "All",
- "timeline.particle": "Particle",
- "timeline.sound": "Sound",
- "timeline.effects": "Effects",
+ "action.timeline_focus.all": "Wszystko",
+ "timeline.particle": "Cząsteczka",
+ "timeline.sound": "Dźwięk",
+ "timeline.effects": "Efekty",
"data.format": "Format",
"format.optifine_part": "OptiFine Part",
"format.optifine_part.desc": "JPM part for OptiFine entity models",
"action.reverse_keyframes": "Reverse Keyframes",
"action.reverse_keyframes.desc": "Reverse the order of the selected keyframes",
- "generic.help": "Help",
- "message.removed_faces": "Removed %0 faces",
- "dialog.sketchfab_uploader.draft": "Draft",
- "action.slider_pos": "Move %0",
+ "generic.help": "Pomoc",
+ "message.removed_faces": "Usunięto %0 powierzchni",
+ "dialog.sketchfab_uploader.draft": "Szkic",
+ "action.slider_pos": "Przesuń %0",
"action.slider_pos.desc": "Move cubes on the %0 axis",
"action.slider_size": "Size %0",
"action.slider_size.desc": "Resize cubes on the %0 axis",
@@ -918,11 +915,11 @@
"action.flip.desc": "Flip the selected cubes on the %0 axis",
"action.center": "Center %0",
"action.center.desc": "Center the selected cubes on the %0 axis",
- "action.bring_up_all_animations": "Bring Up All Animations",
- "panel.bone": "Bone",
- "data.color": "Color",
- "generic.export": "Export",
- "generic.none": "None",
+ "action.bring_up_all_animations": "Bring Up All Animators",
+ "panel.bone": "Kość",
+ "data.color": "Kolor",
+ "generic.export": "Eksportuj",
+ "generic.none": "Żadne",
"status_bar.recording": "Recording Timelapse",
"message.add_to_palette": "Added to palette",
"message.size_modifiers": "Hold down Ctrl or Shift to transform in smaller increments.",
@@ -931,25 +928,23 @@
"message.import_palette.replace_palette": "Replace old palette",
"message.import_palette.threshold": "Merge Threshold",
"dialog.timelapse.interval": "Interval (Seconds)",
- "dialog.timelapse.source": "Source",
- "dialog.timelapse.source.interface": "Interface",
+ "dialog.timelapse.source": "Źródło",
+ "dialog.timelapse.source.interface": "Interfejs",
"dialog.timelapse.source.locked": "Locked Angle",
"dialog.timelapse.destination": "Destination Folder",
"layout.color.checkerboard": "Checkerboard",
"layout.color.checkerboard.desc": "Background of canvas and UV editor",
"layout.font.code": "Code Font",
"layout.css": "Custom CSS",
- "settings.category.paint": "Paint",
+ "settings.category.paint": "Maluj",
"settings.deactivate_size_limit": "Deactivate Size Limit",
"settings.deactivate_size_limit.desc": "Deactivate the size limit for specific model formats. WARNING: This can cause invalid models.",
"settings.brush_opacity_modifier": "Brush Opacity Modifier",
"settings.brush_opacity_modifier.desc": "Modify the brush opacity when using a stylus",
"settings.brush_size_modifier": "Brush Size Modifier",
"settings.brush_size_modifier.desc": "Modify the brush size when using a stylus",
- "settings.brush_modifier.pressure": "Pressure",
+ "settings.brush_modifier.pressure": "Nacisk",
"settings.brush_modifier.tilt": "Tilt",
- "settings.class_export_version": "Modded Entity Export Version",
- "settings.class_export_version.desc": "The format version for modded entity models",
"category.color": "Kolor",
"action.import_theme": "Importuj styl",
"action.export_theme": "Eksportuj styl",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Generate palette from a texture",
"action.sort_palette": "Sort Palette",
"action.sort_palette.desc": "Sort all colors on the palette by color and brightness",
- "action.clear_palette": "Wyczyść paletę",
- "action.clear_palette.desc": "Usuń wszystkie kolory z palety",
"action.timelapse": "Timelapse",
"action.timelapse.desc": "Nagraj timelaps procesu modelowania",
"action.add_keyframe": "Dodaj klatkę kluczową",
@@ -1010,26 +1003,26 @@
"menu.help.plugin_documentation": "Plugin API Documentation",
"menu.help.search_action": "Search and Run Action",
"menu.help.donate": "Wesprzyj",
- "menu.help.about": "About...",
+ "menu.help.about": "O Blockbench...",
"menu.preview.background.clipboard": "Wczytaj ze schowka",
- "dialog.ignore": "Ignore",
+ "dialog.ignore": "Ignoruj",
"generic.unset": "Unset",
"message.invalid_builtin_parent.title": "Invalid Built-in Parent",
"message.invalid_builtin_parent.message": "The link to the invalid parent model '%0' was removed in order to export a valid model.",
"dialog.resize_texture.fill": "Fill with",
- "dialog.resize_texture.fill.transparent": "Transparent",
- "dialog.resize_texture.fill.color": "Color",
- "dialog.resize_texture.fill.repeat": "Repeat",
- "dialog.resize_texture.fill.stretch": "Stretch",
+ "dialog.resize_texture.fill.transparent": "Przeźroczysty",
+ "dialog.resize_texture.fill.color": "Kolor",
+ "dialog.resize_texture.fill.repeat": "Powtórz",
+ "dialog.resize_texture.fill.stretch": "Rozciągnij",
"dialog.scale.element_pivot": "Element Pivot",
"dialog.scale.selection_center": "Selection Center",
"dialog.create_gif.length_mode": "Length Mode",
- "dialog.create_gif.length_mode.seconds": "Seconds",
- "dialog.create_gif.length_mode.frames": "Frames",
- "dialog.create_gif.length_mode.animation": "Animation Length",
+ "dialog.create_gif.length_mode.seconds": "Sekund",
+ "dialog.create_gif.length_mode.frames": "Klatki",
+ "dialog.create_gif.length_mode.animation": "Długość animacji",
"dialog.create_gif.length_mode.turntable": "Turntable Rotation",
"dialog.save_angle.projection": "Projection",
- "dialog.save_angle.projection.perspective": "Perspective",
+ "dialog.save_angle.projection.perspective": "Perspektywa",
"dialog.save_angle.projection.orthographic": "Orthographic",
"dialog.sketchfab_uploader.animations": "Animations",
"dialog.settings.theme": "Theme",
@@ -1045,7 +1038,7 @@
"action.draw_shape_type.rectangle_h": "Rectangle (Hollow)",
"action.draw_shape_type.ellipse": "Ellipse",
"action.draw_shape_type.ellipse_h": "Ellipse (Hollow)",
- "action.draw_shape_type.line": "Line",
+ "action.draw_shape_type.line": "Linia",
"action.mirror_painting": "Mirror Painting",
"action.mirror_painting.description": "Mirror your paint strokes to the other side of the model",
"action.lock_alpha": "Lock Alpha Channel",
@@ -1058,8 +1051,8 @@
"action.export_gltf.desc": "Export model and animations as glTF file to use in other 3D applications",
"action.transform_space": "Transform Space",
"action.transform_space.desc": "Default transform space for elements and bones",
- "action.transform_space.global": "Global",
- "action.transform_space.bone": "Bone",
+ "action.transform_space.global": "Globalne",
+ "action.transform_space.bone": "Kość",
"action.transform_space.local": "Local",
"action.toggle_camera_projection": "Toggle Camera Projection",
"action.toggle_camera_projection.desc": "Toggle the camera projection between perspective and orthographic",
@@ -1068,24 +1061,53 @@
"action.slider_face_tint": "Tint Index",
"action.slider_face_tint.desc": "Set the tint index of the current face. -1 means unset.",
"menu.help.quickstart": "Quickstart Wizard",
- "menu.help.developer": "Developer",
+ "menu.help.developer": "Deweloper",
"menu.help.developer.dev_tools": "Open Dev Tools",
"menu.help.developer.reset_storage": "Factory Reset",
"menu.help.developer.reset_storage.confirm": "Are you sure you want to reset Blockbench to factory settings? This will delete all custom settings, keybindings and installed plugins.",
"menu.help.developer.cache_reload": "Cache Reload",
- "menu.texture.resize": "Resize...",
+ "menu.texture.resize": "Zmień rozmiar...",
"menu.preview.orthographic": "Orthographic",
"menu.preview.save_angle": "Save Angle...",
"menu.preview.angle": "Angles",
"menu.preview.angle.initial": "Initial Angle",
- "menu.preview.angle.load": "Load",
- "menu.preview.maximize": "Maximize",
- "panel.color.both": "Both",
- "uv_editor.copy_selection": "Copy Selection",
- "uv_editor.paste_selection": "Paste Selection",
+ "menu.preview.angle.load": "Wczytaj",
+ "menu.preview.maximize": "Maksymalizuj",
+ "panel.color.both": "Oba",
+ "uv_editor.copy_selection": "Kopiuj zaznaczenie",
+ "uv_editor.paste_selection": "Wklej zaznaczenie",
"uv_editor.copy_paste_tool.place": "Place",
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/pt.json b/lang/pt.json
index 8bf3041e0..03cadd7c5 100644
--- a/lang/pt.json
+++ b/lang/pt.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Cancelar",
"action.slider_inflate": "Aumentar",
"action.slider_inflate.desc": "Aumentar cubos em todas as direções sem alterar o mapeamento.",
- "action.brush_mode": "Modo do pincel",
"action.slider_brush_size": "Tamanho",
"action.slider_brush_size.desc": "Tamanho do pincel (em pixels)",
"action.slider_brush_opacity": "Opacidade",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Última Versão",
"dialog.update.installed": "Versão Instalada",
"dialog.update.update": "Atualização",
- "action.brush_mode.brush": "Redondo",
- "action.brush_mode.noise": "Ruído",
"action.vertex_snap_mode.move": "Mover",
"action.vertex_snap_mode.scale": "Redimensionar",
"action.open_model_folder": "Abrir Pasta de Modelos",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Modifique o tamanho do pincel ao usar uma caneta",
"settings.brush_modifier.pressure": "Pressão",
"settings.brush_modifier.tilt": "Inclinar",
- "settings.class_export_version": "Versão de exportação de entidade modificada",
- "settings.class_export_version.desc": "A versão do formato para modelos de entidade modificados.",
"category.color": "Cor",
"action.import_theme": "Importar Tema",
"action.export_theme": "Exportar Tema",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Gerar paleta a partir de uma textura",
"action.sort_palette": "Classificar Paleta",
"action.sort_palette.desc": "Classifique todas as cores da paleta por cor e brilho",
- "action.clear_palette": "Limpar Paleta",
- "action.clear_palette.desc": "Remove todas as cores da paleta",
"action.timelapse": "Timelapse...",
"action.timelapse.desc": "Grave um timelapse do seu progresso de modelagem",
"action.add_keyframe": "Adicionar quadro-chave",
@@ -994,7 +987,7 @@
"format.skin.desc": "Edit player and entity skins",
"message.sketchfab.setup_guide": "Want to learn how to set up models in Sketchfab? Read %0",
"dialog.skin.title": "Create Skin",
- "dialog.skin.model": "Skin",
+ "dialog.skin.model": "Model",
"dialog.skin.texture": "Texture (Optional)",
"action.toggle_skin_layer": "Toggle Skin Layer",
"action.toggle_skin_layer.desc": "Toggle the hat and clothing layer of the skin model",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/ru.json b/lang/ru.json
index d91c88989..16282d943 100644
--- a/lang/ru.json
+++ b/lang/ru.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Отменить",
"action.slider_inflate": "Раздуть",
"action.slider_inflate.desc": "Раздуть кубы во все направления без изменений в UV",
- "action.brush_mode": "Режим кисти",
"action.slider_brush_size": "Размер",
"action.slider_brush_size.desc": "Размер кисти в пикселях",
"action.slider_brush_opacity": "Непрозрачность",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Новейшая версия",
"dialog.update.installed": "Установленная версия",
"dialog.update.update": "Обновление",
- "action.brush_mode.brush": "Кисть",
- "action.brush_mode.noise": "Шум",
"action.vertex_snap_mode.move": "Перемещение",
"action.vertex_snap_mode.scale": "Масштабирование",
"action.open_model_folder": "Открыть расположение модели",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Модифицировать размер кисти при использовании стилуса",
"settings.brush_modifier.pressure": "Давление",
"settings.brush_modifier.tilt": "Наклон",
- "settings.class_export_version": "Модифицированная версия экспорта сущностей",
- "settings.class_export_version.desc": "Версия формата для модифицированных моделей сущностей",
"category.color": "Цвет",
"action.import_theme": "Импорт темы",
"action.export_theme": "Экспорт темы",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Сгенерировать палитру из текстуры",
"action.sort_palette": "Сортировка палитры",
"action.sort_palette.desc": "Сортировать цвета на палитре по цвету и яркости",
- "action.clear_palette": "Очистка палитры",
- "action.clear_palette.desc": "Удалить все цвета из палитры",
"action.timelapse": "Временная шкала",
"action.timelapse.desc": "Записать процесс создания модели",
"action.add_keyframe": "Добавить ключевой кадр",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/sv.json b/lang/sv.json
index d2a39e1e3..de92bc4fa 100644
--- a/lang/sv.json
+++ b/lang/sv.json
@@ -280,7 +280,6 @@
"keybind.cancel": "Avbryt",
"action.slider_inflate": "Blåsa upp",
"action.slider_inflate.desc": "Blås upp kuber i alla riktningar utan att ändra UV.",
- "action.brush_mode": "Penselläge",
"action.slider_brush_size": "Storlek",
"action.slider_brush_size.desc": "Penselns radie i pixlar",
"action.slider_brush_opacity": "Opacitet",
@@ -541,8 +540,6 @@
"dialog.update.latest": "Senaste version",
"dialog.update.installed": "Installerad version",
"dialog.update.update": "Uppdatera",
- "action.brush_mode.brush": "Rund",
- "action.brush_mode.noise": "Brus",
"action.vertex_snap_mode.move": "Flytta",
"action.vertex_snap_mode.scale": "Skala",
"action.open_model_folder": "Öppna modellmapp",
@@ -918,7 +915,7 @@
"action.flip.desc": "Flip the selected cubes on the %0 axis",
"action.center": "Center %0",
"action.center.desc": "Center the selected cubes on the %0 axis",
- "action.bring_up_all_animations": "Bring Up All Animations",
+ "action.bring_up_all_animations": "Bring Up All Animators",
"panel.bone": "Ben",
"data.color": "Färg",
"generic.export": "Export",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "Modify the brush size when using a stylus",
"settings.brush_modifier.pressure": "Tryck",
"settings.brush_modifier.tilt": "Tilt",
- "settings.class_export_version": "Modded Entity Export Version",
- "settings.class_export_version.desc": "The format version for modded entity models",
"category.color": "Färg",
"action.import_theme": "Import Theme",
"action.export_theme": "Export Theme",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "Generate palette from a texture",
"action.sort_palette": "Sort Palette",
"action.sort_palette.desc": "Sort all colors on the palette by color and brightness",
- "action.clear_palette": "Clear Palette",
- "action.clear_palette.desc": "Remove all colors from the palette",
"action.timelapse": "Timelapse...",
"action.timelapse.desc": "Record a timelapse of your modeling process",
"action.add_keyframe": "Add Keyframe",
@@ -994,7 +987,7 @@
"format.skin.desc": "Edit player and entity skins",
"message.sketchfab.setup_guide": "Want to learn how to set up models in Sketchfab? Read %0",
"dialog.skin.title": "Create Skin",
- "dialog.skin.model": "Skin",
+ "dialog.skin.model": "Model",
"dialog.skin.texture": "Texture (Optional)",
"action.toggle_skin_layer": "Toggle Skin Layer",
"action.toggle_skin_layer.desc": "Toggle the hat and clothing layer of the skin model",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "Cut",
"uv_editor.copy_paste_tool.mirror_x": "Mirror X",
"uv_editor.copy_paste_tool.mirror_y": "Mirror Y",
- "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees"
+ "uv_editor.copy_paste_tool.rotate": "Rotate 90 Degrees",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/lang/zh.json b/lang/zh.json
index 7034904d8..c8c4f0761 100644
--- a/lang/zh.json
+++ b/lang/zh.json
@@ -1,7 +1,7 @@
{
"dialog.ok": "好的",
"dialog.cancel": "取消",
- "dialog.confirm": "确认",
+ "dialog.confirm": "Confirm",
"dialog.close": "关闭",
"dialog.import": "导入",
"dialog.save": "保存",
@@ -280,7 +280,6 @@
"keybind.cancel": "取消",
"action.slider_inflate": "缩放",
"action.slider_inflate.desc": "在不改变 UV 的情况下放大所有立方体",
- "action.brush_mode": "笔刷模式",
"action.slider_brush_size": "尺寸",
"action.slider_brush_size.desc": "笔刷的半径(以像素为单位)",
"action.slider_brush_opacity": "不透明度",
@@ -541,8 +540,6 @@
"dialog.update.latest": "最新版本",
"dialog.update.installed": "已安装的版本",
"dialog.update.update": "更新",
- "action.brush_mode.brush": "笔刷",
- "action.brush_mode.noise": "噪点",
"action.vertex_snap_mode.move": "移动",
"action.vertex_snap_mode.scale": "缩放",
"action.open_model_folder": "打开模型文件夹",
@@ -948,8 +945,6 @@
"settings.brush_size_modifier.desc": "使用手写笔时修改画笔的大小",
"settings.brush_modifier.pressure": "压感",
"settings.brush_modifier.tilt": "倾斜",
- "settings.class_export_version": "模组实体导出版本",
- "settings.class_export_version.desc": "模组实体格式对应的版本",
"category.color": "颜色",
"action.import_theme": "导入主题",
"action.export_theme": "导出主题",
@@ -969,8 +964,6 @@
"action.generate_palette.desc": "从材质纹理贴图生成调色板",
"action.sort_palette": "排列调色板",
"action.sort_palette.desc": "按颜色和亮度对调色板上所有的颜色进行排序",
- "action.clear_palette": "清除调色板",
- "action.clear_palette.desc": "从调色板中删除所有颜色",
"action.timelapse": "延时摄影……",
"action.timelapse.desc": "拍摄你建模过程的延时摄影",
"action.add_keyframe": "添加关键帧",
@@ -1087,5 +1080,34 @@
"uv_editor.copy_paste_tool.cut": "剪切",
"uv_editor.copy_paste_tool.mirror_x": "镜像 X 轴",
"uv_editor.copy_paste_tool.mirror_y": "镜像 Y 轴",
- "uv_editor.copy_paste_tool.rotate": "旋转90度"
+ "uv_editor.copy_paste_tool.rotate": "旋转90度",
+ "dialog.project.modded_entity_version": "Export Version",
+ "dialog.save_angle.position": "Camera Position",
+ "dialog.save_angle.target": "Focal Point",
+ "dialog.skin.pose": "Pose",
+ "layout.color.frame": "Window Frame",
+ "layout.color.frame.desc": "Border and title bar of the window",
+ "settings.large_grid_size": "Block Grid Size",
+ "settings.large_grid_size.desc": "Size of the block grid",
+ "action.load_plugin_from_url": "Load Plugin from URL",
+ "action.load_plugin_from_url.desc": "Load a plugin from a server by specifying the URL",
+ "action.cube_counter.desc": "Displays the current number of cubes and other statistics",
+ "action.unlock_everything": "Unlock Everything",
+ "action.unlock_everything.desc": "Unlock all groups and elements in the outliner.",
+ "action.load_palette": "Load Palette",
+ "action.load_palette.desc": "Load one of the built-in palette presets",
+ "action.toggle_locked": "Toggle Lock",
+ "action.toggle_locked.desc": "Toggle whether the selected elements are locked",
+ "action.apply_display_preset": "Apply Preset",
+ "action.apply_display_preset.desc": "Apply a default or custom display setting preset",
+ "action.apply_display_preset.here": "Apply To This Slot",
+ "action.apply_display_preset.everywhere": "Apply To All Slots",
+ "action.resolve_keyframe_expressions": "Resolve Keyframe",
+ "action.resolve_keyframe_expressions.desc": "Resolves the math expressions of the selected keyframes",
+ "action.fold_all_animations": "Fold All Animators",
+ "action.timeline_focus.used": "In Use",
+ "menu.palette.load.empty": "Blank",
+ "switches.lock": "Lock",
+ "camera_angle.isometric_right": "Isometric Right",
+ "camera_angle.isometric_left": "Isometric Left"
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 8628d1043..c50aeacb7 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Model editing and animation software",
- "version": "3.4.2",
+ "version": "3.5.0",
"license": "MIT",
"author": {
"name": "JannisX11",
@@ -73,6 +73,7 @@
}
},
"scripts": {
+ "dev": "electron .",
"dist": "electron-builder --publish=always",
"win64": "electron-builder -w --ia32 --publish=always",
"win32": "electron-builder -w --x64 --publish=always",
@@ -80,7 +81,7 @@
},
"devDependencies": {
"async": "^2.4.1",
- "electron": "8.0.2",
+ "electron": "8.2.1",
"electron-builder": "^21.2.0"
}
}
diff --git a/service_worker.js b/service_worker.js
new file mode 100644
index 000000000..780a31a07
--- /dev/null
+++ b/service_worker.js
@@ -0,0 +1,51 @@
+const cacheName = 'blockbench_3.2.0';
+const staticAssets = [
+ './',
+ './index.html',
+ './css/',
+ './js',
+ './lib',
+ './font',
+ './lang',
+ './assets',
+ './favicon.png',
+ './manifest.json',
+];
+
+self.addEventListener('install', async (event) => {
+ var cache = await caches.open(cacheName);
+ await cache.addAll(staticAssets);
+ return self.skipWaiting();
+})
+
+self.addEventListener('activate', (event) => {
+ self.clients.claim();
+})
+
+self.addEventListener('fetch', async (event) => {
+ var req = event.request;
+ var url = new URL(req.url);
+
+ if (url.origin == 'location.origin') {
+ event.respondWith(cacheFirst(req));
+ } else {
+ event.respondWith(networkAndCache(req));
+ }
+})
+
+async function cacheFirst(req) {
+ var cache = await caches.open(cacheName);
+ var cached = await cache.match(req);
+ return cached || fetch(req);
+}
+async function networkAndCache(req) {
+ var cache = await caches.open(cacheName);
+ try {
+ var fresh = await fetch(req);
+ await cache.put(req, fresh.clone());
+ return fresh;
+ } catch (err) {
+ var cached = await cache.match(req);
+ return cached;
+ }
+}
\ No newline at end of file