blockbench/js/io/formats/bedrock_old.js
2022-06-11 12:28:21 +02:00

527 lines
14 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]
}
}
let geometry_name = data.name.replace(/^geometry\./, '');
let existing_tab = isApp && ModelProject.all.find(project => (
Project !== project && project.export_path == Project.export_path && project.geometry_name == geometry_name
))
if (existing_tab) {
Project.close().then(() => {
existing_tab.select();
});
pe_list_data.length = 0;
hideDialog()
return;
}
codec.dispatchEvent('parse', {model: data.object});
Project.geometry_name = geometry_name;
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%markerColors.length
}).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,
multiple_per_file: 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',
category: 'minecraft',
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,
onSetup(project) {
if (isApp) {
project.BedrockEntityManager = new BedrockEntityManager(project);
}
}
})
//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()
}
})
})
})()