This commit is contained in:
JannisX11 2019-08-01 00:01:47 +02:00
parent fd3085b8fa
commit d5426e9998
41 changed files with 842 additions and 699 deletions

View File

@ -4,4 +4,4 @@ Blockbench is a free, modern model editor for Minecraft Java and Bedrock Edition
Blockbench features a modern and intuitive UI, plugin support and innovative features. It is the industry standard for creating custom 3D models for the Minecraft Marketplace.
Project and download page: [blockbench.net](https://www.blockbench.net)
![Interface](https://blockbench.net/wp-content/uploads/2018/10/crane.png)
![Interface](https://blockbench.net/wp-content/uploads/2019/07/interface_skidsteer.png)

View File

@ -27,7 +27,7 @@
<script>
if (typeof module === 'object') {window.module = module; module = undefined;}//jQuery Fix
const isApp = typeof require !== 'undefined';
const appVersion = '3.0.4';
const appVersion = '3.0.5';
</script>
<script src="lib/vue.min.js"></script>
<script src="lib/vue_sortable.js"></script>
@ -938,8 +938,8 @@
<div class="texture_res">{{ texture.error
? texture.getErrorMessage()
: (Format.single_texture
? (texture.res + ' x ' + texture.res/texture.ratio + 'px')
: (texture.ratio == 1? texture.res + 'px': (texture.res + 'px, ' + texture.frameCount+'f'))
? (texture.width + ' x ' + texture.height + 'px')
: (texture.ratio == 1? texture.width + 'px': (texture.width + 'px, ' + texture.frameCount+'f'))
)
}}</div>
</li>

View File

@ -23,7 +23,7 @@ class Animation {
Merge.number(this, data, 'length')
if (data.bones) {
for (var key in data.bones) {
var group = Outliner.root.findRecursive( isUUID(key) ? 'uuid' : 'name', key )
var group = Group.all.findInArray( isUUID(key) ? 'uuid' : 'name', key )
if (group) {
var ba = this.getBoneAnimator(group)
var kfs = data.bones[key]
@ -45,7 +45,7 @@ class Animation {
}
return this;
}
undoCopy(options) {
getUndoCopy(options) {
var scope = this;
var copy = {
uuid: this.uuid,
@ -69,7 +69,7 @@ class Animation {
}
var kfs_copy = copy.bones[uuid] = []
kfs.forEach(kf => {
kfs_copy.push(kf.undoCopy())
kfs_copy.push(kf.getUndoCopy())
})
}
}
@ -99,13 +99,40 @@ class Animation {
Animator.preview()
return this;
}
createUniqueName(arr) {
var scope = this;
var others = Animator.animations;
if (arr && arr.length) {
arr.forEach(g => {
others.safePush(g)
})
}
var name = this.name.replace(/\d+$/, '');
function check(n) {
for (var i = 0; i < others.length; i++) {
if (others[i] !== scope && others[i].name == n) return false;
}
return true;
}
if (check(this.name)) {
return this.name;
}
for (var num = 2; num < 8e3; num++) {
if (check(name+num)) {
scope.name = name+num;
return scope.name;
}
}
return false;
}
rename() {
var scope = this;
Blockbench.textPrompt('message.rename_animation', this.name, function(name) {
if (name && name !== scope.name) {
Undo.initEdit({animations: [scope]})
scope.name = name
Undo.finishEdit('rename animation')
Undo.initEdit({animations: [scope]});
scope.name = name;
scope.createUniqueName();
Undo.finishEdit('rename animation');
}
})
return this;
@ -193,7 +220,6 @@ class Animation {
}
}
Animation.prototype.menu = new Menu([
'rename',
{name: 'menu.animation.loop', icon: (a) => (a.loop?'check_box':'check_box_outline_blank'), click: function(animation) {
animation.loop = !animation.loop
}},
@ -203,7 +229,10 @@ class Animation {
{name: 'menu.animation.anim_time_update', icon: 'update', click: function(animation) {
animation.editUpdateVariable()
}},
'delete'
'_',
'duplicate',
'rename',
'delete',
/*
rename
Loop: checkbox
@ -220,7 +249,7 @@ class BoneAnimator {
this.animation = animation;
}
getGroup() {
this.group = Outliner.root.findRecursive('uuid', this.uuid)
this.group = Group.all.findInArray('uuid', this.uuid)
if (!this.group) {
console.log('no group found for '+this.uuid)
if (this.animation && this.animation.bones[this.uuid]) {
@ -429,7 +458,7 @@ class BoneAnimator {
}
}
class Keyframe {
constructor(data) {
constructor(data, uuid) {
this.type = 'keyframe'
this.channel = 'rotation'//, 'position', 'scale'
this.channel_index = 0;
@ -440,7 +469,7 @@ class Keyframe {
this.z = '0';
this.w = '0';
this.isQuaternion = false;
this.uuid = guid()
this.uuid = (uuid && isUUID(uuid)) ? uuid : guid();
if (typeof data === 'object') {
this.extend(data)
if (this.channel === 'scale' && data.x == undefined && data.y == undefined && data.z == undefined) {
@ -631,7 +660,7 @@ class Keyframe {
this.channel_index = Animator.channel_index.indexOf(this.channel)
return this;
}
undoCopy() {
getUndoCopy() {
var copy = {
channel: this.channel_index,
time: this.time,
@ -860,7 +889,7 @@ const Animator = {
//Bones
for (var bone_name in a.bones) {
var b = a.bones[bone_name]
var group = Outliner.root.findRecursive('name', bone_name)
var group = Group.all.findInArray('name', bone_name)
if (group) {
var ba = new BoneAnimator(group.uuid, animation);
animation.bones[group.uuid] = ba;
@ -912,7 +941,7 @@ const Animator = {
if (!channels[kf.channel]) {
channels[kf.channel] = {}
}
let timecode = trimFloatNumber(Math.round(kf.time*60)/60) + ''
let timecode = Math.clamp(trimFloatNumber(Math.round(kf.time*60)/60), 0) + '';
if (!timecode.includes('.')) {
timecode = timecode + '.0'
}

View File

@ -193,6 +193,47 @@ function updateNslideValues() {
}
}
}
function setProjectResolution(width, height, modify_uv) {
let old_res = {
x: Project.texture_width,
y: Project.texture_height
}
Project.texture_width = width;
Project.texture_height = height;
if (Project.texture_width / old_res.x != Project.texture_width / old_res.y) {
modify_uv = false;
}
if (modify_uv) {
var multiplier = [
Project.texture_width/entityMode.old_res.x,
Project.texture_height/entityMode.old_res.y
]
function shiftCube(cube, axis) {
if (Project.box_uv) {
obj.uv_offset[axis] *= multiplier[axis];
} else {
for (var face in cube.faces) {
var uv = cube.faces[face];
uv[axis] *= multiplier[axis];
uv[axis+2] *= multiplier[axis];
}
}
}
if (old_res.x != Project.texture_width && Math.areMultiples(old_res.x, Project.texture_width)) {
Cube.all.forEach(cube => shiftCube(cube, 0));
}
if (old_res.y != Project.texture_height && Math.areMultiples(old_res.x, Project.texture_width)) {
Cube.all.forEach(cube => shiftCube(cube, 1));
}
}
Canvas.updateAllUVs()
if (selected.length) {
main_uv.loadData()
}
}
//Selections
function updateSelection() {
@ -469,155 +510,6 @@ const TickUpdates = {
}
}
}
const Screencam = {
fullScreen(options, cb) {
setTimeout(function() {
currentwindow.capturePage(function(screenshot) {
var dataUrl = screenshot.toDataURL()
dataUrl = dataUrl.replace('data:image/png;base64,','')
Jimp.read(Buffer.from(dataUrl, 'base64')).then(function(image) {
if (options && options.width && options.height) {
image.contain(options.width, options.height)
}
image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
Screencam.returnScreenshot(dataUrl, cb)
})
});
})
}, 40)
},
returnScreenshot(dataUrl, cb) {
if (cb) {
cb(dataUrl)
} else if (isApp) {
var screenshot = nativeImage.createFromDataURL(dataUrl)
var img = new Image()
var is_gif = dataUrl.substr(5, 9) == 'image/gif'
img.src = dataUrl
var btns = [tl('dialog.cancel'), tl('dialog.save')]
if (!is_gif) {
btns.push(tl('message.screenshot.clipboard'))
}
Blockbench.showMessageBox({
translateKey: 'screenshot',
icon: img,
buttons: btns,
confirm: 1,
cancel: 0
}, function(result) {
if (result === 1) {
electron.dialog.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 => {})
})
} else if (result === 2) {
clipboard.writeImage(screenshot)
}
})
} else {
new Dialog({
title: tl('message.screenshot.right_click'),
id: 'screenie',
lines: ['<img src="'+dataUrl+'" width="600px" class="allow_default_menu"></img>'],
draggable: true,
singleButton: true
}).show()
}
},
cleanCanvas(options, cb) {
quad_previews.current.screenshot(options, cb)
},
createGif(options, cb) {
/*
var images = [];
var preview = quad_previews.current;
var interval = setInterval(function() {
var shot = preview.canvas.toDataURL()
images.push(shot);
if (images.length >= options.length/1000*options.fps) {
clearInterval(interval);
gifshot.createGIF({
images,
frameDuration: 10/options.fps,
progressCallback: cl,
text: 'BLOCKBENCH'
}, obj => {
Screencam.returnScreenshot(obj.image, cb);
})
}
}, 1000/options.fps)
//Does not support transparency
*/
if (typeof options !== 'object') {
options = {}
}
if (!options.length) {
options.length = 1000
}
var preview = quad_previews.current;
var interval = options.fps ? (1000/options.fps) : 100
var gif = new GIF({
repeat: options.repeat,
quality: options.quality,
transparent: 0x000000,
})
var frame_count = (options.length/interval)
if (options.turnspeed) {
preview.controls.autoRotate = true;
preview.controls.autoRotateSpeed = options.turnspeed;
}
gif.on('finished', blob => {
var reader = new FileReader()
reader.onload = () => {
if (!options.silent) {
Blockbench.setProgress(0)
Blockbench.setStatusBarText()
}
Screencam.returnScreenshot(reader.result, cb)
}
reader.readAsDataURL(blob)
})
if (!options.silent) {
Blockbench.setStatusBarText(tl('status_bar.recording_gif'))
gif.on('progress', Blockbench.setProgress)
}
var frames = 0;
var loop = setInterval(() => {
var img = new Image()
img.src = preview.canvas.toDataURL()
img.onload = () => {
gif.addFrame(img, {delay: interval})
}
Blockbench.setProgress(interval*frames/options.length)
frames++;
}, interval)
setTimeout(() => {
clearInterval(loop)
if (!options.silent) {
Blockbench.setStatusBarText(tl('status_bar.processing_gif'))
}
gif.render()
if (Animator.open && Timeline.playing) {
Timeline.pause()
}
if (options.turnspeed) {
preview.controls.autoRotate = false;
}
}, options.length)
}
}
const Clipbench = {
elements: [],
copy(event, cut) {
@ -732,6 +624,9 @@ const Clipbench = {
function iterate(obj, parent) {
if (obj.children) {
var copy = new Group(obj).addTo(parent).init()
if (Format.bone_rig) {
copy.createUniqueName();
}
if (obj.children && obj.children.length) {
obj.children.forEach((child) => {
iterate(child, copy)
@ -823,6 +718,5 @@ const Clipbench = {
}
const entityMode = {
old_res: {},
hardcodes: {"geometry.chicken":{"body":{"rotation":[90,0,0]}},"geometry.llama":{"chest1":{"rotation":[0,90,0]},"chest2":{"rotation":[0,90,0]},"body":{"rotation":[90,0,0]}},"geometry.cow":{"body":{"rotation":[90,0,0]}},"geometry.sheep.sheared":{"body":{"rotation":[90,0,0]}},"geometry.sheep":{"body":{"rotation":[90,0,0]}},"geometry.phantom":{"body":{"rotation":[0,0,0]},"wing0":{"rotation":[0,0,5.7]},"wingtip0":{"rotation":[0,0,5.7]},"wing1":{"rotation":[0,0,-5.7]},"wingtip1":{"rotation":[0,0,-5.7]},"head":{"rotation":[11.5,0,0]},"tail":{"rotation":[0,0,0]},"tailtip":{"rotation":[0,0,0]}},"geometry.pig":{"body":{"rotation":[90,0,0]}},"geometry.ocelot":{"body":{"rotation":[90,0,0]},"tail1":{"rotation":[90,0,0]},"tail2":{"rotation":[90,0,0]}},"geometry.cat":{"body":{"rotation":[90,0,0]},"tail1":{"rotation":[90,0,0]},"tail2":{"rotation":[90,0,0]}},"geometry.turtle":{"eggbelly":{"rotation":[90,0,0]},"body":{"rotation":[90,0,0]}},"geometry.villager.witch":{"hat2":{"rotation":[-3,0,1.5]},"hat3":{"rotation":[-6,0,3]},"hat4":{"rotation":[-12,0,6]}},"geometry.pufferfish.mid":{"spines_top_front":{"rotation":[45,0,0]},"spines_top_back":{"rotation":[-45,0,0]},"spines_bottom_front":{"rotation":[-45,0,0]},"spines_bottom_back":{"rotation":[45,0,0]},"spines_left_front":{"rotation":[0,45,0]},"spines_left_back":{"rotation":[0,-45,0]},"spines_right_front":{"rotation":[0,-45,0]},"spines_right_back":{"rotation":[0,45,0]}},"geometry.pufferfish.large":{"spines_top_front":{"rotation":[45,0,0]},"spines_top_back":{"rotation":[-45,0,0]},"spines_bottom_front":{"rotation":[-45,0,0]},"spines_bottom_back":{"rotation":[45,0,0]},"spines_left_front":{"rotation":[0,45,0]},"spines_left_back":{"rotation":[0,-45,0]},"spines_right_front":{"rotation":[0,-45,0]},"spines_right_back":{"rotation":[0,45,0]}},"geometry.tropicalfish_a":{"leftFin":{"rotation":[0,-35,0]},"rightFin":{"rotation":[0,35,0]}},"geometry.tropicalfish_b":{"leftFin":{"rotation":[0,-35,0]},"rightFin":{"rotation":[0,35,0]}}},
}

View File

@ -211,7 +211,7 @@ function changeImageEditor(texture, from_settings) {
var id = $('.dialog#image_editor option:selected').attr('id')
var path;
switch (id) {
case 'ps': path = 'C:\\Program Files\\Adobe\\Adobe Photoshop CC 2018\\Photoshop.exe'; break;
case 'ps': path = 'C:\\Program Files\\Adobe\\Adobe Photoshop CC 2019\\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;
}

View File

@ -69,6 +69,7 @@ const EditSession = {
}, result => {
showDialog('edit_sessions');
})
return;
}
EditSession.token = token;
@ -289,7 +290,7 @@ EditSession.Client = class {
this.conn.send(tag)
}
disconnect(e) {
Blockbench.dispatchEvent('user_leaves_session', {conn: this.conn})
Blockbench.dispatchEvent('user_leaves_session', this)
delete EditSession.peer.connections[this.conn.peer];
delete EditSession.clients[this.id];
EditSession.updateClientCount();
@ -313,7 +314,11 @@ const Chat = {
$('input#chat_input').val('')
}
if (!text) return;
Chat.processMessage({author: EditSession.username, text: text})
Chat.processMessage({
author: EditSession.username,
text: text,
sender: EditSession.peer.id
})
},
addMessage(message) {
if (!(message instanceof Chat.Message)) {
@ -330,7 +335,11 @@ const Chat = {
},
processMessage(data) {
if (!EditSession.hosting) {
EditSession.host.send({type: 'chat_input', data: data})
EditSession.host.send({
type: 'chat_input',
data,
sender: EditSession.peer.id
})
return;
}
var message = new Chat.Message(data)
@ -345,7 +354,8 @@ Chat.Message = class {
constructor(data) {
this.author = data.author||'';
this.author = this.author.substr(0, 64)
this.self = (this.author && this.author === EditSession.username);
this.sender = data.sender
this.self = data.sender == EditSession.peer.id;
this.text = data.text.substr(0, Chat.maxlength)||'';
this.html = this.text.replace(/</g, '&lt;').replace(/>/g, '&gt;');

View File

@ -993,7 +993,7 @@ const BARS = {
transformerMode: 'translate',
toolbar: Blockbench.isMobile ? 'element_origin' : 'main_tools',
alt_tool: 'rotate_tool',
modes: ['edit'],
modes: ['edit', 'animate'],
keybind: new Keybind({key: 80}),
})
new Tool({
@ -1106,8 +1106,6 @@ const BARS = {
keybind: new Keybind({key: 88, ctrl: true, shift: null}),
click: function (event) {Clipbench.copy(event, true)}
})
new Action({
id: 'rename',
icon: 'text_format',
@ -1162,6 +1160,39 @@ const BARS = {
})
new Action({
id: 'duplicate',
icon: 'content_copy',
category: 'edit',
condition: () => (Animator.selected && Modes.animate) || (Modes.edit && (selected.length || Group.selected)),
keybind: new Keybind({key: 68, ctrl: true}),
click: function () {
if (Modes.animate) {
if (Animator.selected && Prop.active_panel == 'animations') {
var copy = Animator.selected.getUndoCopy();
var animation = new Animation(copy);
animation.createUniqueName();
Animator.animations.splice(Animator.animations.indexOf(Animator.selected)+1, 0, animation)
animation.add(true).select();
}
} else if (Group.selected && (Group.selected.matchesSelection() || selected.length === 0)) {
var cubes_before = elements.length;
Undo.initEdit({outliner: true, elements: [], selection: true});
var g = Group.selected.duplicate();
g.select();
Undo.finishEdit('duplicate_group', {outliner: true, elements: elements.slice().slice(cubes_before), selection: true})
} else {
var added_elements = [];
Undo.initEdit({elements: added_elements, outliner: true, selection: true})
selected.forEach(function(obj, i) {
var copy = obj.duplicate();
added_elements.push(copy);
})
BarItems.move_tool.select();
Undo.finishEdit('duplicate')
}
}
})
//Move Cube Keys
@ -1499,10 +1530,13 @@ const BARS = {
children: [
'brush_mode',
'fill_mode',
'_',
'slider_brush_size',
'slider_brush_opacity',
'slider_brush_min_opacity',
'slider_brush_softness'
'slider_brush_softness',
'_',
'painting_grid',
]
})
Toolbars.vertex_snap = new Toolbar({

View File

@ -265,8 +265,8 @@ function setupInterface() {
try {
interface_data = JSON.parse(interface_data)
var old_data = Interface.data
Interface.data.left_bar = interface_data.left_bar
Interface.data.right_bar = interface_data.right_bar
if (interface_data.left_bar) Interface.data.left_bar = interface_data.left_bar;
if (interface_data.right_bar) Interface.data.right_bar = interface_data.right_bar;
for (key in Interface.Panels) {
if (!Interface.data.left_bar.includes(key) && !Interface.data.right_bar.includes(key)) {
if (old_data.left_bar.includes(key)) {

View File

@ -580,6 +580,7 @@ const MenuBar = {
]},
'_',
'toggle_wireframe',
'painting_grid',
'toggle_quad_view',
{name: 'menu.view.screenshot', id: 'screenshot', icon: 'camera_alt', children: [
'screenshot_model',

View File

@ -66,7 +66,7 @@ var codec = new Codec('project', {
if (Animator.animations.length) {
model.animations = [];
Animator.animations.forEach(a => {
model.animations.push(a.undoCopy({bone_names: true}))
model.animations.push(a.getUndoCopy({bone_names: true}))
})
}
@ -227,10 +227,6 @@ BARS.defineActions(function() {
}
})
if (BarItems.save_project.keybind.key == 83 && BarItems.save_project.keybind.ctrl && !BarItems.save_project.keybind.alt && !BarItems.save_project.keybind.shift) {
//Blockbench 3.0.2 update
BarItems.save_project.keybind.set({key: 83, ctrl: true, alt: true}).save(true)
}
new Action({
id: 'save_project_as',
icon: 'save',
@ -241,10 +237,6 @@ BARS.defineActions(function() {
codec.export()
}
})
if (BarItems.save_project_as.keybind.key == 83 && BarItems.save_project_as.keybind.ctrl && !BarItems.save_project_as.keybind.alt && BarItems.save_project_as.keybind.shift) {
//Blockbench 3.0.2 update
BarItems.save_project_as.keybind.set({key: 83, ctrl: true, alt: true, shift: true}).save(true)
}
})
})()

View File

@ -132,8 +132,8 @@ function parseGeometry(data) {
if (b.pivot) {
group.origin[0] *= -1
}
group.rotation.forEach(function(br, ri) {
group.rotation[ri] *= -1
group.rotation.forEach(function(br, axis) {
group.rotation[axis] *= -1
})
group.mirror_uv = b.mirror === true
@ -148,8 +148,8 @@ function parseGeometry(data) {
rotation: s.rotation,
origin: s.pivot
})
base_cube.rotation.forEach(function(br, ri) {
base_cube.rotation[ri] *= -1
base_cube.rotation.forEach(function(br, axis) {
if (axis != 2) base_cube.rotation[axis] *= -1
})
base_cube.origin[0] *= -1;
if (s.origin) {
@ -168,25 +168,28 @@ function parseGeometry(data) {
} else if (s.uv) {
Project.box_uv = false;
for (var key in base_cube.faces) {
var face = base_cube.faces[key]
if (s.uv[key]) {
base_cube.faces[key].extend({
face.extend({
uv: [
s.uv[key].uv[0] * Project.texture_width/16,
s.uv[key].uv[1] * Project.texture_height/16,
s.uv[key].uv[0] * (16/Project.texture_width),
s.uv[key].uv[1] * (16/Project.texture_height),
]
})
if (s.uv[key].uv_size) {
base_cube.faces[key].uv_size = [
s.uv[key].uv_size[0] * Project.texture_width/16,
s.uv[key].uv_size[1] * Project.texture_height/16,
face.uv_size = [
s.uv[key].uv_size[0] * (16/Project.texture_width),
s.uv[key].uv_size[1] * (16/Project.texture_height),
]
} else {
base_cube.autouv = 1;
base_cube.mapAutoUV();
}
if (key == 'up') {
face.uv = [face.uv[2], face.uv[3], face.uv[0], face.uv[1]]
}
} else {
base_cube.faces[key].texture = null;
face.texture = null;
}
}
@ -238,6 +241,7 @@ function parseGeometry(data) {
if (isApp && Project.geometry_name) {
findEntityTexture(Project.geometry_name)
}
updateSelection()
EditSession.initNewModel()
}
@ -291,8 +295,8 @@ var codec = new Codec('bedrock', {
bone.pivot[0] *= -1
if (!g.rotation.allEqual(0)) {
bone.rotation = g.rotation.slice()
bone.rotation.forEach(function(br, ri) {
bone.rotation[ri] *= -1
bone.rotation.forEach(function(br, axis) {
bone.rotation[axis] *= -1
})
}
if (g.reset) {
@ -326,8 +330,8 @@ var codec = new Codec('bedrock', {
cube.pivot = obj.origin.slice();
cube.pivot[0] *= -1
cube.rotation = obj.rotation.slice();
cube.rotation.forEach(function(br, ri) {
cube.rotation[ri] *= -1
cube.rotation.forEach(function(br, axis) {
if (axis != 2) cube.rotation[axis] *= -1
})
}
@ -351,6 +355,12 @@ var codec = new Codec('bedrock', {
face.uv_size[1] * entitymodel.description.texture_height/16,
]
});
if (key == 'up') {
cube.uv[key].uv[0] += cube.uv[key].uv_size[0];
cube.uv[key].uv[1] += cube.uv[key].uv_size[1];
cube.uv[key].uv_size[0] *= -1;
cube.uv[key].uv_size[1] *= -1;
}
}
}
}

View File

@ -112,6 +112,7 @@ function parseGeometry(data) {
if (isApp && Project.geometry_name) {
findEntityTexture(Project.geometry_name)
}
updateSelection()
EditSession.initNewModel()
}

View File

@ -156,24 +156,23 @@ class ModelFormat {
Cube.all.forEach(function(s, i) {
//Push elements into 3x3 block box
[0, 1, 2].forEach(function(ax) {
var overlap = s.to[ax] - 32
var overlap = s.to[ax] + s.inflate - 32
if (overlap > 0) {
//If positive site overlaps
s.from[ax] -= overlap
s.to[ax] -= overlap
overlap = 16 + s.from[ax]
if (overlap < 0) {
s.from[ax] = -16
if (16 + s.from[ax] - s.inflate < 0) {
s.from[ax] = -16 + s.inflate
}
} else {
overlap = s.from[ax] + 16
overlap = s.from[ax] - s.inflate + 16
if (overlap < 0) {
s.from[ax] -= overlap
s.to[ax] -= overlap
if (s.to[ax] > 32) {
s.to[ax] = 32
if (s.to[ax] + s.inflate > 32) {
s.to[ax] = 32 - s.inflate
}
}
}
@ -1015,10 +1014,6 @@ BARS.defineActions(function() {
}
}
})
if (BarItems.export_over.keybind.key == 69 && BarItems.export_over.keybind.ctrl) {
//Blockbench 3.0.2 update
BarItems.export_over.keybind.set({key: 83, ctrl: true}).save(true)
}
if (!isApp) {
new Action({
id: 'export_asset_archive',

View File

@ -33,10 +33,11 @@ var codec = new Codec('java_block', {
if (s.shade === false) {
element.shade = false
}
if (!s.rotation.equals([0, 0, 0])) {
if (!s.rotation.allEqual(0) || !s.origin.allEqual(8)) {
var axis = s.rotationAxis()||'y';
element.rotation = new oneLiner({
angle: s.rotation[getAxisNumber(s.rotationAxis())],
axis: s.rotationAxis(),
angle: s.rotation[getAxisNumber(axis)],
axis,
origin: s.origin
})
}
@ -98,12 +99,12 @@ var codec = new Codec('java_block', {
function inVd(n) {
return n > 32 || n < -16
}
if (inVd(s.from[0]) ||
inVd(s.from[1]) ||
inVd(s.from[2]) ||
inVd(s.to[0]) ||
inVd(s.to[1]) ||
inVd(s.to[2])
if (inVd(element.from[0]) ||
inVd(element.from[1]) ||
inVd(element.from[2]) ||
inVd(element.to[0]) ||
inVd(element.to[1]) ||
inVd(element.to[2])
) {
largerCubesNr++;
}

View File

@ -48,106 +48,100 @@ var codec = new Codec('obj', {
var nbNormals = 0;
var geometry = mesh.geometry;
var element = Outliner.root.findRecursive('uuid', mesh.name)
var element = elements.findInArray('uuid', mesh.name)
if (element === undefined) return;
if (!element) return;
if (element.export === false) return;
if ( geometry instanceof THREE.Geometry ) {
output += 'o ' + element.name + '\n';
output += 'o ' + element.name + '\n';
var vertices = geometry.vertices;
var vertices = geometry.vertices;
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
var vertex = vertices[ i ].clone();
vertex.applyMatrix4( mesh.matrixWorld );
var vertex = vertices[ i ].clone();
vertex.applyMatrix4( mesh.matrixWorld );
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
nbVertex ++;
}
// uvs
var faces = geometry.faces;
var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
var hasVertexUvs = faces.length === faceVertexUvs.length;
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
nbVertex ++;
}
// uvs
var faces = geometry.faces;
var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
var hasVertexUvs = faces.length === faceVertexUvs.length;
if ( hasVertexUvs ) {
if ( hasVertexUvs ) {
for ( var i = 0, l = faceVertexUvs.length; i < l; i ++ ) {
for ( var i = 0, l = faceVertexUvs.length; i < l; i ++ ) {
var vertexUvs = faceVertexUvs[ i ];
var vertexUvs = faceVertexUvs[ i ];
for ( var j = 0, jl = vertexUvs.length; j < jl; j ++ ) {
for ( var j = 0, jl = vertexUvs.length; j < jl; j ++ ) {
var uv = vertexUvs[ j ];
output += 'vt ' + uv.x + ' ' + uv.y + '\n';
nbVertexUvs ++;
}
var uv = vertexUvs[ j ];
output += 'vt ' + uv.x + ' ' + uv.y + '\n';
nbVertexUvs ++;
}
}
}
// normals
// normals
var normalMatrixWorld = new THREE.Matrix3();
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
var normalMatrixWorld = new THREE.Matrix3();
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
for ( var i = 0, l = faces.length; i < l; i ++ ) {
for ( var i = 0, l = faces.length; i < l; i ++ ) {
var face = faces[ i ];
var vertexNormals = face.vertexNormals;
var face = faces[ i ];
var vertexNormals = face.vertexNormals;
if ( vertexNormals.length === 3 ) {
if ( vertexNormals.length === 3 ) {
for ( var j = 0, jl = vertexNormals.length; j < jl; j ++ ) {
for ( var j = 0, jl = vertexNormals.length; j < jl; j ++ ) {
var normal = vertexNormals[ j ].clone();
normal.applyMatrix3( normalMatrixWorld );
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
nbNormals ++;
}
} else {
var normal = face.normal.clone();
var normal = vertexNormals[ j ].clone();
normal.applyMatrix3( normalMatrixWorld );
for ( var j = 0; j < 3; j ++ ) {
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
nbNormals ++;
}
nbNormals ++;
}
} else {
var normal = face.normal.clone();
normal.applyMatrix3( normalMatrixWorld );
for ( var j = 0; j < 3; j ++ ) {
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
nbNormals ++;
}
}
// material
for (var face in element.faces) {
var tex = element.faces[face].getTexture()
if (tex && tex.uuid && !materials[tex.id]) {
materials[tex.id] = tex
}
}
// material
for (var face in element.faces) {
var tex = element.faces[face].getTexture()
if (tex && tex.uuid && !materials[tex.id]) {
materials[tex.id] = tex
}
}
for ( var i = 0, j = 1, l = faces.length; i < l; i ++, j += 3 ) {
for ( var i = 0, j = 1, l = faces.length; i < l; i ++, j += 3 ) {
var f_mat = getMtlFace(element, i)
if (f_mat) {
var f_mat = getMtlFace(element, i)
if (f_mat) {
var face = faces[ i ];
if (i % 2 === 0) {
output += f_mat
}
output += 'f ';
output += ( indexVertex + face.a + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ) + ' ';
output += ( indexVertex + face.b + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 1 ) : '' ) + '/' + ( indexNormals + j + 1 ) + ' ';
output += ( indexVertex + face.c + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 2 ) : '' ) + '/' + ( indexNormals + j + 2 ) + '\n';
var face = faces[ i ];
if (i % 2 === 0) {
output += f_mat
}
output += 'f ';
output += ( indexVertex + face.a + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j ) : '' ) + '/' + ( indexNormals + j ) + ' ';
output += ( indexVertex + face.b + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 1 ) : '' ) + '/' + ( indexNormals + j + 1 ) + ' ';
output += ( indexVertex + face.c + 1 ) + '/' + ( hasVertexUvs ? ( indexVertexUvs + j + 2 ) : '' ) + '/' + ( indexNormals + j + 2 ) + '\n';
}
} else {
console.warn( 'THREE.OBJExporter.parseMesh(): geometry type unsupported', mesh );
// TODO: Support only BufferGeometry and use setFromObject()
}
// update index

View File

@ -193,7 +193,7 @@ class Cube extends NonGroup {
if (!this.parent || (this.parent === 'root' && Outliner.root.indexOf(this) === -1)) {
this.addTo('root')
}
if (this.visibility && (!this.mesh || !scene.children.includes(this.mesh))) {
if (this.visibility && (!this.mesh || !this.mesh.parent)) {
Canvas.addCube(this)
}
TickUpdates.outliner = true;
@ -474,12 +474,7 @@ class Cube extends NonGroup {
shift.sub(dq)
shift.applyQuaternion(q.inverse())
this.from[0] += shift.x;
this.from[1] += shift.y;
this.from[2] += shift.z;
this.to[0] += shift.x;
this.to[1] += shift.y;
this.to[2] += shift.z;
this.move(shift)
this.origin = origin.slice();
@ -538,7 +533,7 @@ class Cube extends NonGroup {
}
mapAutoUV() {
if (Blockbench.box_uv) return;
var scope = this
var scope = this;
if (scope.autouv === 2) {
//Relative UV
function gt(n) {
@ -547,6 +542,7 @@ class Cube extends NonGroup {
var all_faces = ['north', 'south', 'west', 'east', 'up', 'down']
all_faces.forEach(function(side) {
var uv = scope.faces[side].uv.slice()
var texture = scope.faces[side]
switch (side) {
case 'north':
uv = [
@ -597,9 +593,11 @@ class Cube extends NonGroup {
];
break;
}
var fr_u = 16 / Project.texture_width;
var fr_v = 16 / Project.texture_height;
uv.forEach(function(s, uvi) {
s *= (uvi%2 ? fr_v : fr_u);
uv[uvi] = limitNumber(s, 0, 16)
uv[uvi] *= 16 / (uvi%2 ? Project.texture_height : Project.texture_width)
})
scope.faces[side].uv = uv
})
@ -615,6 +613,8 @@ class Cube extends NonGroup {
if (rot === 90 || rot === 270) {
size.reverse()
}
size[0] *= 16/Project.texture_width;
size[1] *= 16/Project.texture_height;
//Limit Input to 16
size.forEach(function(s) {
if (s > 16) {
@ -652,12 +652,19 @@ class Cube extends NonGroup {
Canvas.updateUV(scope)
}
}
move(val, axis, absolute, move_origin) {
move(val, axis, absolute, move_origin, no_update) {
if (val instanceof THREE.Vector3) {
return this.move(val.x, 0, absolute, move_origin, true)
&& this.move(val.y, 1, absolute, move_origin, true)
&& this.move(val.z, 2, absolute, move_origin, true);
}
var size = this.size(axis)
if (!absolute) {
val = val + this.from[axis]
}
val = limitToBox(limitToBox(val) + size) - size
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
@ -684,11 +691,12 @@ class Cube extends NonGroup {
if (Blockbench.globalMovement && move_origin) {
this.origin[axis] += val
}
this.mapAutoUV()
Canvas.adaptObjectPosition(this);
TickUpdates.selection = true;
return this;
if (!no_update) {
this.mapAutoUV()
Canvas.adaptObjectPosition(this);
TickUpdates.selection = true;
}
return in_box;
}
scale(val, axis, negative, absolute, allow_negative) {
if (absolute) {
@ -700,14 +708,14 @@ class Cube extends NonGroup {
val = Math.ceil(val);
}
if (!negative) {
var pos = limitToBox(val + this.from[axis] + before);
var pos = limitToBox(val + this.from[axis] + before, this.inflate);
if (pos >= this.from[axis] || settings.negative_size.value || allow_negative) {
this.to[axis] = pos;
} else {
this.to[axis] = this.from[axis];
}
} else {
var pos = limitToBox(val + this.to[axis] - before);
var pos = limitToBox(val + this.to[axis] - before, this.inflate);
if (pos <= this.to[axis] || settings.negative_size.value || allow_negative) {
this.from[axis] = pos;
} else {

View File

@ -255,7 +255,7 @@ class OutlinerElement {
others.safePush(g)
})
}
var name = this.name.replace(/\d+$/, '');
var name = this.name.replace(/\d+$/, '').replace(/\s+/g, '_');
function check(n) {
for (var i = 0; i < others.length; i++) {
if (others[i] !== scope && others[i].name == n) return false;
@ -265,7 +265,7 @@ class OutlinerElement {
if (check(this.name)) {
return this.name;
}
for (var num = 2; num < 2e3; num++) {
for (var num = 2; num < 8e3; num++) {
if (check(name+num)) {
scope.name = name+num;
return scope.name;
@ -440,7 +440,8 @@ class NonGroup extends OutlinerElement {
//Normal
} else {
unselectAll()
selected.forEachReverse(obj => obj.unselect())
if (Group.selected) Group.selected.unselect()
scope.selectLow()
just_selected.push(scope)
scope.showInOutliner()
@ -950,31 +951,6 @@ BARS.defineActions(function() {
}
})
new Action({
id: 'duplicate',
icon: 'content_copy',
category: 'edit',
condition: () => (Modes.edit && (selected.length || Group.selected)),
keybind: new Keybind({key: 68, ctrl: true}),
click: function () {
if (Group.selected && (Group.selected.matchesSelection() || selected.length === 0)) {
var cubes_before = elements.length;
Undo.initEdit({outliner: true, elements: [], selection: true});
var g = Group.selected.duplicate();
g.select();
Undo.finishEdit('duplicate_group', {outliner: true, elements: elements.slice().slice(cubes_before), selection: true})
} else {
var added_elements = [];
Undo.initEdit({elements: added_elements, outliner: true, selection: true})
selected.forEach(function(obj, i) {
var copy = obj.duplicate();
added_elements.push(copy);
})
BarItems.move_tool.select();
Undo.finishEdit('duplicate')
}
}
})
new Action({
id: 'sort_outliner',
icon: 'sort_by_alpha',

View File

@ -170,8 +170,8 @@ const Painter = {
getCanvas(texture) {
var c = document.createElement('canvas')
var ctx = c.getContext('2d');
c.width = texture.res;
c.height = texture.img.naturalHeight;
c.width = texture.width;
c.height = texture.height;
ctx.drawImage(texture.img, 0, 0)
return c;
},
@ -856,7 +856,6 @@ const Painter = {
}
//Drawing
cl(templates.length)
templates.forEach(function(t) {
let obj = t.obj
@ -1052,6 +1051,23 @@ BARS.defineActions(function() {
}
})
new Action({
id: 'painting_grid',
name: tl('settings.painting_grid'),
description: tl('settings.painting_grid.desc'),
icon: 'check_box',
category: 'view',
condition: () => Modes.paint,
linked_setting: 'painting_grid',
click: function () {
BarItems.painting_grid.toggleLinkedSetting()
Cube.all.forEach(cube => {
Canvas.buildGridBox(cube)
})
}
})
new NumSlider({
id: 'slider_brush_size',
condition: () => (Toolbox && ['brush_tool', 'eraser'].includes(Toolbox.selected.id)),

View File

@ -81,6 +81,10 @@ const Canvas = {
objects.push(s)
}
})
for (var uuid in Canvas.meshes) {
var mesh = Canvas.meshes[uuid];
objects.safePush(mesh);
}
objects.forEach(function(s) {
if (s.parent) {
s.parent.remove(s)
@ -157,13 +161,17 @@ const Canvas = {
}
})
},
updateRenderSides() {
getRenderSide() {
var side = Format.id === 'java_block' ? 0 : 2;
if (display_mode) {
if (['thirdperson_righthand', 'thirdperson_lefthand', 'head'].includes(display_slot)) {
side = 2;
}
}
return side;
},
updateRenderSides() {
var side = Canvas.getRenderSide();
textures.forEach(function(t) {
var mat = Canvas.materials[t.uuid]
if (mat) {
@ -173,7 +181,6 @@ const Canvas = {
emptyMaterials.forEach(function(mat) {
mat.side = side
})
return side;
},
//Selection updaters
updateSelected(arr) {
@ -324,6 +331,7 @@ const Canvas = {
},
//Object handlers
addCube(obj) {
//This does NOT remove old cubes
var mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1))
Canvas.adaptObjectFaces(obj, mesh)
@ -661,8 +669,8 @@ const Canvas = {
}
if (true) {
var tex = cube.faces.north.getTexture()
var width = tex ? tex.res : 16
var height = tex ? tex.res / tex.ratio : 16
var width = tex ? tex.width : 16
var height = tex ? tex.height : 16
size = [
Math.abs(width/16 * cube.faces.north.uv_size[cube.faces.north.rotation%180 ? 1 : 0]),
Math.abs(height/16 * cube.faces.north.uv_size[cube.faces.north.rotation%180 ? 0 : 1]),

View File

@ -1442,7 +1442,7 @@ enterDisplaySettings = function() { //Enterung Display Setting Mode, changes th
display_area.updateMatrixWorld()
display_base.updateMatrixWorld()
DisplayMode.centerTransformer()
Transformer.center()
if (outlines.children.length) {
outlines.children.length = 0
Canvas.updateAllPositions()
@ -1493,22 +1493,6 @@ function resetDisplayBase() {
display_base.scale.z = 1;
}
DisplayMode.centerTransformer = function() {
display_scene.add(Transformer)
Transformer.attach(display_base)
display_base.getWorldPosition(Transformer.position)
if (Toolbox.selected.transformerMode === 'translate') {
Transformer.rotation.copy(display_area.rotation)
} else if (Toolbox.selected.transformerMode === 'scale') {
var q = display_base.getWorldQuaternion(new THREE.Quaternion())
Transformer.rotation.setFromQuaternion(q)
} else {
Transformer.rotation.set(0, 0, 0)
}
Transformer.update()
}
DisplayMode.updateDisplayBase = function(slot) {
if (!slot) slot = display[display_slot]
@ -1524,7 +1508,7 @@ DisplayMode.updateDisplayBase = function(slot) {
display_base.scale.y = (slot.scale[1]||0.001) * (slot.mirror[1] ? -1 : 1);
display_base.scale.z = (slot.scale[2]||0.001) * (slot.mirror[2] ? -1 : 1);
DisplayMode.centerTransformer()
Transformer.center()
}
@ -1589,13 +1573,13 @@ var setDisplayArea = DisplayMode.setBase = function(x, y, z, rx, ry, rz, sx, sy,
display_area.updateMatrixWorld()
DisplayMode.centerTransformer()
Transformer.center()
}
DisplayMode.groundAnimation = function() {
display_area.rotation.y += 0.015
ground_timer += 1
display_area.position.y = 13.5 + Math.sin(Math.PI * (ground_timer / 100)) * Math.PI/2
DisplayMode.centerTransformer()
Transformer.center()
if (ground_timer === 200) ground_timer = 0;
}

View File

@ -144,7 +144,7 @@ class Preview {
var intersect = intersects[0].object
if (intersect.isElement) {
this.controls.hasMoved = true
var obj = Outliner.root.findRecursive('uuid', intersects[0].object.name)
var obj = elements.findInArray('uuid', intersects[0].object.name)
switch (Math.floor( intersects[0].faceIndex / 2 )) {
case 5: var face = 'north'; break;
case 0: var face = 'east'; break;
@ -322,7 +322,11 @@ class Preview {
main_uv.setFace(data.face, false)
}
Blockbench.dispatchEvent( 'canvas_select', data )
if (Animator.open || (!Format.rotate_cubes && Format.bone_rig && ['rotate_tool', 'pivot_tool'].includes(Toolbox.selected.id))) {
if (Format.bone_rig && (
Animator.open ||
(!Format.rotate_cubes && ['rotate_tool', 'pivot_tool'].includes(Toolbox.selected.id)) ||
event.shiftKey
)) {
if (data.cube.parent.type === 'group') {
data.cube.parent.select()
}
@ -793,6 +797,159 @@ function openQuadView() {
updateInterface()
}
const Screencam = {
fullScreen(options, cb) {
setTimeout(function() {
currentwindow.capturePage(function(screenshot) {
var dataUrl = screenshot.toDataURL()
dataUrl = dataUrl.replace('data:image/png;base64,','')
Jimp.read(Buffer.from(dataUrl, 'base64')).then(function(image) {
if (options && options.width && options.height) {
image.contain(options.width, options.height)
}
image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
Screencam.returnScreenshot(dataUrl, cb)
})
});
})
}, 40)
},
returnScreenshot(dataUrl, cb) {
if (cb) {
cb(dataUrl)
} else if (isApp) {
var screenshot = nativeImage.createFromDataURL(dataUrl)
var img = new Image()
var is_gif = dataUrl.substr(5, 9) == 'image/gif'
img.src = dataUrl
var btns = [tl('dialog.cancel'), tl('dialog.save')]
if (!is_gif) {
btns.push(tl('message.screenshot.clipboard'))
}
Blockbench.showMessageBox({
translateKey: 'screenshot',
icon: img,
buttons: btns,
confirm: 1,
cancel: 0
}, function(result) {
if (result === 1) {
electron.dialog.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 => {})
})
} else if (result === 2) {
clipboard.writeImage(screenshot)
}
})
} else {
new Dialog({
title: tl('message.screenshot.right_click'),
id: 'screenie',
lines: ['<img src="'+dataUrl+'" width="600px" class="allow_default_menu"></img>'],
draggable: true,
singleButton: true
}).show()
}
},
cleanCanvas(options, cb) {
quad_previews.current.screenshot(options, cb)
},
createGif(options, cb) {
if (typeof options !== 'object') {
options = {}
}
/*
var images = [];
var preview = quad_previews.current;
var interval = setInterval(function() {
var shot = preview.canvas.toDataURL()
images.push(shot);
if (images.length >= options.length/1000*options.fps) {
clearInterval(interval);
gifshot.createGIF({
images,
frameDuration: 10/options.fps,
progressCallback: cl,
text: 'BLOCKBENCH'
}, obj => {
Screencam.returnScreenshot(obj.image, cb);
})
}
}, 1000/options.fps)
//Does not support transparency
*/
if (!options.length) {
options.length = 1000;
}
var preview = quad_previews.current;
var interval = options.fps ? (1000/options.fps) : 100;
var gif = new GIF({
repeat: options.repeat,
quality: options.quality,
transparent: 0x000000,
});
var frame_count = (options.length/interval);
if (options.turnspeed) {
preview.controls.autoRotate = true;
preview.controls.autoRotateSpeed = options.turnspeed;
}
gif.on('finished', blob => {
var reader = new FileReader();
reader.onload = () => {
if (!options.silent) {
Blockbench.setProgress(0);
Blockbench.setStatusBarText();
}
Screencam.returnScreenshot(reader.result, cb);
}
reader.readAsDataURL(blob);
});
if (!options.silent) {
Blockbench.setStatusBarText(tl('status_bar.recording_gif'));
gif.on('progress', Blockbench.setProgress);
}
var frames = 0;
var loop = setInterval(() => {
frames++;
var last_frame = frames >= options.length / interval;
if (last_frame) {
clearInterval(loop)
if (!options.silent) {
Blockbench.setStatusBarText(tl('status_bar.processing_gif'))
}
if (Animator.open && Timeline.playing) {
Timeline.pause();
}
if (options.turnspeed) {
preview.controls.autoRotate = false;
}
}
var img = new Image();
img.src = preview.canvas.toDataURL();
img.onload = () => {
gif.addFrame(img, {delay: interval});
if (last_frame) {
gif.render();
}
}
Blockbench.setProgress(interval*frames/options.length);
}, interval)
}
}
//Init/Update
function initCanvas() {
@ -1187,7 +1344,7 @@ BARS.defineActions(function() {
form: {
length: {label: 'dialog.create_gif.length', type: 'number', value: 10, step: 0.25},
fps: {label: 'dialog.create_gif.fps', type: 'number', value: 10},
quality:{label: 'dialog.create_gif.compression', type: 'number', value: 4},
quality:{label: 'dialog.create_gif.compression', type: 'number', value: 20, min: 1, max: 80},
turn: {label: 'dialog.create_gif.turn', type: 'number', value: 0, min: -10, max: 10},
play: {label: 'dialog.create_gif.play', type: 'checkbox', condition: Animator.open},
},

View File

@ -698,10 +698,8 @@
this.update = function (object) {
var scope = Transformer;
if (!object && Modes.id === 'display' && Toolbox.selected.transformerMode === 'rotate') {
object = display_area;
} else if (Modes.id == 'animate' && Group.selected && Group.selected.parent instanceof Group) {
object = Group.selected.parent.mesh
if (!object) {
object = this.rotation_ref;
}
if (scope.elements.length == 0) {
this.detach()
@ -726,9 +724,16 @@
if (object) {
worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( object.matrixWorld ) );
_gizmo[ _mode ].update( worldRotation, eye );
if (Toolbox.selected.transformerMode === 'rotate') {
_gizmo[ _mode ].update( worldRotation, eye );
this.rotation.set(0, 0, 0);
} else {
object.getWorldQuaternion(this.rotation)
}
} else {
worldRotation.set(0, 0, 0);
this.rotation.set(0, 0, 0);
_gizmo[ _mode ].update( new THREE.Euler(), eye );
}
_gizmo[ _mode ].highlight( scope.axis );
@ -786,7 +791,7 @@
this.updateSelection = function() {
this.elements.empty()
if (Modes.edit) {
if (Modes.edit || Toolbox.selected.id == 'pivot_tool') {
if (selected.length) {
selected.forEach(element => {
if (
@ -808,7 +813,8 @@
return this;
}
this.center = function() {
if (Modes.edit) {
delete Transformer.rotation_ref;
if (Modes.edit || Toolbox.selected.id == 'pivot_tool') {
if (Transformer.visible) {
var rotation_tool = Toolbox.selected.id === 'rotate_tool' || Toolbox.selected.id === 'pivot_tool'
var rotation_object = getRotationObject()
@ -827,7 +833,7 @@
return;
}
this.rotation_object = rotation_object;
if (Format.bone_rig) {
if (Format.bone_rig && !Modes.animate) {
Canvas.updateAllBones()
}
//Center
@ -840,32 +846,41 @@
}
//Rotation
Transformer.rotation.set(0, 0, 0);
if (Toolbox.selected.transformerMode !== 'rotate') {
if (Toolbox.selected.id == 'pivot_tool') {
if (rotation_tool) {
Transformer.rotation_ref = rotation_object.mesh.parent;
if (rotation_object.parent instanceof Group) {
rotation_object.parent.mesh.getWorldQuaternion(this.rotation)
}
} else if (!Blockbench.globalMovement && Cube.selected[0] && Cube.selected[0].mesh) {
Cube.selected[0].mesh.getWorldQuaternion(this.rotation)
}
} else if (Group.selected) {
Transformer.rotation_ref = rotation_object.mesh;
} else if (!Blockbench.globalMovement && Cube.selected[0] && Cube.selected[0].mesh) {
Transformer.rotation_ref = Cube.selected[0].mesh;
}
}
} else if (Modes.display) {
this.attach(display_base);
DisplayMode.centerTransformer();
display_scene.add(Transformer)
Transformer.attach(display_base)
display_base.getWorldPosition(Transformer.position)
if (Toolbox.selected.transformerMode === 'translate') {
Transformer.rotation_ref = display_area;
} else if (Toolbox.selected.transformerMode === 'scale') {
Transformer.rotation_ref = display_base;
}
Transformer.update()
} else if (Modes.animate && Group.selected) {
this.attach(Group.selected);
Group.selected.mesh.getWorldPosition(this.position);
if (Toolbox.selected.transformerMode == 'rotate') {
this.rotation.set(0, 0, 0);
if (Toolbox.selected.id == 'resize_tool') {
Transformer.rotation_ref = Group.selected.mesh;
} else {
Transformer.rotation_ref = Group.selected.mesh.parent;
}
Transformer.update()
return;
}
}
@ -937,7 +952,7 @@
function beforeFirstChange(event) {
if (scope.hasChanged) return;
if (Modes.edit) {
if (Modes.edit || Toolbox.selected.id == 'pivot_tool') {
if (Toolbox.selected.id === 'resize_tool') {
var axisnr = getAxisNumber(scope.axis.toLowerCase().replace('n', ''))
@ -1001,37 +1016,25 @@
if (Toolbox.selected.transformerMode !== 'rotate') {
point.sub( offset );
}
if (Toolbox.selected.transformerMode === 'rotate') {
point.removeEuler(worldRotation)
} else {
point.sub( worldPosition );
point.removeEuler(worldRotation)
var rotations = [
Math.atan2( point.z, point.y ),
Math.atan2( point.x, point.z ),
Math.atan2( point.y, point.x )
]
var angle = Math.radToDeg( rotations[axisNumber] )
} else if ((!Blockbench.globalMovement || !Modes.edit) && scope.elements.length) {
var rotation = new THREE.Quaternion()
var i = 0;
while (scope.elements[i]) {
if (scope.elements[i].mesh) {
scope.elements[0].mesh.getWorldQuaternion(rotation)
i = Infinity;
}
i++;
}
point.applyQuaternion(rotation.inverse())
}
if (Modes.edit) {
if (Modes.edit || Toolbox.selected.id == 'pivot_tool') {
if (Toolbox.selected.id === 'move_tool') {
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor;
if (previousValue === undefined) {
previousValue = point[axis]
@ -1046,10 +1049,10 @@
selected.forEach(function(obj) {
if (obj.movable && obj.scalable) {
overlapping = overlapping || (
obj.to[axisNumber] + difference > 32 ||
obj.to[axisNumber] + difference < -16 ||
obj.from[axisNumber] + difference > 32 ||
obj.from[axisNumber] + difference < -16
obj.to[axisNumber] + difference + obj.inflate > 32 ||
obj.to[axisNumber] + difference + obj.inflate < -16 ||
obj.from[axisNumber] + difference - obj.inflate > 32 ||
obj.from[axisNumber] + difference - obj.inflate < -16
)
}
})
@ -1074,7 +1077,7 @@
} else if (Toolbox.selected.id === 'resize_tool') {
//Scale
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor;
if (previousValue !== point[axis]) {
@ -1110,7 +1113,7 @@
} else if (Toolbox.selected.id === 'pivot_tool') {
var snap_factor = canvasGridSize(event.shiftKey, event.ctrlKey)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor// * (useBedrockFlipFix(axis) ? -1 : 1)
point[axis] = Math.round( point[axis] / snap_factor ) * snap_factor;
if (previousValue === undefined) {
previousValue = point[axis]
@ -1121,9 +1124,13 @@
var difference = point[axis] - previousValue
if (Format.bone_rig && Group.selected) {
var origin = Group.selected.origin.slice();
origin[axisNumber] += difference;
Group.selected.transferOrigin(origin, true);
if (Modes.edit) {
var origin = Group.selected.origin.slice();
origin[axisNumber] += difference;
Group.selected.transferOrigin(origin, true);
} else if (Modes.animate) {
Group.selected.origin[axisNumber] += difference;
}
} else {
var origin = Transformer.rotation_object.origin.slice()
origin[axisNumber] += difference;
@ -1133,7 +1140,10 @@
}
})
}
Canvas.updatePositions(true)
Canvas.updatePositions(true);
if (Modes.animate) {
Animator.preview();
}
scope.updateSelection()
previousValue = point[axis]
@ -1174,7 +1184,6 @@
scope.keyframe.offset(axis, difference);
scope.keyframe.select()
updateKeyframeSelection()
Animator.preview()
previousValue = value
scope.hasChanged = true
@ -1242,7 +1251,7 @@
scope.orbit_controls.stopMovement()
outlines.children.length = 0
if (Modes.id === 'edit') {
if (Modes.id === 'edit' || Toolbox.selected.id == 'pivot_tool') {
if (Toolbox.selected.id === 'resize_tool') {
//Scale
selected.forEach(function(obj) {
@ -1297,27 +1306,3 @@
THREE.TransformControls.prototype.constructor = THREE.TransformControls;
}() );
THREE.Euler.prototype.inverse = function () {
var q = new THREE.Quaternion();
return function inverse() {
return this.setFromQuaternion( q.setFromEuler( this ).inverse() );
};
}();
THREE.Vector3.prototype.removeEuler = function (euler) {
var normal = new THREE.Vector3(0, 0, 1)
return function removeEuler(euler) {
this.applyAxisAngle(normal, -euler.z)
this.applyAxisAngle(normal.set(0, 1, 0), -euler.y)
this.applyAxisAngle(normal.set(1, 0, 0), -euler.x)
return this;
};
}();

View File

@ -19,7 +19,7 @@ class Texture {
//Data
this.ratio = 1
this.img = 0;
this.res = 0;
this.width = 0;
this.saved = true
this.mode = isApp ? 'link' : 'bitmap';
@ -58,16 +58,19 @@ class Texture {
var mat = new THREE.MeshLambertMaterial({
color: 0xffffff,
side: Canvas.getRenderSide(),
map: tex,
transparent: settings.transparency.value,
alphaTest: 0.2
});
Canvas.materials[this.uuid] = mat
var size_control = {};
this.img.onload = function() {
if (!this.src) return;
this.tex.needsUpdate = true;
scope.res = img.naturalWidth;
scope.width = img.naturalWidth;
scope.ratio = img.naturalWidth / img.naturalHeight;
if (scope.isDefault) {
@ -88,17 +91,35 @@ class Texture {
}
//--------------------------------------------------------------------------
/*
if (Project.box_uv && textures.indexOf(scope) === 0 && !scope.keep_size) {
var size = {
pw: Project.texture_width,
ph: Project.texture_height,
nw: img.naturalWidth,
nh: img.naturalHeight
}
if (false && (scope.old_width != size.nw || scope.old_height != size.nh) && (size.pw != size.nw || size.ph != size.nh)) {
if (Project.box_uv && Format.single_texture && !scope.keep_size) {
let pw = Project.texture_width;
let ph = Project.texture_height;
let nw = img.naturalWidth;
let nh = img.naturalHeight;
//texture is unlike project
var unlike = (pw != nw || ph != nh);
//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)
)
// x__ >
// xx_ >
// x_x >
// xxx >
// ___ >
// _x_ >
// __x >
// _xx >
if (unlike && changed) {
Blockbench.showMessageBox({
translateKey: 'update_res',
icon: 'photo_size_select_small',
@ -107,11 +128,7 @@ class Texture {
cancel: 1
}, function(result) {
if (result === 0) {
var lockUV = ( // EG. Texture Optimierung > Modulo geht nicht in allen Bereichen auf
(size.pw%size.nw || size.ph%size.nh) &&
(size.nw%size.pw || size.nh%size.ph)
)
entityMode.setResolution(img.naturalWidth, img.naturalHeight, lockUV)
setProjectResolution(img.naturalWidth, img.naturalHeight)
if (selected.length) {
main_uv.loadData()
main_uv.setGrid()
@ -119,11 +136,13 @@ class Texture {
}
})
}
scope.old_width = img.naturalWidth
scope.old_height = img.naturalHeight
size_control.old_width = img.naturalWidth
size_control.old_height = img.naturalHeight
}
*/
//--------------------------------------------------------------------------
if ($('.dialog#texture_edit:visible').length > 0 && scope.selected === true) {
@ -153,6 +172,9 @@ class Texture {
return 1/this.ratio
}
}
get height() {
return this.width / this.ratio;
}
getErrorMessage() {
switch (this.error) {
case 0: return ''; break;
@ -441,6 +463,7 @@ class Texture {
return;
}
var scope = this;
this.stopWatcher();
fs.watchFile(scope.path, {interval: 50}, function(curr, prev) {
if (curr.mtime !== prev.mtime) {

View File

@ -98,13 +98,14 @@ function isMovementGlobal() {
function isInBox(val) {
return !Format.canvas_limit || (val < 32 && val > -16)
}
function limitToBox(val) {
function limitToBox(val, inflate) {
if (typeof inflate != 'number') inflate = 0;
if (!Format.canvas_limit) {
return val;
} else if (val > 32) {
return 32;
} else if (val < -16) {
return -16;
} else if (val + inflate > 32) {
return 32 - inflate;
} else if (val - inflate < -16) {
return -16 + inflate;
} else {
return val;
}
@ -338,9 +339,9 @@ const Vertexsnap = {
for (i=0; i<3; i++) {
if (m[i] === 1) {
obj.to[i] += cube_pos.getComponent(i)
obj.to[i] = limitToBox(obj.to[i] + cube_pos.getComponent(i), obj.inflate)
} else {
obj.from[i] += cube_pos.getComponent(i)
obj.from[i] = limitToBox(obj.from[i] + cube_pos.getComponent(i), -obj.inflate)
}
}
if (Project.box_uv && obj.visibility) {
@ -358,12 +359,10 @@ const Vertexsnap = {
var q = obj.mesh.getWorldQuaternion(new THREE.Quaternion()).inverse()
cube_pos.applyQuaternion(q)
}
obj.from[0] += cube_pos.getComponent(0)
obj.from[1] += cube_pos.getComponent(1)
obj.from[2] += cube_pos.getComponent(2)
obj.to[0] += cube_pos.getComponent(0)
obj.to[1] += cube_pos.getComponent(1)
obj.to[2] += cube_pos.getComponent(2)
var in_box = obj.move(cube_pos);
if (!in_box) {
Blockbench.showMessageBox({translateKey: 'canvas_limit_error'})
}
})
}
@ -403,13 +402,13 @@ function scaleAll(save, size) {
if (obj.from) {
obj.from[i] = (obj.before.from[i] - ogn) * size;
if (obj.from[i] + ogn > 32 || obj.from[i] + ogn < -16) overflow.push(obj);
obj.from[i] = limitToBox(obj.from[i] + ogn);
obj.from[i] = limitToBox(obj.from[i] + ogn, -obj.inflate);
}
if (obj.to) {
obj.to[i] = (obj.before.to[i] - ogn) * size;
if (obj.to[i] + ogn > 32 || obj.to[i] + ogn < -16) overflow.push(obj);
obj.to[i] = limitToBox(obj.to[i] + ogn);
obj.to[i] = limitToBox(obj.to[i] + ogn, obj.inflate);
}
if (obj.origin) {
@ -513,8 +512,8 @@ function centerCubes(axis, update) {
var difference = (Format.bone_rig ? 0 : 8) - average
selected.forEach(function(obj) {
if (obj.movable) obj.from[axis] = limitToBox(obj.from[axis] + difference);
if (obj.scalable) obj.to[axis] = limitToBox(obj.to[axis] + difference);
if (obj.movable) obj.from[axis] = limitToBox(obj.from[axis] + difference, obj.inflate);
if (obj.scalable) obj.to[axis] = limitToBox(obj.to[axis] + difference, obj.inflate);
if (obj.origin) obj.origin[axis] += difference;
})
@ -734,7 +733,7 @@ BARS.defineActions(function() {
Undo.finishEdit('resize')
}
})
//Inflage
//Inflate
new NumSlider({
id: 'slider_inflate',
condition: function() {return Cube.selected.length && Modes.edit},
@ -836,7 +835,7 @@ BARS.defineActions(function() {
))
}
//Origin
function moveOriginOnAxis(value, fixed, axis) {
if (Group.selected) {
var diff = value

View File

@ -170,7 +170,7 @@ var Undo = {
if (aspects.animations) {
this.animations = {}
aspects.animations.forEach(a => {
scope.animations[a.uuid] = a.undoCopy();
scope.animations[a.uuid] = a.getUndoCopy();
})
}
if (aspects.keyframes && Animator.selected && Animator.selected.getBoneAnimator()) {
@ -179,7 +179,7 @@ var Undo = {
bone: Animator.selected.getBoneAnimator().uuid
}
aspects.keyframes.forEach(kf => {
scope.keyframes[kf.uuid] = kf.undoCopy()
scope.keyframes[kf.uuid] = kf.getUndoCopy()
})
}
@ -250,7 +250,7 @@ var Undo = {
if (save.selection_group && !is_session) {
Group.selected = undefined
var sel_group = Outliner.root.findRecursive('uuid', save.selection_group)
var sel_group = Group.all.findInArray('uuid', save.selection_group)
if (sel_group) {
sel_group.select()
}
@ -266,7 +266,7 @@ var Undo = {
}
if (save.group) {
var group = Outliner.root.findRecursive('uuid', save.group.uuid)
var group = Group.all.findInArray('uuid', save.group.uuid)
if (group) {
if (is_session) {
delete save.group.isOpen;
@ -361,19 +361,19 @@ var Undo = {
for (var uuid in Animator.selected.bones) {
if (uuid === save.keyframes.bone) {
bone = Animator.selected.bones[uuid]
if (bone.select && Animator.open && is_session) {
bone.select()
if (bone.group && Animator.open && !is_session) {
bone.group.select()
}
}
}
}
if (bone) {
if (bone.uuid === save.keyframes.bone) {
function getKeyframe(uuid) {
var i = 0;
while (i < Timeline.keyframes.length) {
if (Timeline.keyframes[i].uuid === uuid) {
return Timeline.keyframes[i];
while (i < bone.keyframes.length) {
if (bone.keyframes[i].uuid === uuid) {
return bone.keyframes[i];
}
i++;
}
@ -386,10 +386,9 @@ var Undo = {
if (kf) {
kf.extend(data)
} else {
kf = new Keyframe(data)
kf = new Keyframe(data, uuid)
kf.parent = bone;
kf.uuid = uuid;
Timeline.keyframes.push(kf)
bone.keyframes.push(kf)
added++;
}
}

View File

@ -18,36 +18,6 @@ function compareVersions(string1/*new*/, string2/*old*/) {
}
return false;
}
function useBedrockFlipFix(axis) {
if (Format.bone_rig === false) return false;
if (typeof axis === 'string') {
axis = getAxisNumber(axis)
}
var group;
if (Group.selected) {
var group = Group.selected
} else {
var i = 0;
while (i < selected.length) {
if (typeof selected[i].parent === 'object' &&
selected[i].parent.type === 'group'
) {
var group = selected[i].parent
}
i++;
}
}
if (group) {
var rotations = group.rotation.slice()
rotations.splice(axis, 1)
rotations.forEach(function(r, i) {
rotations[i] = (r >= -90 && r <= 90)
})
return rotations[0] !== rotations[1]
} else {
return false
}
}
const Condition = function(condition, context) {
if (condition !== undefined && condition !== null && condition.condition !== undefined) {
condition = condition.condition
@ -155,6 +125,12 @@ Math.isPowerOfTwo = function(x) {
Math.randomab = function(a, b) {
return a + Math.random()*(b-a);
}
Math.areMultiples = function(n1, n2) {
return (
(n1/n2)%1 === 0 ||
(n2/n1)%1 === 0
)
}
function trimFloatNumber(val) {
if (val == '') return val;
var string = val.toFixed(4)
@ -448,6 +424,7 @@ function pathToName(path, extension) {
}
}
function pathToExtension(path) {
if (typeof path !== 'string') return '';
var matches = path.match(/\.\w{2,24}$/)
if (!matches || !matches.length) return '';
return matches[0].replace('.', '').toLowerCase()

View File

@ -20,7 +20,7 @@ function showUVShiftDialog() {
id: 'uv_shift_dialog',
fadeTime: 100,
onConfirm: function() {
Undo.initEdit({elements: elements, uv_only: true})
Undo.initEdit({elements: Cube.all, uv_only: true})
dialog.hide()
var h = $(dialog.object).find('#shift_uv_horizontal').val()
if (h.length > 0) {
@ -30,7 +30,7 @@ function showUVShiftDialog() {
add = true
}
h = eval(h)
elements.forEach(function(obj) {
Cube.all.forEach(function(obj) {
if (add) {
obj.uv_offset[0] += h
} else {
@ -46,7 +46,7 @@ function showUVShiftDialog() {
add = true
}
v = eval(v)
elements.forEach(function(obj) {
Cube.all.forEach(function(obj) {
if (add) {
obj.uv_offset[1] += v
} else {
@ -387,8 +387,8 @@ class UVEditor {
//Brush
getBrushCoordinates(event, tex) {
var scope = this;
var multiplier = (Project.box_uv && tex) ? tex.res/Project.texture_width : 1
var pixel_size = scope.inner_size / tex.res
var multiplier = (Project.box_uv && tex) ? tex.width/Project.texture_width : 1
var pixel_size = scope.inner_size / tex.width
return {
x: Math.floor(event.offsetX/scope.getPixelSize()*multiplier),
y: Math.floor(event.offsetY/scope.getPixelSize()*multiplier)
@ -450,8 +450,8 @@ class UVEditor {
return this.inner_size/this.grid
} else {
return this.inner_size/ (
(typeof this.texture === 'object' && this.texture.res)
? this.texture.res
(typeof this.texture === 'object' && this.texture.width)
? this.texture.width
: this.grid
)
}
@ -543,13 +543,13 @@ class UVEditor {
grid = BarItems.uv_grid.get()
if (grid === 'auto') {
if (this.texture) {
grid = this.texture.res
grid = this.texture.width
} else {
grid = 16
}
this.autoGrid = true
} else if (grid === 'none') {
grid = 1024
grid = 2084
} else {
grid = parseInt(grid)
}
@ -731,21 +731,21 @@ class UVEditor {
this.jquery.frame.css('background-color', 'var(--color-back)').css('background-image', 'none')
this.texture = false;
this.setFrameColor()
if (this.autoGrid || Project.box_uv) {
this.setGrid(16, false)
}
this.setGrid(16, false)
} else {
this.setFrameColor(tex.dark_box)
var css = 'url("'+tex.source.split('\\').join('\\\\').replace(/ /g, '%20')+'")'
this.jquery.frame.css('background-image', css)
if (Project.box_uv) {
this.jquery.frame.css('background-size', 'contain')
} else {
if (Format.id == 'java_block') {
this.jquery.frame.css('background-size', 'cover')
} else {
this.jquery.frame.css('background-size', 'contain')
}
this.texture = tex;
if (this.autoGrid || Project.box_uv) {
this.setGrid(tex.res, false)
if (this.autoGrid) {
this.setGrid(Project.texture_width, false)
} else {
this.setGrid(undefined, false)
}
}
if (!tex || typeof tex !== 'object') {
@ -1027,11 +1027,13 @@ class UVEditor {
}
setAutoSize(event) {
var scope = this;
var top, left, top2, left2;
var top2, left2;
this.forCubes(obj => {
scope.getFaces(event).forEach(function(side) {
left = top = 0;
var face = obj.faces[side];
face.uv[0] = Math.min(face.uv[0], face.uv[2]);
face.uv[1] = Math.min(face.uv[1], face.uv[3]);
if (side == 'north' || side == 'south') {
left2 = limitNumber(obj.size('0'), 0, 16)
top2 = limitNumber(obj.size('1'), 0, 16)
@ -1042,10 +1044,12 @@ class UVEditor {
left2 = limitNumber(obj.size('0'), 0, 16)
top2 = limitNumber(obj.size('2'), 0, 16)
}
if (obj.faces[side].rotation % 180) {
if (face.rotation % 180) {
[left2, top2] = [top2, left2];
}
obj.faces[side].uv = [left, top, left2, top2]
left2 *= 16 / Project.texture_width;
top2 *= 16 / Project.texture_height;
face.uv_size = [left2, top2];
})
obj.autouv = 0
Canvas.updateUV(obj)
@ -1109,7 +1113,7 @@ class UVEditor {
break;
}
uv.forEach(function(s, uvi) {
uv[uvi] = limitNumber(s, 0, 16)
uv[uvi] = limitNumber(s * 16 / (uvi%2 ? Project.texture_height : Project.texture_width), 0, 16)
})
obj.faces[side].uv = uv
})
@ -1373,7 +1377,7 @@ class UVEditor {
'uv_maximize',
'uv_auto',
'uv_rel_auto',
{icon: 'rotate_90_degrees_ccw', name: 'menu.uv.mapping.rotation', children: function() {
{icon: 'rotate_90_degrees_ccw', condition: () => Format.id == 'java_block', name: 'menu.uv.mapping.rotation', children: function() {
var off = 'radio_button_unchecked'
var on = 'radio_button_checked'
return [
@ -1789,7 +1793,7 @@ BARS.defineActions(function() {
new BarSlider({
id: 'uv_rotation',
category: 'uv',
condition: () => !Project.box_uv && Cube.selected.length,
condition: () => !Project.box_uv && Format.id == 'java_block' && Cube.selected.length,
min: 0, max: 270, step: 90, width: 80,
onBefore: () => {
Undo.initEdit({elements: Cube.selected, uv_only: true})
@ -1812,6 +1816,8 @@ BARS.defineActions(function() {
'16': '16x16',
'32': '32x32',
'64': '64x64',
'128': '128x128',
'256': '256x256',
none: true,
},
onChange: function(slider) {

View File

@ -964,5 +964,7 @@
"panel.element.position": "Position",
"panel.element.size": "Größe",
"panel.element.origin": "Angelpunkt",
"panel.element.rotation": "Rotation"
"panel.element.rotation": "Rotation",
"message.canvas_limit_error.title": "Begrenzungsfehler",
"message.canvas_limit_error.message": "Die Aktion konnte nicht korrekt durchgeführt werden, weil das Format das Modell auf einen Bereich von 48 Einheiten beschränkt. Verschiebe den Angelpunkt, um das zu verhindern."
}

View File

@ -98,6 +98,8 @@
"status_bar.recording_gif":"Recording GIF",
"status_bar.processing_gif":"Processing GIF",
"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 origin to prevent this.",
"message.rotation_limit.title": "Rotation Limits",
"message.rotation_limit.message": "Rotations are limited by Minecraft to one axis and 22.5 degree increments. Rotating on a different axis will clear all rotations on the other axes. Convert the model to \"Free Model\" if you are modeling for other purposes and need free rotations.",
"message.file_not_found.title": "File Not Found",

View File

@ -890,7 +890,7 @@
"dates.yesterday": "Ayer",
"dates.this_week": "Esta semana",
"dates.weeks_ago": "Hace %0 semanas",
"mode.start": "Empezar",
"mode.start": "Inicio",
"mode.start.new": "Nuevo",
"mode.start.recent": "Reciente",
"format.free": "Modelo libre",
@ -953,16 +953,18 @@
"web.download_app": "Descargar Aplicación",
"uv_editor.turned": "Mapeado Girado",
"display.reference.crossbow": "Ballesta",
"dialog.settings.search_results": "Search Results",
"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",
"action.import_optifine_part.desc": "Import an entity part model for OptiFine",
"data.locator": "Locator",
"mode.start.no_recents": "No recently opened models",
"panel.element": "Element",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"dialog.settings.search_results": "Resultados de Búsqueda",
"settings.animation_snap": "Imán de Animación",
"settings.animation_snap.desc": "Intervalo del imán para frames en la timeline de la animación en pasos por segundo",
"action.import_optifine_part": "Importar Parte de OptiFine",
"action.import_optifine_part.desc": "Importa un modelo de parte de entidad para OptiFine",
"data.locator": "Localizador",
"mode.start.no_recents": "No hay modelos abiertos recientemente",
"panel.element": "Elemento",
"panel.element.position": "Posición",
"panel.element.size": "Tamaño",
"panel.element.origin": "Punto de Pivote",
"panel.element.rotation": "Rotación",
"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 origin to prevent this."
}

View File

@ -49,7 +49,7 @@
"message.rotation_limit.title": "Limites de rotation",
"message.rotation_limit.message": "Minecraft limite les rotations à un axe et par intervalles de 22.5 degrés. Appliquer une rotation sur un axe va supprimer la rotation des autres axes. Désactiver l'option \"Rotation restreintes\" si vous modélisez pour d'autres logiciels et avez besoin de rotations libres. ",
"message.file_not_found.title": "Fichier non trouvé",
"message.file_not_found.message": "Blockbench n'a pas pu trouver le fichier demandé. Assurez-vous qu'il est stocké localement et pas sur le cloud.",
"message.file_not_found.message": "Blockbench n'a pas pu trouver le fichier demandé. Assurez-vous qu'il soit stocké localement et pas sur un cloud.",
"message.screenshot.title": "Capture d'écran",
"message.screenshot.message": "Capture d'écran effectuée",
"message.screenshot.clipboard": "Presse-papier",
@ -60,7 +60,7 @@
"message.invalid_model.message": "Le fichier ne contient pas de données de modèle valide",
"message.child_model_only.title": "Modèle enfant vide",
"message.child_model_only.message": "Ce fichier a pour parent %0 et ne contient pas de model",
"message.drag_background.title": "Positionner le fond d'écran ",
"message.drag_background.title": "Positionner le fond d'écran",
"message.drag_background.message": "Glissez l'écran pour changer sa position. Maintenez shift puis glissez verticalement pour changer sa taille.",
"message.unsaved_textures.title": "Textures non sauvegardées",
"message.unsaved_textures.message": "Votre modèle a des textures non sauvegardées. Assurez-vous de les enregistrer et de les coller dans votre pack de ressources dans le dossier approprié.",
@ -68,7 +68,7 @@
"message.model_clipping.message": "Votre modèle contient %0 cubes plus grands que la limite de 3x3x3 autorisée par Minecraft. Ce modèle ne fonctionnera pas dans Minecraft. Activer l'option 'Toile limité' pour empêcher cela",
"message.loose_texture.title": "Importation texture",
"message.loose_texture.message": "La texture importée n'est pas dans un pack de ressource. Minecraft peut seulement charger des textures dans le dossier textures d'un pack de ressources chargé.",
"message.loose_texture.change": "Changer chemin",
"message.loose_texture.change": "Changer le chemin",
"message.update_res.title": "Résolution de texture",
"message.update_res.message": "Souhaitez-vous mettre à jour la résolution du projet avec la résolution de cette texture? Cliquez sur 'Annuler' si votre texture a une résolution supérieure à la normale.",
"message.update_res.update": "Mise à jour",
@ -752,7 +752,7 @@
"menu.group.material": "Mettre un matériel",
"action.camera_reset": "Réinitialiser la caméra",
"action.camera_reset.desc": "Réinitialiser l'angle de la caméra de la prévisualisation actuelle",
"panel.variable_placeholders": "Espaces réservés variables",
"panel.variable_placeholders": "Espaces réservés aux variables",
"panel.variable_placeholders.info": "Lister les variables à prévisualiser par nom=valeur",
"status_bar.vertex_distance": "Distance: %0",
"dialog.create_gif.title": "Enregistrer un GIF",
@ -893,7 +893,7 @@
"mode.start": "Commencer",
"mode.start.new": "Nouveau",
"mode.start.recent": "Récent",
"format.free": "Modèle gratuit",
"format.free": "Modèle libre",
"format.free.desc": "Modèle sans limites pour Unity etc.",
"format.java_block": "Bloc/Objet Java",
"format.java_block.desc": "Model de bloc pour Java Edition. La taille et les rotations sont limitées.",
@ -953,16 +953,18 @@
"web.download_app": "Télécharger l'application",
"uv_editor.turned": "Mappage UV retournée",
"display.reference.crossbow": "Arbalète",
"dialog.settings.search_results": "Search Results",
"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",
"action.import_optifine_part.desc": "Import an entity part model for OptiFine",
"data.locator": "Locator",
"mode.start.no_recents": "No recently opened models",
"panel.element": "Element",
"dialog.settings.search_results": "Rechercher les résultats",
"settings.animation_snap": "Aligner les animations",
"settings.animation_snap.desc": "Intervalle de Snap des clés d'animation dans la ligne de temps, en pas par seconde",
"action.import_optifine_part": "Importer une partie OptiFine",
"action.import_optifine_part.desc": "Importer une partie d'entité pour un modèle OptiFine",
"data.locator": "Localisateur",
"mode.start.no_recents": "Aucun modèle ouvert récemment",
"panel.element": "Élément",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.size": "Taille",
"panel.element.origin": "Point de pivot",
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -959,10 +959,12 @@
"action.import_optifine_part": "Importare parte OptiFine",
"action.import_optifine_part.desc": "Importare una parte di un modello per OptiFine",
"data.locator": "Locator",
"mode.start.no_recents": "No recently opened models",
"panel.element": "Element",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"mode.start.no_recents": "Nessun modello aperto recentemente",
"panel.element": "Elemento",
"panel.element.position": "Posizione",
"panel.element.size": "Dimensione",
"panel.element.origin": "Origine",
"panel.element.rotation": "Rotazione",
"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 origin to prevent this."
}

View File

@ -445,13 +445,13 @@
"action.center_z.desc": "選択したキューブをZ軸の中心に合わせる",
"action.center_all": "Center All",
"action.center_all.desc": "キューブの中心を回転軸にする",
"action.toggle_visibility": "Toggle Visibility",
"action.toggle_visibility": "可視化",
"action.toggle_visibility.desc": "選択したキューブの表示を切り替えます",
"action.toggle_export": "Toggle Export",
"action.toggle_export": "エキスポート",
"action.toggle_export.desc": "選択したキューブのエクスポート設定を切り替えます",
"action.toggle_autouv": "Toggle Auto UV",
"action.toggle_autouv": "オートUV",
"action.toggle_autouv.desc": "選択したキューブの自動UV設定を切り替えます",
"action.toggle_shade": "Toggle Shading",
"action.toggle_shade": "シェーディング",
"action.toggle_shade.desc": "選択したキューブの偏光を切り替えます",
"action.rename": "名前を変更",
"action.rename.desc": "選択したキューブの名前を変更します",
@ -498,7 +498,7 @@
"menu.display": "Display",
"menu.view": "View",
"menu.file.new": "新規作成",
"menu.file.recent": "呼び戻し",
"menu.file.recent": "最近使ったファイル",
"menu.file.import": "インポート",
"menu.file.export": "エクスポート",
"menu.transform.rotate": "回転",
@ -792,7 +792,7 @@
"action.open_backup_folder.desc": "Blockbenchのバックアップフォルダーを開きます",
"switches.mirror": "ミラーUV",
"Name of the Language you are editing, NOT English": {
"language_name": "English"
"language_name": "英語"
},
"message.plugin_reload": "%0プラグインをリロードしました",
"settings.brightness": "輝度",
@ -898,13 +898,13 @@
"format.java_block": "Java block/item",
"format.java_block.desc": "Java Editionモデリング (サイズと回転は限られています)",
"format.bedrock": "Bedrock Model",
"format.bedrock.desc": "統合版 モデリング",
"format.bedrock_old": "統合版デフォルトモデル",
"format.bedrock.desc": "統合版モデリング",
"format.bedrock_old": "Bedrock Legacy Model",
"format.bedrock_old.desc": "統合版-1.12 エンティティモデル",
"format.modded_entity": "Modded Entity",
"format.modded_entity.desc": "エンティティモデリング",
"format.optifine_entity": "OptiFine Entity",
"format.optifine_entity.desc": "OptiFine カスタムエンティティモデリング",
"format.optifine_entity.desc": "OptiFineカスタムエンティティモデリング",
"keys.mouse": "マウス %0",
"message.cleared_blank_faces.title": "空白の面",
"message.cleared_blank_faces.message": "%0個のテクスチャがない面を発見しました。それらをすべて削除しますか",
@ -927,42 +927,44 @@
"settings.painting_grid.desc": "キューブ上にグリットを表示する",
"action.slider_brush_min_opacity": "最小不透明度",
"action.slider_brush_min_opacity.desc": "ブラシの最小不透明度",
"action.convert_project": "Convert Project",
"action.convert_project.desc": "Converts the current project to a project for another model format",
"action.convert_project": "プロジェクトを変換",
"action.convert_project.desc": "別のモデルフォーマットに変換します",
"action.close_project": "プロジェクトを閉じる",
"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.close_project.desc": "進行中のプロジェクトを閉じます",
"action.export_bedrock": "ジオメトリーとしてエクスポート",
"action.export_bedrock.desc": "モデルを統合版ジオメトリーファイルとしてエクスポートします",
"action.save_project": "プロジェクトを保存",
"action.save_project.desc": "Saves the current model as a project file",
"action.save_project.desc": "進行中のプロジェクトを保存します",
"action.save_project_as": "すべてのプロジェクトを保存",
"action.save_project_as.desc": "Saves the current model as a project file at a new location",
"action.save_project_as.desc": "進行中のすべてのプロジェクトを保存します",
"action.export_over": "上書き保存",
"action.export_over.desc": "Saves the model, textures and animations by overwriting the files",
"action.export_over.desc": "呼び出したファイルに上書きで保存します",
"action.add_locator": "ロケータ-を追加",
"action.add_locator.desc": "Adds a new locator to control positions of particles, leashes etc",
"action.sidebar_left": "Textures and UV",
"action.sidebar_left.desc": "Open the interface for UV and textures",
"action.add_locator.desc": "パーティクルの位置を制御するための新しいロケータを追加します",
"action.sidebar_left": "テクスチャとUV",
"action.sidebar_left.desc": "テクスチャとUVを呼び出します",
"action.sidebar_right": "エレメント",
"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.sidebar_right.desc": "変更したエレメントを呼び出します",
"action.uv_turn_mapping": "ターンマッピング",
"action.uv_turn_mapping.desc": "UVマッピングを90度回転させます",
"action.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",
"uv_editor.turned": "Turned Mapping",
"action.remove_blank_faces.desc": "選択した面をすべて削除します",
"menu.uv.select": "キューブを選択",
"web.download_app": "Appをダウンロード",
"uv_editor.turned": "ターンマッピング",
"display.reference.crossbow": "クロスボウ",
"dialog.settings.search_results": "Search Results",
"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",
"action.import_optifine_part.desc": "Import an entity part model for OptiFine",
"dialog.settings.search_results": "検索",
"settings.animation_snap": "アニメーションスナップ",
"settings.animation_snap.desc": "アニメーションライン1ステップの間隔幅",
"action.import_optifine_part": "OptiFineをインポート",
"action.import_optifine_part.desc": "OptiFineエンティティモデルをインポートします",
"data.locator": "ロケーター",
"mode.start.no_recents": "最近開いたモデルはありません",
"panel.element": "Element",
"panel.element": "エレメント",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.origin": "Center",
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -964,5 +964,7 @@
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -964,5 +964,7 @@
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -964,5 +964,7 @@
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -25,12 +25,12 @@
"keys.rightclick": "ПКМ",
"keys.tab": "Tab",
"keys.backspace": "Backspace",
"keys.enter": "Ввод",
"keys.enter": "Enter",
"keys.escape": "Escape",
"keys.function": "F%0",
"keys.numpad": "%0 (цифр. кл.)",
"keys.caps": "Caps Lock",
"keys.menu": "Menu",
"keys.menu": "Меню",
"keys.left": "Стрелка влево",
"keys.up": "Стрелка вверх",
"keys.right": "Стрелка вправо",
@ -47,7 +47,7 @@
"keys.printscreen": "Print Screen",
"keys.pause": "Pause",
"message.rotation_limit.title": "Ограничение поворота",
"message.rotation_limit.message": "Поворот ограничен до 22,5 градусов Майнкрафтом по каждой с осей. Поворот на разных осях сбросит повороты на других. Отключите функцию \"Ограничение поворота\", если вы создаете модель для других целей, где нужен свободный поворот.",
"message.rotation_limit.message": "Поворот ограничен до 22,5 градусов Майнкрафтом по каждой из осей. Поворот на разных осях сбросит повороты на других. Отключите функцию \"Ограничение поворота\", если вы создаете модель для других целей, где нужен свободный поворот.",
"message.file_not_found.title": "Файл не найден",
"message.file_not_found.message": "Blockbench не может найти запрашиваемый файл. Убедитесь, что он сохранен по указанному пути, не в облаке.",
"message.screenshot.title": "Скриншот",
@ -147,8 +147,8 @@
"dialog.plugins.show_less": "Показать меньше",
"dialog.entitylist.title": "Открыть модель сущности",
"dialog.entitylist.text": "Выберите модель, которую Вы хотите импортировать",
"dialog.entitylist.bones": "костей",
"dialog.entitylist.cubes": "кубов",
"dialog.entitylist.bones": "Костей",
"dialog.entitylist.cubes": "Кубов",
"dialog.create_texture.title": "Создать текстуру",
"dialog.create_texture.name": "Имя файла",
"dialog.create_texture.folder": "Папка",
@ -958,11 +958,13 @@
"settings.animation_snap.desc": "Интервал привязки кадров на графике анимаций",
"action.import_optifine_part": "Импортировать часть OptiFine",
"action.import_optifine_part.desc": "Импортировать часть модели сущности OptiFine",
"data.locator": "Locator",
"mode.start.no_recents": "No recently opened models",
"panel.element": "Element",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"data.locator": "Маркер",
"mode.start.no_recents": "Вы еще не открывали моделей",
"panel.element": "Элемент",
"panel.element.position": "Позиция",
"panel.element.size": "Размер",
"panel.element.origin": "Центральная точка",
"panel.element.rotation": "Поворот",
"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 origin to prevent this."
}

View File

@ -964,5 +964,7 @@
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element.rotation": "Rotation",
"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 origin to prevent this."
}

View File

@ -1,20 +1,20 @@
{
"dialog.ok": "OK",
"dialog.cancel": "Cancel",
"dialog.confirm": "Confirm",
"dialog.close": "Close",
"dialog.import": "Import",
"dialog.save": "Save",
"dialog.discard": "Discard",
"dialog.dontshowagain": "Don't Show Again",
"dialog.ok": "好的",
"dialog.cancel": "取消",
"dialog.confirm": "确认",
"dialog.close": "关闭",
"dialog.import": "导入",
"dialog.save": "保存",
"dialog.discard": "弃置",
"dialog.dontshowagain": "不再显示",
"data.cube": "Cube",
"data.group": "Group",
"data.texture": "Texture",
"data.plugin": "Plugin",
"data.preview": "Preview",
"data.toolbar": "Toolbar",
"data.image": "Image",
"keys.ctrl": "Control",
"data.group": "",
"data.texture": "材质",
"data.plugin": "插件",
"data.preview": "预览",
"data.toolbar": "工具栏",
"data.image": "图片",
"keys.ctrl": "Ctrl",
"keys.shift": "Shift",
"keys.alt": "Alt",
"keys.meta": "Cmd",
@ -47,7 +47,7 @@
"keys.printscreen": "截屏键",
"keys.pause": "暂停键",
"message.rotation_limit.title": "旋转限制",
"message.rotation_limit.message": "受 Minecarft 的限制仅允许旋转单个轴且旋转角度限制为22.5的倍数。在不同的轴上旋转将清除其他轴上的旋转。如果您的建模需要自由旋转,请禁用“限制旋转”选项",
"message.rotation_limit.message": "受 Minecarft 的限制仅允许旋转单个轴且旋转角度限制为22.5的倍数。在不同的轴上旋转将清除其他轴上的旋转。如果您的建模需要自由旋转,请禁用“限制旋转”选项",
"message.file_not_found.title": "未找到文件",
"message.file_not_found.message": "Blockbench 找不到该文件。请确保此文件为本地文件而不是云文件。",
"message.screenshot.title": "屏幕截图",
@ -65,7 +65,7 @@
"message.unsaved_textures.title": "未保存的贴图",
"message.unsaved_textures.message": "您的模型中存在未保存的贴图。请确保它们存在于资源包里正确的文件夹中。",
"message.model_clipping.title": "模型太大了",
"message.model_clipping.message": "您的模型包含了 %0 个大于 Minecraft 限制允许的 3x3x3 方块范围。此模型将不会被 Minecraft 成功读取。可启用 “限制工作区” 选项防止此类情况发生",
"message.model_clipping.message": "您的模型包含了 %0 个大于 Minecraft 限制允许的 3x3x3 方块范围。此模型将不会被 Minecraft 成功读取。可启用 “限制工作区” 选项防止此类情况发生",
"message.loose_texture.title": "导入贴图",
"message.loose_texture.message": "导入的贴图不在资源包中。Minecraft 只能加载资源包里 textures 文件夹中的贴图。",
"message.loose_texture.change": "更改路径",
@ -395,66 +395,66 @@
"action.paste.desc": "粘贴选定的,面或显示设置",
"action.cut": "剪切",
"action.cut.desc": "剪切选定的,面或显示设置",
"action.add_cube": "添加方块",
"action.add_cube.desc": "添加一个新的方块",
"action.add_cube": "添加立方体",
"action.add_cube.desc": "添加一个新的立方体",
"action.add_group": "添加组",
"action.add_group.desc": "添加一个新的组",
"action.outliner_toggle": "切换更多选项",
"action.outliner_toggle.desc": "切换开关以在outliner中添加更多选项",
"action.duplicate": "生成副本",
"action.duplicate.desc": "复制选定的方块或组",
"action.duplicate.desc": "复制选定的立方体或组",
"action.delete": "删除",
"action.delete.desc": "删除选定的方块或组",
"action.delete.desc": "删除选定的立方体或组",
"action.sort_outliner": "排序大纲",
"action.sort_outliner.desc": "按字母顺序排列大纲",
"action.local_move": "相对移动",
"action.local_move.desc": "如果可以,将旋转的元素移动到自己的轴上",
"action.select_window": "选择",
"action.select_window.desc": "根据其属性搜索并选择方块",
"action.select_window.desc": "根据其属性搜索并选择立方体",
"action.invert_selection": "反向选择",
"action.invert_selection.desc": "反向选择当前的方块",
"action.invert_selection.desc": "反向选择当前的立方体",
"action.select_all": "全选",
"action.select_all.desc": "选择所有方块",
"action.select_all.desc": "选择所有立方体",
"action.collapse_groups": "折叠组",
"action.collapse_groups.desc": "折叠所有组",
"action.scale": "缩放",
"action.scale.desc": "缩放选定的方块",
"action.scale.desc": "缩放选定的立方体",
"action.rotate_x_cw": "顺时针旋转",
"action.rotate_x_cw.desc": "在 X 轴上将选定的方块旋转 90°",
"action.rotate_x_cw.desc": "在 X 轴上将选定的立方体旋转 90°",
"action.rotate_x_ccw": "逆时针旋转",
"action.rotate_x_ccw.desc": "在 X 轴上旋转选定的方块 -90°",
"action.rotate_x_ccw.desc": "在 X 轴上旋转选定的立方体 -90°",
"action.rotate_y_cw": "顺时针旋转",
"action.rotate_y_cw.desc": "在 Y 轴上将选定的方块旋转 90°",
"action.rotate_y_cw.desc": "在 Y 轴上将选定的立方体旋转 90°",
"action.rotate_y_ccw": "逆时针旋转",
"action.rotate_y_ccw.desc": "在 Y 轴上将选定的方块旋转 -90°",
"action.rotate_y_ccw.desc": "在 Y 轴上将选定的立方体旋转 -90°",
"action.rotate_z_cw": "顺时针旋转",
"action.rotate_z_cw.desc": "在 Z 轴上将选定的方块旋转 90°",
"action.rotate_z_cw.desc": "在 Z 轴上将选定的立方体旋转 90°",
"action.rotate_z_ccw": "逆时针旋转",
"action.rotate_z_ccw.desc": "在 Y 轴上将选定的方块旋转 -90°",
"action.rotate_z_ccw.desc": "在 Y 轴上将选定的立方体旋转 -90°",
"action.flip_x": "X 轴翻转",
"action.flip_x.desc": "翻转 X 轴上的选定方块",
"action.flip_x.desc": "翻转 X 轴上的选定立方体",
"action.flip_y": "Y 轴翻转",
"action.flip_y.desc": "翻转 Y 轴上的选定方块",
"action.flip_y.desc": "翻转 Y 轴上的选定立方体",
"action.flip_z": "Z 轴翻转",
"action.flip_z.desc": "翻转 Z 轴上的选定方块",
"action.flip_z.desc": "翻转 Z 轴上的选定立方体",
"action.center_x": "X 轴居中",
"action.center_x.desc": "将所选方块居中在 X 轴上",
"action.center_x.desc": "将所选立方体居中在 X 轴上",
"action.center_y": "Y 轴居中",
"action.center_y.desc": "将所选方块居中在 Y 轴上",
"action.center_y.desc": "将所选立方体居中在 Y 轴上",
"action.center_z": "Z 轴居中",
"action.center_z.desc": "将所选方块居中在 Z 轴上",
"action.center_z.desc": "将所选立方体居中在 Z 轴上",
"action.center_all": "全部居中",
"action.center_all.desc": "使所选方块居中",
"action.center_all.desc": "使所选立方体居中",
"action.toggle_visibility": "切换可见",
"action.toggle_visibility.desc": "切换所选方块的可见",
"action.toggle_visibility.desc": "切换所选立方体的可见",
"action.toggle_export": "切换导出",
"action.toggle_export.desc": "切换所选方块的导出设置",
"action.toggle_export.desc": "切换所选立方体的导出设置",
"action.toggle_autouv": "切换自动 UV",
"action.toggle_autouv.desc": "切换所选方块的自动UV设置",
"action.toggle_autouv.desc": "切换所选立方体的自动UV设置",
"action.toggle_shade": "切换阴影",
"action.toggle_shade.desc": "切换所选方块的阴影",
"action.toggle_shade.desc": "切换所选立方体的阴影",
"action.rename": "重命名",
"action.rename.desc": "更改所选方块的名称",
"action.rename.desc": "更改所选立方体的名称",
"action.add_display_preset": "新建预设",
"action.add_display_preset.desc": "添加新的显示设置预设",
"action.fullscreen": "全屏",
@ -486,9 +486,9 @@
"action.origin_to_geometry": "原点到几何",
"action.origin_to_geometry.desc": "将原点设置为几何体的中心",
"action.rescale_toggle": "切换重新调节",
"action.rescale_toggle.desc": "根据当前旋转重新缩放方块",
"action.rescale_toggle.desc": "根据当前旋转重新缩放立方体",
"action.bone_reset_toggle": "重置骨骼",
"action.bone_reset_toggle.desc": "阻止骨骼显示父模型中的方块",
"action.bone_reset_toggle.desc": "阻止骨骼显示父模型中的立方体",
"action.reload": "重载 Blockbench",
"action.reload.desc": "重载 Blockbench这将删除所有未保存的进度",
"menu.file": "文件",
@ -531,7 +531,7 @@
"menu.texture.properties": "属性",
"menu.preview.background": "背景",
"menu.preview.background.load": "加载",
"menu.preview.background.position": "位置",
"menu.preview.background.position": "立方体位置",
"menu.preview.background.lock": "锁定视角",
"menu.preview.background.remove": "清除",
"menu.preview.screenshot": "截图",
@ -601,7 +601,7 @@
"display.reference.bow": "弓",
"display.reference.block": "方块",
"display.reference.frame": "物品展示框",
"display.reference.inventory_nine": "3x3",
"display.reference.inventory_nine": "合成台",
"display.reference.inventory_full": "物品栏",
"display.reference.hud": "HUD",
"display.preset.blank_name": "请输入名称",
@ -638,7 +638,7 @@
"dialog.shift_uv.vertical": "垂直",
"keybindings.reset": "重启",
"keybindings.clear": "Empty",
"action.cube_counter": "方块数量",
"action.cube_counter": "立方体数量",
"action.uv_rotation": "UV旋转",
"action.uv_rotation.desc": "UV面的旋转",
"action.uv_grid": "UV网格",
@ -690,7 +690,7 @@
"message.image_editor_missing.message": "选择图像编辑器的可执行文件",
"message.image_editor_missing.detail": "Blockbench 无法在您的计算机上找到图像编辑器,请选择图像编辑器的可执行文件。",
"action.update_autouv": "自动更新UV",
"action.update_autouv.desc": "更新所选方块的自动UV映射",
"action.update_autouv.desc": "更新所选立方体的自动UV映射",
"category.uv": "UV",
"status_bar.saved": "模型已保存",
"status_bar.unsaved": "有未保存的更改",
@ -768,7 +768,7 @@
"message.set_background_position.title": "背景位置",
"menu.preview.background.set_position": "设置位置",
"dialog.toolbar_edit.hidden": "隐藏",
"action.export_class_entity": "导出java实体",
"action.export_class_entity": "导出为java实体模型",
"action.export_class_entity.desc": "作为java class导出模型",
"settings.seethrough_outline": "X-Ray 轮廓",
"settings.seethrough_outline.desc": "通过对象显示轮廓",
@ -894,15 +894,15 @@
"mode.start.new": "新建",
"mode.start.recent": "最近",
"format.free": "自由模型",
"format.free.desc": "为 Unity 等软件的无限制模型",
"format.free.desc": "制作 Unity 等游戏制作软件的无建模限制的模型",
"format.java_block": "Java 方块/物品",
"format.java_block.desc": "Java 版方块模型,大小和旋转都有限制。",
"format.java_block.desc": "Java 版方块模型,大小和旋转都有限制",
"format.bedrock": "基岩版模型",
"format.bedrock.desc": "适用于基岩版的模型",
"format.bedrock_old": "旧版基岩版模型",
"format.bedrock_old.desc": "Pre-1.12 基岩版实体模型",
"format.modded_entity": "模组版实体",
"format.modded_entity.desc": "为模组所适用的模型,能够直接以 .java 类文件形式导出。",
"format.modded_entity.desc": "为模组所适用的模型,能够直接以 .java 的class文件形式导出",
"format.optifine_entity": "OptiFine 实体",
"format.optifine_entity.desc": "OptiFine 自定义的实体模型",
"keys.mouse": "鼠标按键 %0",
@ -943,7 +943,7 @@
"action.add_locator.desc": "添加新的定位器,以控制粒子、牵引等位置",
"action.sidebar_left": "材质和 UV",
"action.sidebar_left.desc": "打开一个 UV 和材质的面板",
"action.sidebar_right": "元素",
"action.sidebar_right": "立方体元素属性",
"action.sidebar_right.desc": "打开编辑元素的面板",
"action.uv_turn_mapping": "转向映射",
"action.uv_turn_mapping.desc": "将 UV 映射转 90 度",
@ -960,9 +960,11 @@
"action.import_optifine_part.desc": "为 OptiFine 导入一个实体部件模型",
"data.locator": "定位器",
"mode.start.no_recents": "最近未打开过模型文件",
"panel.element": "Element",
"panel.element.position": "Position",
"panel.element.size": "Size",
"panel.element.origin": "Pivot Point",
"panel.element.rotation": "Rotation"
"panel.element": "元素",
"panel.element.position": "位置",
"panel.element.size": "尺寸",
"panel.element.origin": "旋转源点",
"panel.element.rotation": "旋转",
"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 origin to prevent this."
}

View File

@ -59,6 +59,27 @@ Object.assign( THREE.Euler.prototype, {
}
})
THREE.Euler.prototype.inverse = function () {
var q = new THREE.Quaternion();
return function inverse() {
return this.setFromQuaternion( q.setFromEuler( this ).inverse() );
};
}();
THREE.Vector3.prototype.removeEuler = function (euler) {
var normal = new THREE.Vector3(0, 0, 1)
return function removeEuler(euler) {
this.applyAxisAngle(normal, -euler.z)
this.applyAxisAngle(normal.set(0, 1, 0), -euler.y)
this.applyAxisAngle(normal.set(1, 0, 0), -euler.x)
return this;
};
}();
var GridBox = function( from, to, size, material) {
var vertices = [];
@ -110,7 +131,6 @@ var GridBox = function( from, to, size, material) {
material = material || new THREE.LineBasicMaterial( { color: gizmo_colors.grid } );
THREE.LineSegments.call( this, geometry, material );
}
GridBox.prototype = Object.assign( Object.create( THREE.LineSegments.prototype ), {
constructor: GridBox,

View File

@ -1,7 +1,7 @@
{
"name": "Blockbench",
"description": "Model editing and animation software",
"version": "3.0.4",
"version": "3.0.5",
"license": "MIT",
"author": {
"name": "JannisX11",