mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-02-17 16:20:13 +08:00
Fix Update Notification button not working Split event for new project / setup project Fix issue where deleting theme files would cause error pop up on start Fix issue with field in texture generator dialog toggling on input (caused by underlying bug in Condition system) Fix wrong icon in "Display UV" option Fix new cubes not updating position
505 lines
13 KiB
JavaScript
505 lines
13 KiB
JavaScript
(function() {
|
|
|
|
function parseGeometry(data) {
|
|
if (data === undefined) {
|
|
pe_list_data.forEach(function(s) {
|
|
if (s.selected === true) {
|
|
data = s
|
|
}
|
|
})
|
|
if (data == undefined) {
|
|
data = pe_list_data[0]
|
|
}
|
|
}
|
|
codec.dispatchEvent('parse', {model: data.object});
|
|
Project.geometry_name = data.name.replace(/^geometry\./, '');
|
|
Project.texture_width = data.object.texturewidth || 64;
|
|
Project.texture_height = data.object.textureheight || 64;
|
|
|
|
if (typeof data.object.visible_bounds_width == 'number' && typeof data.object.visible_bounds_height == 'number') {
|
|
Project.visible_box[0] = Math.max(Project.visible_box[0], data.object.visible_bounds_width || 0);
|
|
Project.visible_box[1] = Math.max(Project.visible_box[1], data.object.visible_bounds_height || 0);
|
|
if (data.object.visible_bounds_offset && typeof data.object.visible_bounds_offset[1] == 'number') {
|
|
Project.visible_box[2] = data.object.visible_bounds_offset[1] || 0;
|
|
}
|
|
}
|
|
|
|
var bones = {}
|
|
|
|
if (data.object.bones) {
|
|
var included_bones = []
|
|
data.object.bones.forEach(function(b) {
|
|
included_bones.push(b.name)
|
|
})
|
|
data.object.bones.forEach(function(b, bi) {
|
|
var group = new Group({
|
|
name: b.name,
|
|
origin: b.pivot,
|
|
rotation: b.rotation,
|
|
material: b.material,
|
|
color: Group.all.length%8
|
|
}).init()
|
|
bones[b.name] = group
|
|
if (b.pivot) {
|
|
group.origin[0] *= -1
|
|
}
|
|
group.rotation[0] *= -1;
|
|
group.rotation[1] *= -1;
|
|
|
|
group.mirror_uv = b.mirror === true
|
|
group.reset = b.reset === true
|
|
|
|
if (b.cubes) {
|
|
b.cubes.forEach(function(s) {
|
|
var base_cube = new Cube({name: b.name, autouv: 0, color: group.color})
|
|
if (s.origin) {
|
|
base_cube.from.V3_set(s.origin);
|
|
base_cube.from[0] = -(base_cube.from[0] + s.size[0])
|
|
if (s.size) {
|
|
base_cube.to[0] = s.size[0] + base_cube.from[0]
|
|
base_cube.to[1] = s.size[1] + base_cube.from[1]
|
|
base_cube.to[2] = s.size[2] + base_cube.from[2]
|
|
}
|
|
}
|
|
if (s.uv) {
|
|
base_cube.uv_offset[0] = s.uv[0]
|
|
base_cube.uv_offset[1] = s.uv[1]
|
|
}
|
|
if (s.inflate && typeof s.inflate === 'number') {
|
|
base_cube.inflate = s.inflate
|
|
}
|
|
if (s.mirror === undefined) {
|
|
base_cube.mirror_uv = group.mirror_uv
|
|
} else {
|
|
base_cube.mirror_uv = s.mirror === true
|
|
}
|
|
base_cube.addTo(group).init()
|
|
})
|
|
}
|
|
if (b.children) {
|
|
b.children.forEach(function(cg) {
|
|
cg.addTo(group)
|
|
})
|
|
}
|
|
if (b.locators) {
|
|
for (var key in b.locators) {
|
|
var coords, rotation;
|
|
if (b.locators[key] instanceof Array) {
|
|
coords = b.locators[key];
|
|
} else {
|
|
coords = b.locators[key].offset;
|
|
rotation = b.locators[key].rotation;
|
|
}
|
|
coords[0] *= -1
|
|
var locator = new Locator({from: coords, name: key, rotation}).addTo(group).init();
|
|
}
|
|
}
|
|
var parent_group = 'root';
|
|
if (b.parent) {
|
|
if (bones[b.parent]) {
|
|
parent_group = bones[b.parent]
|
|
} else {
|
|
data.object.bones.forEach(function(ib) {
|
|
if (ib.name === b.parent) {
|
|
ib.children && ib.children.length ? ib.children.push(group) : ib.children = [group]
|
|
}
|
|
})
|
|
}
|
|
}
|
|
group.addTo(parent_group)
|
|
})
|
|
}
|
|
|
|
codec.dispatchEvent('parsed', {model: data.object});
|
|
|
|
pe_list_data.length = 0;
|
|
hideDialog()
|
|
|
|
loadTextureDraggable()
|
|
Canvas.updateAllBones()
|
|
setProjectTitle()
|
|
if (isApp && Project.geometry_name) {
|
|
Project.BedrockEntityManager.initEntity()
|
|
}
|
|
updateSelection()
|
|
}
|
|
|
|
|
|
var codec = new Codec('bedrock_old', {
|
|
name: 'Bedrock Entity Model',
|
|
extension: 'json',
|
|
remember: true,
|
|
load_filter: {
|
|
type: 'json',
|
|
extensions: ['json'],
|
|
condition(model) {
|
|
return (model.format_version && compareVersions('1.12.0', model.format_version)) ||
|
|
Object.keys(model).find(s => s.match(/^geometry\./));
|
|
}
|
|
},
|
|
compile(options) {
|
|
if (options === undefined) options = {}
|
|
var entitymodel = {}
|
|
entitymodel.texturewidth = Project.texture_width;
|
|
entitymodel.textureheight = Project.texture_height;
|
|
var bones = []
|
|
var visible_box = new THREE.Box3()
|
|
|
|
var groups = getAllGroups();
|
|
var loose_elements = [];
|
|
Outliner.root.forEach(obj => {
|
|
if (obj.type === 'cube' || obj.type == 'locator') {
|
|
loose_elements.push(obj)
|
|
}
|
|
})
|
|
if (loose_elements.length) {
|
|
let group = new Group({
|
|
name: 'bb_main'
|
|
});
|
|
group.children.push(...loose_elements);
|
|
group.is_catch_bone = true;
|
|
group.createUniqueName();
|
|
groups.splice(0, 0, group);
|
|
}
|
|
|
|
groups.forEach(function(g) {
|
|
if (g.type !== 'group') return;
|
|
if (!settings.export_empty_groups.value && !g.children.find(child => child.export)) return;
|
|
//Bone
|
|
var bone = {}
|
|
bone.name = g.name
|
|
if (g.parent.type === 'group') {
|
|
bone.parent = g.parent.name
|
|
}
|
|
bone.pivot = g.origin.slice()
|
|
bone.pivot[0] *= -1
|
|
if (!g.rotation.allEqual(0)) {
|
|
bone.rotation = [
|
|
-g.rotation[0],
|
|
-g.rotation[1],
|
|
g.rotation[2]
|
|
]
|
|
}
|
|
if (g.reset) bone.reset = true;
|
|
if (g.mirror_uv && Project.box_uv) bone.mirror = true;
|
|
if (g.material) bone.material = g.material;
|
|
|
|
//Elements
|
|
var cubes = []
|
|
var locators = {};
|
|
for (var obj of g.children) {
|
|
if (obj.export) {
|
|
if (obj instanceof Cube) {
|
|
|
|
var template = new oneLiner()
|
|
template.origin = obj.from.slice()
|
|
template.size = obj.size()
|
|
template.origin[0] = -(template.origin[0] + template.size[0])
|
|
template.uv = obj.uv_offset
|
|
if (obj.inflate && typeof obj.inflate === 'number') {
|
|
template.inflate = obj.inflate
|
|
}
|
|
if (obj.mirror_uv === !bone.mirror) {
|
|
template.mirror = obj.mirror_uv
|
|
}
|
|
//Visible Bounds
|
|
var mesh = obj.mesh
|
|
if (mesh) {
|
|
visible_box.expandByObject(mesh)
|
|
}
|
|
cubes.push(template)
|
|
|
|
} else if (obj instanceof Locator) {
|
|
|
|
locators[obj.name] = obj.from.slice();
|
|
locators[obj.name][0] *= -1;
|
|
}
|
|
}
|
|
}
|
|
if (cubes.length) {
|
|
bone.cubes = cubes
|
|
}
|
|
if (Object.keys(locators).length) {
|
|
bone.locators = locators
|
|
}
|
|
bones.push(bone)
|
|
})
|
|
|
|
if (bones.length && options.visible_box !== false) {
|
|
|
|
let visible_box = calculateVisibleBox();
|
|
entitymodel.visible_bounds_width = visible_box[0] || 0;
|
|
entitymodel.visible_bounds_height = visible_box[1] || 0;
|
|
entitymodel.visible_bounds_offset = [0, visible_box[2] || 0, 0]
|
|
}
|
|
if (bones.length) {
|
|
entitymodel.bones = bones
|
|
}
|
|
this.dispatchEvent('compile', {model: entitymodel, options});
|
|
|
|
if (options.raw) {
|
|
return entitymodel
|
|
} else {
|
|
var model_name = 'geometry.' + (Project.geometry_name||'unknown')
|
|
return autoStringify({
|
|
format_version: '1.10.0',
|
|
[model_name]: entitymodel
|
|
})
|
|
}
|
|
},
|
|
parse(data, path) {
|
|
pe_list_data.length = 0
|
|
|
|
var geometries = []
|
|
for (var key in data) {
|
|
if (typeof data[key] === 'object') {
|
|
geometries.push(key);
|
|
}
|
|
}
|
|
if (geometries.length === 1) {
|
|
parseGeometry({object: data[geometries[0]], name: geometries[0]})
|
|
return;
|
|
}
|
|
|
|
$('#pe_search_bar').val('')
|
|
if (pe_list && pe_list._data) {
|
|
pe_list._data.search_term = ''
|
|
}
|
|
|
|
function create_thumbnail(model_entry, isize) {
|
|
var included_bones = []
|
|
model_entry.object.bones.forEach(function(b) {
|
|
included_bones.push(b.name)
|
|
})
|
|
new Jimp(48, 48, 0x00000000, function(err, image) {
|
|
model_entry.object.bones.forEach(function(b) {
|
|
var rotation = b.rotation
|
|
if (!rotation || rotation[0] === undefined) {
|
|
if (entityMode.hardcodes[model_entry.name] && entityMode.hardcodes[model_entry.name][b.name]) {
|
|
rotation = entityMode.hardcodes[model_entry.name][b.name].rotation
|
|
}
|
|
}
|
|
if (b.cubes) {
|
|
b.cubes.forEach(function(c) {
|
|
if (c.origin && c.size) {
|
|
//Do cube
|
|
var inflate = c.inflate||0
|
|
var coords = {
|
|
x: (c.origin[2]-inflate)*isize+24,
|
|
y: 40-(c.origin[1]+c.size[1]+inflate)*isize,
|
|
w: (c.size[2]+2*inflate)*isize,
|
|
h: (c.size[1]+2*inflate)*isize
|
|
}
|
|
var shade = (limitNumber(c.origin[0], -24, 24)+24)/48*255
|
|
var color = parseInt('0xffffff'+shade.toString(16))
|
|
coords.x = limitNumber(coords.x, 0, 47)
|
|
coords.y = limitNumber(coords.y, 0, 47)
|
|
coords.w = limitNumber(coords.w, 0, 47 - coords.x)
|
|
coords.h = limitNumber(coords.h, 0, 47 - coords.y)
|
|
if (coords.h > 0 && coords.w > 0) {
|
|
if (rotation && rotation[0] !== 0 && b.pivot) {
|
|
Painter.drawRotatedRectangle(
|
|
image,
|
|
0xffffff88,
|
|
coords,
|
|
b.pivot[2]*isize+24,
|
|
40-b.pivot[1]*isize,
|
|
-rotation[0]
|
|
)
|
|
} else {
|
|
Painter.drawRectangle(image, 0xffffff88, coords)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
//Send
|
|
image.getBase64("image/png", function(a, dataUrl){
|
|
model_entry.icon = dataUrl
|
|
})
|
|
})
|
|
}
|
|
for (var key in data) {
|
|
if (key.includes('geometry.') && data.hasOwnProperty(key)) {
|
|
var base_model = {name: key, bonecount: 0, cubecount: 0, selected: false, object: data[key], icon: false}
|
|
var oversize = 2;
|
|
var words = key.replace(/:.*/g, '').replace('geometry.', '').split(/[\._]/g)
|
|
words.forEach(function(w, wi) {
|
|
words[wi] = capitalizeFirstLetter(w)
|
|
})
|
|
base_model.title = words.join(' ')
|
|
if (data[key].bones) {
|
|
base_model.bonecount = data[key].bones.length
|
|
data[key].bones.forEach(function(b) {
|
|
if (b.cubes) {
|
|
base_model.cubecount += b.cubes.length
|
|
b.cubes.forEach(function(c) {
|
|
if (c.origin && c.size && (c.origin[2] < -12 || c.origin[2] + c.size[2] > 12 || c.origin[1] + c.size[1] > 22) && oversize === 2) oversize = 1
|
|
if (c.origin && c.size && (c.origin[2] < -24 || c.origin[2] + c.size[2] > 24)) oversize = 0.5
|
|
})
|
|
}
|
|
})
|
|
if (typeof base_model.cubecount !== 'number') {
|
|
base_model.cubecount = '[E]'
|
|
} else if (base_model.cubecount > 0) {
|
|
|
|
create_thumbnail(base_model, oversize)
|
|
|
|
|
|
}
|
|
}
|
|
pe_list_data.push(base_model)
|
|
}
|
|
}
|
|
if (pe_list == undefined) {
|
|
pe_list = new Vue({
|
|
el: '#pe_list',
|
|
data: {
|
|
search_term: '',
|
|
list: pe_list_data
|
|
},
|
|
methods: {
|
|
selectE: function(item, event) {
|
|
var index = pe_list_data.indexOf(item)
|
|
pe_list_data.forEach(function(s) {
|
|
s.selected = false;
|
|
})
|
|
pe_list_data[index].selected = true
|
|
},
|
|
open() {
|
|
parseGeometry()
|
|
},
|
|
tl
|
|
},
|
|
computed: {
|
|
searched() {
|
|
return this.list.filter(item => {
|
|
return item.name.toUpperCase().includes(this.search_term)
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
showDialog('entity_import')
|
|
$('#pe_list').css('max-height', (window.innerHeight - 320) +'px')
|
|
$('input#pe_search_bar').select()
|
|
$('#entity_import .confirm_btn').off('click')
|
|
$('#entity_import .confirm_btn').on('click', (e) => {
|
|
parseGeometry()
|
|
})
|
|
},
|
|
export() {
|
|
var scope = this;
|
|
Blockbench.export({
|
|
resource_id: 'model',
|
|
type: this.name,
|
|
extensions: [this.extension],
|
|
name: this.fileName(),
|
|
startpath: this.startPath(),
|
|
content: this.compile({raw: isApp}),
|
|
custom_writer: isApp ? (a, path) => scope.overwrite(a, path, () => scope.afterSave(path)) : null,
|
|
})
|
|
},
|
|
|
|
overwrite(content, path, cb) {
|
|
|
|
var model_name = 'geometry.' + (Project.geometry_name.replace(/^geometry\./, '')||'unknown')
|
|
var data;
|
|
try {
|
|
data = fs.readFileSync(path, 'utf-8')
|
|
} catch (err) {}
|
|
var obj = {
|
|
format_version: '1.10.0'
|
|
}
|
|
if (data) {
|
|
try {
|
|
obj = autoParseJSON(data, false)
|
|
} catch (err) {
|
|
err = err+''
|
|
var answer = electron.dialog.showMessageBoxSync(currentwindow, {
|
|
type: 'warning',
|
|
buttons: [
|
|
tl('message.bedrock_overwrite_error.backup_overwrite'),
|
|
tl('message.bedrock_overwrite_error.overwrite'),
|
|
tl('dialog.cancel')
|
|
],
|
|
title: 'Blockbench',
|
|
message: tl('message.bedrock_overwrite_error.message'),
|
|
detail: err,
|
|
noLink: false
|
|
})
|
|
if (answer === 0) {
|
|
var backup_file_name = pathToName(path, true) + ' backup ' + new Date().toLocaleString().split(':').join('_')
|
|
backup_file_name = path.replace(pathToName(path, false), backup_file_name)
|
|
fs.writeFile(backup_file_name, data, function (err2) {
|
|
if (err2) {
|
|
console.log('Error saving backup model: ', err2)
|
|
}
|
|
})
|
|
}
|
|
if (answer === 2) {
|
|
return;
|
|
}
|
|
}
|
|
if (typeof obj === 'object') {
|
|
for (var key in obj) {
|
|
if (obj.hasOwnProperty(key) &&
|
|
obj[key].bones &&
|
|
typeof obj[key].bones === 'object' &&
|
|
obj[key].bones.constructor.name === 'Array'
|
|
) {
|
|
obj[key].bones.forEach(function(bone) {
|
|
if (typeof bone.cubes === 'object' &&
|
|
bone.cubes.constructor.name === 'Array'
|
|
) {
|
|
bone.cubes.forEach(function(c, ci) {
|
|
bone.cubes[ci] = new oneLiner(c)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
obj[model_name] = this.compile({raw: true});
|
|
content = autoStringify(obj);
|
|
|
|
Blockbench.writeFile(path, {content}, cb);
|
|
}
|
|
})
|
|
|
|
var format = new ModelFormat({
|
|
id: 'bedrock_old',
|
|
extension: 'json',
|
|
icon: 'icon-format_bedrock_legacy',
|
|
show_on_start_screen: false,
|
|
box_uv: true,
|
|
single_texture: true,
|
|
bone_rig: true,
|
|
centered_grid: true,
|
|
animated_textures: true,
|
|
animation_files: true,
|
|
animation_mode: true,
|
|
locators: true,
|
|
codec,
|
|
})
|
|
//Object.defineProperty(format, 'single_texture', {get: _ => !settings.layered_textures.value})
|
|
codec.format = format;
|
|
|
|
BARS.defineActions(function() {
|
|
codec.export_action = new Action({
|
|
id: 'export_entity',
|
|
icon: format.icon,
|
|
category: 'file',
|
|
condition: () => Format == format,
|
|
click: function () {
|
|
codec.export()
|
|
}
|
|
})
|
|
})
|
|
|
|
})()
|
|
|