diff --git a/plugins.json b/plugins.json index 1e93784a..773ff9ac 100644 --- a/plugins.json +++ b/plugins.json @@ -906,5 +906,15 @@ "variant": "both", "creation_date": "2024-04-19", "new_repository_format": true + }, + "bunch_o_screenshots": { + "title": "Bunch o' Screenshots", + "author": "0Key", + "description": "Take screenshots of all your models by just pressing one button (or hotkey!)", + "tags": ["Screenshots", "Automation"], + "version": "0.0.2", + "min_version": "4.9.0", + "variant": "desktop", + "creation_date": "2024-04-28" } } diff --git a/plugins/bunch_o_snapshots/about.md b/plugins/bunch_o_snapshots/about.md new file mode 100644 index 00000000..2b3d37bd --- /dev/null +++ b/plugins/bunch_o_snapshots/about.md @@ -0,0 +1,100 @@ +# Bunch o' Screenshots + +## TLDR: This plugin allows you to take a screenshots of a bunch of models at a time. +### **Very useful for Java to Bedrock sprite making.** + +--- +## How to use: + +### Save modes: +- **direct**: + - (Fastest! Most recommended ;) ) + Directly saves every single screenshot into your computer in the designated output directory (check the plugin's settings for that). +- **justSave**: + - (Not so fast...) + Will prompt you a directory selection for every screenshot taken, might be annoying if you have multiple screenshots to take. +- **fullConfirmation**: + - (Slowest :c) +Will prompt you the default Blockbench screenshot window for every screenshot (with the `Clipboard`, `Save`, `Edit`, `Cancel` buttons), super annoying for multiple screenshots. + +### **Steps** +1. Confirm that your settings are fine (most importantly the **output directory**) +2. Open a model (or multiple models at the same time). +3. Use the action button (`Tools > Screenshot all models`) or the hotkey (defaults to `Shift + Alt + S`) + +--- +## Future features: +- Select output directory with GUI. +- Change width and height of the output image. +- Friendlier save mode names? maybe if requested. +- Beautify this about section lol + + + + + diff --git a/plugins/bunch_o_snapshots/icon.png b/plugins/bunch_o_snapshots/icon.png new file mode 100644 index 00000000..cf4b9a4f Binary files /dev/null and b/plugins/bunch_o_snapshots/icon.png differ diff --git a/plugins/bunch_o_snapshots/members.yml b/plugins/bunch_o_snapshots/members.yml new file mode 100644 index 00000000..fdc9f3ab --- /dev/null +++ b/plugins/bunch_o_snapshots/members.yml @@ -0,0 +1,4 @@ +maintainers: + - "0Key (discord: 0key2)" +developers: + - Me! (0Key) diff --git a/plugins/bunch_o_snapshots/plugin.js b/plugins/bunch_o_snapshots/plugin.js new file mode 100644 index 00000000..32636b5f --- /dev/null +++ b/plugins/bunch_o_snapshots/plugin.js @@ -0,0 +1,416 @@ +/// + +// Set width and height options + +(function () { + doLog = (str) => { + console.log(`[BoS] ${str}`); + }; + var pluginData = { + title: "Bunch o' Screenshots", + author: "0Key", + description: "Take screenshots of all your models by just pressing one button (or hotkey!)", + tags: ["Screenshots", "Automation"], + version: "0.0.2", + min_version: "4.9.0", + variant: "desktop", + creation_date: "2024-04-28" + } + const fs = require("fs"); + const os = require("os"); + const path = require("path"); + var runAction, runKeybind, options, allSaves; + var BosSettings; + + var currentSettings; + const defaultSettings = { + devMode: false, + outputDirectory: null, + saveMode: "direct", + }; + + function deleteSettings() { + delete Settings.structure["BoSetings"]; + delete Settings.dialog.sidebar.pages["BoSetings"]; + Settings.dialog.sidebar.build(); + for (const name in BosSettings) { + BosSettings[name]?.delete?.(); + doLog(`Deleted setting: ${name}`); + } + BosSettings = null; + } + + function updateCurrentSettings() { + function invalidDirectoryFallback(fallbackDirectory) { + Settings.openDialog(); + Settings.dialog.close(0); + BosSettings["BoSoutputDirectory"].set(fallbackDirectory); + Blockbench.showMessageBox({ + confirm: 1, + cancel: 0, + title: "[BoS] Invalid directory", + message: + "The given directory is invalid, the previous set directory was restored in settings.", + icon: "report", + width: electron.getCurrentWindow().getContentBounds().width / 10, + }); + } + currentSettings.devMode = BosSettings["BoSdevMode"].value || false; + if (!BosSettings["BoSoutputDirectory"].value) { + const homeDir = os.homedir(); + const docsDirectory = path.join(homeDir, "Documents"); + invalidDirectoryFallback(docsDirectory); + } else + fs.stat(BosSettings["BoSoutputDirectory"].value, (err, stats) => { + if (err) { + if (err.code === "ENOENT") { + invalidDirectoryFallback(currentSettings.outputDirectory); + doLog("Directory does not exist."); + } else { + invalidDirectoryFallback(currentSettings.outputDirectory); + console.error("Error checking directory:", err); + } + } else { + if (stats.isDirectory()) { + currentSettings.outputDirectory = BosSettings["BoSoutputDirectory"].value; + } else { + invalidDirectoryFallback(currentSettings.outputDirectory); + doLog("Path exists, but it is not a directory."); + } + } + }); + currentSettings.saveMode = BosSettings["BoSsaveMode"].value; + } + + function loadSettings() { + currentSettings = { + devMode: currentSettings?.devMode || defaultSettings.devMode, + outputDirectory: currentSettings?.outputDirectory || defaultSettings.outputDirectory, + saveMode: currentSettings?.saveMode || defaultSettings.saveMode + }; + Settings.openDialog(); + Settings.dialog.close(0); + + Settings.addCategory( + "BoSetings", + (data = { + name: pluginData.title, + }) + ); + if (BosSettings == null) { + doLog("create settings"); + BosSettings = { + BoSdevMode: new Setting("BoSdevMode", { + name: "BoSdevMode", + description: + "Developer mode, disables the actualy usage of the plugin, testing purposes only, DO NOT USE.", + category: "BoSetings", + value: currentSettings.devMode, + type: "toggle", + onChange(value) { + updateCurrentSettings(); + }, + }), + BoSoutputDirectory: new Setting("BoSoutputDirectory", { + name: "BoSoutputDirectory", + description: "Where all your screenshots will be saved.", + category: "BoSetings", + value: currentSettings.outputDirectory, + type: "text", + onChange(value) { + updateCurrentSettings(); + }, + }), + BoSoutputDirectorySelection: new Setting( + "BoSoutputDirectorySelection", + { + name: "Change directory", + description: + "Open a directory selection window to change your BoSoutputDirectory", + category: "BoSetings", + value: false, + type: "click", + click: function () { + let dialog = new Dialog({ + title: "WIP", + id: "dirSelectionWIP", + width: img.naturalWidth + 50, + lines: [ + "This is a work in progress...
Can't change the directory like this yet, you'll have to write the directory manually.", + ], + buttons: ["dialog.close"], + onButton(result_index) { + dialog.delete(); + return true; + }, + }); + dialog.show(); + setTimeout(() => { + dialog.close(0); + dialog.delete(); + }, 10000); + }, + } + ), + BoSsaveMode: new Setting("BoSsaveMode", { + name: "BoSsaveMode", + description: + "Set the screenshot saving mode, more info about modes in Plugin's documentation.", + category: "BoSetings", + value: currentSettings.saveMode, + type: "select", + options: { + direct: "direct", + justSave: "justSave", + fullConfirmation: "fullConfirmation", + }, + onChange(value) { + updateCurrentSettings(); + }, + }), + }; + } + updateCurrentSettings(); + Settings.openDialog(); + Settings.dialog.close(0); + } + + // When updating, apply meta data changes to plugins.json entry! + BBPlugin.register("plugin", { + title: pluginData.title, + icon: "icon.png", + author: pluginData.author, + description: pluginData.description, + tags: pluginData.tags, + version: pluginData.version, + min_version: pluginData.min_version, + variant: pluginData.variant, + onload() { + loadSettings(); + allSaves = []; + doLog("Plugin loaded"); + + function notifyAllDone() { + Blockbench.showMessageBox({ + confirm: 1, + cancel: 0, + title: "All screenshots created!", + message: `Paths for screenshots:\n${allSaves + .map((saved) => `
- ${saved}`) + .join("")}`, + icon: "icon-saved", + width: electron.getCurrentWindow().getContentBounds().width / 2, + }); + } + + function takeScreenshot() { + if (currentSettings.devMode !== false) return; + let savePath; + options = { + width: 320, + height: 320, + }; + + let preview = main_preview; + let prevTarget = new THREE.Vector3(); + preview.camera.getWorldDirection(prevTarget); + const oldData = { + isOrtho: preview.isOrtho, + axis: preview.camOrtho.axis, + position: preview.camera.position.clone(), + target: prevTarget, + }; + const captureData = { + isOrtho: true, + axis: null, + position: new THREE.Vector3(-1, 1, -1), + target: new THREE.Vector3(0, 0, 0), + }; + function rollbackPreview() { + preview.isOrtho = oldData.isOrtho; + preview.camOrtho.axis = oldData.axis; + preview.camera.position.copy(oldData.position); + preview.camera.lookAt(oldData.target); + } + function fixPreview() { + preview.isOrtho = captureData.isOrtho; + preview.camOrtho.axis = captureData.axis; + preview.camera.position.copy(captureData.position); + preview.camera.lookAt(captureData.target); + preview.resize(); + } + + let cb = null; + fixPreview(); + Canvas.withoutGizmos(function () { + preview.render(); + + let frame = new CanvasFrame(preview.canvas); + frame.autoCrop(); + + if (options.width && options.height) { + let new_frame = new CanvasFrame(options.width, options.height); + let width = frame.width; + let height = frame.height; + if (width > options.width) { + height /= width / options.width; + width = options.width; + } + if (height > options.height) { + width /= height / options.height; + height = options.height; + } + new_frame.ctx.drawImage( + frame.canvas, + (options.width - width) / 2, + (options.height - height) / 2, + width, + height + ); + frame = new_frame; + } + + let dataUrl = frame.canvas.toDataURL(); + let img = new Image(); + img.src = dataUrl; + img.className = "allow_default_menu checkerboard"; + const projectName = Project + ? Project.name.replace(/\.geo$/, "") + : "screenshot"; + savePath = `${currentSettings.outputDirectory}\\${projectName}.png`; + + // Show screenshot (+options to handle it) + function fullConfirmation() { + Screencam.returnScreenshot(dataUrl, cb); + } + + // Confirm saving... + function justSave() { + let is_gif = dataUrl.substr(5, 9) == "image/gif"; + Blockbench.export( + { + startpath: savePath, + type: tl("data.image"), + // extensions: [is_gif ? 'gif' : 'png'], + extensions: ["png"], + name: Project + ? Project.name.replace(/\.geo$/, "") + : "screenshot", + // content: is_gif ? (isApp ? Buffer(dataUrl.split(',')[1], 'base64') : blob) : dataUrl, + content: dataUrl, + // savetype: is_gif ? 'binary' : 'image', + savetype: "image", + resource_id: "screenshot", + }, + function (path) { + doLog(`Saved in ${path}`); + } + ); + } + // Direct saving into directory from settings + function directSave() { + // Direct save + Blockbench.writeFile( + savePath, + { + content: dataUrl, + savetype: "image", + }, + function (path) { + doLog(`Autosaved in ${path}`); + } + ); + } + switch (currentSettings.saveMode) { + case "direct": + directSave(); + break; + case "justSave": + justSave(); + break; + case "fullConfirmation": + fullConfirmation(); + break; + } + }); + doLog("save triggered!"); + rollbackPreview(); + allSaves = [...allSaves, savePath]; + } + + function doForProjects(func) { + Blockbench.setStatusBarText(`Creating screenshots... `); + const initial = Project; + const numProjects = ModelProject.all.length; + + if (numProjects < 1) { + Blockbench.showMessageBox({ + confirm: 1, + cancel: 0, + title: "Open a project", + message: + "You need at least 1 project currently opened to use Bunch o' Screenshots", + icon: "icon-objects", + }); + return; + } + + let currentProject = 1; + + ModelProject.all.forEach((e) => { + Blockbench.setStatusBarText( + `Processing screenshot (${currentProject}/${numProjects})` + ); + Blockbench.setProgress( + parseFloat((currentProject / numProjects).toFixed(3)) + ); + e.select(); + func(); + currentProject++; + }); + initial.select(); + if (currentSettings.devMode !== false) + doLog("Process finished in dev mode"); + else notifyAllDone(); + savePath = ""; + + Blockbench.setStatusBarText(`All screenshots done!`); + setTimeout(() => { + Blockbench.setProgress(); + Blockbench.setStatusBarText(); + }, 3000); + } + + runAction?.delete?.(); + runKeybind?.delete?.(); + runKeybind = new Keybind({ key: "s", shift: true, alt: true }); + runAction = new Action("runBos", { + name: "Screenshot all models", + description: "Take a Screenshot of all currently opened models.", + icon: "photo_camera", + // condition: null, + category: "tools", + keybind: runKeybind, + click: () => doForProjects(takeScreenshot), + // color: "", + // linked_setting: "", + // children: [], + // label: "", + }); + MenuBar.addAction(runAction, "tools"); + }, + onunload() { + runAction?.delete?.(); + runKeybind?.delete?.(); + }, + oninstall() { + loadSettings(); + doLog("Installed!"); + }, + onuninstall() { + // Delete our settings from user's config + // actually just aesthetics, bc BB handles it anyway after restart. + deleteSettings(); + }, + }); +})();