Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@
"icon": "icon.png",
"author": "McHorse",
"description": "Adds actions to export/import models in BBS format, which is used by BBS mod.",
"version": "1.3.1",
"version": "1.3.2",
"variant": "both",
"tags": ["Exporter", "Importer"],
"min_version": "4.8.0",
Expand Down
171 changes: 131 additions & 40 deletions plugins/bbs_exporter/bbs_exporter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
(function() {
(function ()
{
const IS_BLOCKBENCH5_OR_GT = Blockbench.isNewerThan('4.99');

function processValue(v, group, component)
{
if (!v)
{
return v;
}

if (IS_BLOCKBENCH5_OR_GT)
{
if (
(group === "p" && component === "x") ||
(group === "r" && (component === "x" || component === "y"))
)
{
return invertMolang(v);
}
}

return v;
}

var exportAction, importAction;
var lastOptions = {};
var sides = {
Expand All @@ -22,7 +46,7 @@

function areThereObjects(objects)
{
for (let i = 0; i < objects.length; i++)
for (let i = 0; i < objects.length; i++)
{
if (objects[i].type === "group")
{
Expand Down Expand Up @@ -50,7 +74,7 @@
for (let i = 0; i < objects.length; i++)
{
var object = objects[i];

if (object.type === "group")
{
groups[object.name] = createModelGroup(object, groups);
Expand Down Expand Up @@ -195,7 +219,7 @@
{
/* Triangulate a quad */
var sorted = face.getSortedVertices();

pushVertexKey(sorted[0], face);
pushVertexKey(sorted[1], face);
pushVertexKey(sorted[2], face);
Expand Down Expand Up @@ -266,7 +290,7 @@
var out = [
keyframe.time,
keyframe.interpolation,
getExpression(data, "x", typeGroup === "r" || typeGroup === "p"), getExpression(data, "y", typeGroup === "r"), getExpression(data, "z", false)
getExpression(data, typeGroup, "x"), getExpression(data, typeGroup, "y"), getExpression(data, typeGroup, "z")
];

keyframes.push(out);
Expand All @@ -275,15 +299,14 @@
return keyframes;
}

function getExpression(data, component, invert)
function getExpression(data, group, component)
{
var value = data[component] || 0;
var parsed = parseFloat(value);
var inverter = invert && Blockbench.isNewerThan('4.99') ? invertMolang : (v) => v;

if (!isNaN(value) && !isNaN(parsed))
{
return inverter(parsed);
return processValue(parsed, group, component);
}

if (typeof value === "string")
Expand All @@ -296,7 +319,7 @@
}
}

return inverter(value);
return processValue(parsed, group, component);
}

function compile()
Expand Down Expand Up @@ -445,13 +468,15 @@

function importChannel(animator, name, channel)
{
channel.forEach(kf =>
var group = name[0];

channel.forEach(kf =>
{
animator.addKeyframe({
channel: name,
time: kf[0],
interpolation: kf[1],
data_points: [{x: kf[2], y: kf[3], z: kf[4]}]
data_points: [{ x: processValue(kf[2], group, "x"), y: processValue(kf[3], group, "y"), z: processValue(kf[4], group, "z") }]
});
});
}
Expand Down Expand Up @@ -585,22 +610,26 @@
load_filter: {
type: "json",
extensions: ["json"],
condition: (file) => {
condition: (file) =>
{
return file && file.model && file.model.groups && file.model.texture;
}
},
load(content, file) {
load(content, file)
{
if (!Undo)
{
setupProject(Formats.free);
}

importBBS(content);
},
compile(options) {
compile(options)
{
return autoStringify(compile());
},
fileName() {
fileName()
{
var name = Project.name || "model";

return name.endsWith("bbs") ? name : name + ".bbs";
Expand Down Expand Up @@ -633,42 +662,98 @@
value: false
},
exportAsFolder: {
label: "Export to folder",
description: "When enabled, you can pick the folder where .bbs.json and the texture would be exported to.",
label: (typeof isApp !== 'undefined' && isApp) ? "Export to folder" : "Export as ZIP",
description: (typeof isApp !== 'undefined' && isApp)
? "When enabled, you can pick the folder where .bbs.json and the texture would be exported to."
: "When enabled, exports the model and its textures compiled into a ZIP file.",
type: "checkbox",
value: false
}
},
onConfirm: function(formData) {
onConfirm: function (formData)
{
this.hide();

lastOptions.model = formData.exportModel;
lastOptions.animations = formData.exportAnimations;

if (formData.exportAsFolder)
{
var folder = Blockbench.pickDirectory({
title: "Export destination..."
});

if (folder)
if (typeof isApp !== 'undefined' && isApp)
{
Blockbench.writeFile(PathModule.join(folder, "model.bbs.json"), {
content: autoStringify(compile())
var folder = Blockbench.pickDirectory({
title: "Export destination..."
});

Texture.all.forEach((t) =>
if (folder)
{
if (t.error)
Blockbench.writeFile(PathModule.join(folder, "model.bbs.json"), {
content: autoStringify(compile())
});

var usedNames = {};

Texture.all.forEach((t) =>
{
if (t.error)
{
return;
}

var base = t.name.endsWith(".png") ? t.name.slice(0, -4) : t.name;
var name = base + ".png";
var n = 1;

while (usedNames[name])
{
name = base + "_" + (n++) + ".png";
}

usedNames[name] = true;

Blockbench.writeFile(PathModule.join(folder, name), {
content: t.source,
savetype: 'image'
});
});
}
}
else
{
var zip = new JSZip();
zip.file("model.bbs.json", autoStringify(compile()));

var usedNames = {};

Texture.all.forEach((t) =>
{
if (t.error)
{
return;
}

var name = t.name.endsWith(".png") ? t.name : t.name + ".png";

Blockbench.writeFile(PathModule.join(folder, name), {
content: t.source,
savetype: 'image'

var base = t.name.endsWith(".png") ? t.name.slice(0, -4) : t.name;
var name = base + ".png";
var n = 1;

while (usedNames[name])
{
name = base + "_" + (n++) + ".png";
}

usedNames[name] = true;

zip.file(name, t.getBase64(), {base64: true});
});

zip.generateAsync({type: "blob"}).then((blob) =>
{
Blockbench.export({
type: "Zip Archive",
extensions: ["zip"],
name: (typeof Project !== 'undefined' && Project && Project.name) || "model",
content: blob,
savetype: "zip"
});
});
}
Expand Down Expand Up @@ -700,17 +785,20 @@
author: "McHorse",
description: "Adds actions to export/import models in BBS format, which is used by BBS mod.",
icon: "icon.png",
version: "1.3.1",
version: "1.3.2",
min_version: "4.8.0",
variant: "both",
tags: ["Exporter", "Importer"],
has_changelog: true,
onload() {
onload()
{
exportAction = new Action("bbs_exporter", {
name: "Export BBS model",
category: "file",
description: "Export model as a BBS (.bbs.json) model",
icon: "fa-file-export",
click() {
click()
{
exportDialog.show();
}
});
Expand All @@ -720,12 +808,14 @@
category: "file",
description: "Import a BBS model (.bbs.json) model",
icon: "fa-file-import",
click() {
click()
{
Blockbench.import({
extensions: ['bbs.json'],
type: 'BBS model',
readtype: 'text',
}, (files) => {
}, (files) =>
{
importBBS(JSON.parse(files[0].content));
});
}
Expand All @@ -734,9 +824,10 @@
MenuBar.addAction(exportAction, "file.export");
MenuBar.addAction(importAction, "file.import");
},
onunload() {
onunload()
{
exportAction.delete();
importAction.delete();
}
});
})();
})();
19 changes: 19 additions & 0 deletions plugins/bbs_exporter/changelog.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,24 @@
]
}
]
},
"1.3.2": {
"title": "1.3.2",
"date": "2026-07-07",
"author": "bay4lly",
"categories": [
{
"title": "Features",
"list": [
"Added ZIP export fallback for \"Export as ZIP\" option on the web version of Blockbench"
]
},
{
"title": "Bug fixes",
"list": [
"Fixed animation import"
]
}
]
}
}
Loading