diff --git a/js/interface/menu.js b/js/interface/menu.js index aac7d9cf..1ccaf36c 100644 --- a/js/interface/menu.js +++ b/js/interface/menu.js @@ -504,7 +504,8 @@ const MenuBar = { 'close_project', '_', {name: 'menu.file.import', id: 'import', icon: 'insert_drive_file', children: [ - 'add_model', + 'import_project', + 'import_java_block_model', 'import_optifine_part', 'extrude_texture' ]}, diff --git a/js/io/codec.js b/js/io/codec.js index 3b214e0b..a9c1043d 100644 --- a/js/io/codec.js +++ b/js/io/codec.js @@ -9,6 +9,7 @@ class Codec { Merge.function(this, data, 'load'); Merge.function(this, data, 'compile'); Merge.function(this, data, 'parse'); + Merge.function(this, data, 'merge'); Merge.function(this, data, 'write'); Merge.function(this, data, 'overwrite'); Merge.function(this, data, 'export'); diff --git a/js/io/formats/bbmodel.js b/js/io/formats/bbmodel.js index 545e6241..20f4129e 100644 --- a/js/io/formats/bbmodel.js +++ b/js/io/formats/bbmodel.js @@ -2,6 +2,56 @@ let FORMATV = '3.6'; +function processHeader(model) { + if (!model.meta) { + Blockbench.showMessageBox({ + translateKey: 'invalid_model', + icon: 'error', + }) + return; + } + if (!model.meta.format_version) { + model.meta.format_version = model.meta.format; + } + if (compareVersions(model.meta.format_version, FORMATV)) { + Blockbench.showMessageBox({ + translateKey: 'outdated_client', + icon: 'error', + }) + return; + } +} +function processCompatibility(model) { + + if (!model.meta.model_format) { + if (model.meta.bone_rig) { + model.meta.model_format = 'bedrock_old'; + } else { + model.meta.model_format = 'java_block'; + } + } + + if (model.cubes && !model.elements) { + model.elements = model.cubes; + } + + if (model.outliner) { + if (compareVersions('3.2', model.meta.format_version)) { + //Fix Z-axis inversion pre 3.2 + function iterate(list) { + for (var child of list) { + if (typeof child == 'object' ) { + iterate(child.children); + if (child.rotation) child.rotation[2] *= -1; + } + } + } + iterate(model.outliner) + } + parseGroups(model.outliner) + } +} + var codec = new Codec('project', { name: 'Blockbench Project', extension: 'bbmodel', @@ -124,30 +174,13 @@ var codec = new Codec('project', { } }, parse(model, path) { - if (!model.meta) { - Blockbench.showMessageBox({ - translateKey: 'invalid_model', - icon: 'error', - }) - return; - } - if (!model.meta.format_version) { - model.meta.format_version = model.meta.format; - } - if (compareVersions(model.meta.format_version, FORMATV)) { - Blockbench.showMessageBox({ - translateKey: 'outdated_client', - icon: 'error', - }) - return; - } + + processHeader(model); + processCompatibility(model); + if (model.meta.model_format) { var format = Formats[model.meta.model_format]||Formats.free; format.select() - } else if (model.meta.bone_rig) { - Formats.bedrock_old.select() - } else { - Formats.java_block.select() } Blockbench.dispatchEvent('load_project', {model, path}); @@ -179,10 +212,8 @@ var codec = new Codec('project', { } }) } - if (model.cubes && !model.elements) { - model.elements = model.cubes; - } if (model.elements) { + let default_texture = Texture.getDefault(); model.elements.forEach(function(element) { var copy = NonGroup.fromSave(element, true) @@ -192,8 +223,8 @@ var codec = new Codec('project', { if (texture) { copy.faces[face].texture = texture.uuid } - } else if (Texture.getDefault() && copy.faces && copy.faces[face].texture !== null) { - copy.faces[face].texture = Texture.getDefault().uuid + } else if (default_texture && copy.faces && copy.faces[face].texture !== null) { + copy.faces[face].texture = default_texture.uuid } } copy.init() @@ -202,18 +233,6 @@ var codec = new Codec('project', { loadOutlinerDraggable() } if (model.outliner) { - if (compareVersions('3.2', model.meta.format_version)) { - //Fix Z-axis inversion pre 3.2 - function iterate(list) { - for (var child of list) { - if (typeof child == 'object' ) { - iterate(child.children); - if (child.rotation) child.rotation[2] *= -1; - } - } - } - iterate(model.outliner) - } parseGroups(model.outliner) } if (model.animations) { @@ -236,6 +255,89 @@ var codec = new Codec('project', { Canvas.updateAllBones() Canvas.updateAllPositions() this.dispatchEvent('parsed', {model}) + }, + merge(model, path) { + + /** + * Todo + * + * texture merging + * UV handling + * Outliner issue + * undo + */ + + processHeader(model); + processCompatibility(model); + + Blockbench.dispatchEvent('merge_project', {model, path}); + this.dispatchEvent('merge', {model}) + + + if (model.overrides instanceof Array && Project.overrides instanceof Array) { + Project.overrides.push(...model.overrides); + } + + let width = model.resolution.width || Project.texture_width; + let height = model.resolution.height || Project.texture_height; + + function loadTexture(tex) { + var tex_copy = new Texture(tex, tex.uuid).add(false); + if (isApp && tex.path && fs.existsSync(tex.path) && !model.meta.backup) { + tex_copy.fromPath(tex.path) + } else if (tex.source && tex.source.substr(0, 5) == 'data:') { + tex_copy.fromDataURL(tex.source) + } + } + + if (model.textures && (!Format.single_texture || Texture.all.length == 0)) { + model.textures.forEach(loadTexture) + } + + if (model.elements) { + let default_texture = Texture.getDefault(); + model.elements.forEach(function(element) { + + var copy = NonGroup.fromSave(element, true) + for (var face in copy.faces) { + if (!Format.single_texture && element.faces) { + var texture = element.faces[face].texture !== null && textures[element.faces[face].texture] + if (texture) { + copy.faces[face].texture = texture.uuid + } + } else if (default_texture && copy.faces && copy.faces[face].texture !== null) { + copy.faces[face].texture = default_texture.uuid + } + } + copy.init() + + }) + loadOutlinerDraggable() + } + if (model.outliner) { + parseGroups(model.outliner) + } + if (model.animations) { + model.animations.forEach(ani => { + var base_ani = new Animation() + base_ani.uuid = ani.uuid; + base_ani.extend(ani).add(); + }) + } + if (model.animation_variable_placeholders) { + let vue = Interface.Panels.variable_placeholders.inside_vue; + if (vue._data.text) { + vue._data.text = vue._data.text + '\n\n' + model.animation_variable_placeholders; + } else { + vue._data.text = model.animation_variable_placeholders; + } + } + if (model.display !== undefined) { + DisplayMode.loadJSON(model.display) + } + Canvas.updateAllBones() + Canvas.updateAllPositions() + this.dispatchEvent('parsed', {model}) } }) @@ -263,6 +365,24 @@ BARS.defineActions(function() { codec.export() } }) + + new Action('import_project', { + icon: 'icon-blockbench_file', + category: 'file', + click: function () { + Blockbench.import({ + resource_id: 'model', + extensions: [codec.extension], + type: codec.name, + multiple: true, + }, function(files) { + files.forEach(file => { + var model = autoParseJSON(file.content); + codec.merge(model); + }) + }) + } + }) }) })() diff --git a/js/io/formats/java_block.js b/js/io/formats/java_block.js index 4a913233..ce833a00 100644 --- a/js/io/formats/java_block.js +++ b/js/io/formats/java_block.js @@ -473,6 +473,24 @@ BARS.defineActions(function() { codec.export(); } }) + new Action('import_java_block_model', { + icon: 'assessment', + category: 'file', + condition: () => Format == format, + click: function () { + Blockbench.import({ + resource_id: 'model', + extensions: ['json'], + type: codec.name, + multiple: true, + }, function(files) { + files.forEach(file => { + var model = autoParseJSON(file.content) + codec.parse(model, file.path, true) + }) + }) + } + }) }) })() \ No newline at end of file diff --git a/js/io/io.js b/js/io/io.js index 44665a91..53e2ae70 100644 --- a/js/io/io.js +++ b/js/io/io.js @@ -496,24 +496,6 @@ BARS.defineActions(function() { }) } }) - new Action('add_model', { - icon: 'assessment', - category: 'file', - condition: _ => (Format.id == 'java_block'), - click: function () { - Blockbench.import({ - resource_id: 'model', - extensions: ['json'], - type: 'JSON Model', - multiple: true, - }, function(files) { - files.forEach(file => { - var model = autoParseJSON(file.content) - Codecs.java_block.parse(model, file.path, true) - }) - }) - } - }) new Action('extrude_texture', { icon: 'eject', category: 'file', diff --git a/lang/en.json b/lang/en.json index ce5b7107..445066c9 100644 --- a/lang/en.json +++ b/lang/en.json @@ -701,8 +701,8 @@ "action.convert_project.desc": "Converts the current project to a project for another model format", "action.close_project": "Close Project", "action.close_project.desc": "Closes the currently open project", - "action.add_model": "Add Model", - "action.add_model.desc": "Add a model from a file to the current model", + "action.import_java_block_model": "Add Java Block/Item Model", + "action.import_java_block_model.desc": "Add a Minecraft Java block/item model from a json file to the current model", "action.extrude_texture": "Extruded Texture", "action.extrude_texture.desc": "Generate a model by stretching out a texture", "action.export_blockmodel": "Export Block/Item Model", @@ -732,6 +732,8 @@ "action.save_project.desc": "Saves the current model as a project file", "action.save_project_as": "Save Project As", "action.save_project_as.desc": "Saves the current model as a project file at a new location", + "action.import_project": "Import Project", + "action.import_project.desc": "Import another project from a .bbmodel file into the current project", "action.export_over": "Save Model", "action.export_over.desc": "Saves the model, textures and animations by overwriting the files",