UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

248 lines (247 loc) 7.75 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.recolorTemplate = recolorTemplate; exports.getTemplateForTraits = getTemplateForTraits; exports.generateItemTextureFromTemplate = generateItemTextureFromTemplate; /** * ItemTextureTemplates - Template-based item texture generation. * * Stores 16x16 shape masks for each item type (sword, helmet, etc.) extracted * from the custom_items sample pack. Each pixel is encoded as a shade level: * 0 = transparent * 1 = dark (shadow) * 2 = mid (base color) * 3 = light (highlight) * * The recolor function applies a user-chosen color, producing darker/lighter * variants for depth, then encodes the result as RGBA pixels. */ const ImageCodec_1 = __importDefault(require("../core/ImageCodec")); const PngEncoder_1 = require("./PngEncoder"); /** * 16x16 shape masks. Each string is 16 rows of 16 chars. * Chars: '0'=transparent, '1'=dark, '2'=mid, '3'=highlight */ const TEMPLATES = { sword: "0000000000000111" + "0000000000001331" + "0000000000013231" + "0000000000132310" + "0000000001322100" + "0000000013221000" + "0011000122210000" + "0011101222100000" + "0001112121000000" + "0001111210000000" + "0000111100000000" + "0001111110000000" + "0012101111000000" + "1111000011000000" + "1110000000000000" + "1110000000000000", tool: "0000000000000000" + "0000000002300000" + "0000000023300000" + "0000000233000000" + "0000000230000000" + "0000000213000330" + "0000000231203320" + "0000000222133200" + "0000002222222000" + "0000022200000000" + "0000232000000000" + "0002320000000000" + "0023200000000000" + "0232000000000000" + "2220000000000000" + "2200000000000000", helmet: "0000000000000000" + "0000000000000000" + "0000000000000000" + "0000011111100000" + "0000122222210000" + "0001233322221000" + "0001233222221000" + "0001221111221000" + "0001211111121000" + "0001211111121000" + "0001211111121000" + "0000110000110000" + "0000000000000000" + "0000000000000000" + "0000000000000000" + "0000000000000000", chestplate: "0000000000000000" + "0000000000000000" + "0111110000111110" + "0133210000133210" + "0132221001232210" + "0122232112222210" + "0122233332222210" + "0112333222222110" + "0001332222221000" + "0001232222221000" + "0001222222221000" + "0001222222221000" + "0001222222221000" + "0000122222210000" + "0000011111100000" + "0000000000000000", leggings: "0000000000000000" + "0000000000000000" + "0000111111110000" + "0001333332221000" + "0001332222221000" + "0001322222221000" + "0001322112221000" + "0001221001221000" + "0001221001221000" + "0001221001221000" + "0001221001221000" + "0001221001221000" + "0001221001221000" + "0001111001111000" + "0000000000000000" + "0000000000000000", boots: "0000000000000000" + "0000000000000000" + "0000000000000000" + "0000111001110000" + "0001331001331000" + "0001331001321000" + "0001321001221000" + "0001221001221000" + "0001221001221000" + "0012221001222100" + "0122221001222210" + "0122211001122210" + "0111100000011110" + "0000000000000000" + "0000000000000000" + "0000000000000000", food: "0000002200000000" + "0000023320000000" + "0000232232220000" + "0002232332332000" + "0023323323223200" + "0232232223233200" + "0232232222332200" + "0223322223222000" + "0022222223233200" + "0002332332322200" + "0023223222322200" + "0023223233232000" + "0002332322320000" + "0000222322200000" + "0000000220000000" + "0000000000000000", }; /** Map item trait IDs to template names */ const TRAIT_TO_TEMPLATE = { sword: "sword", pickaxe: "tool", axe: "tool", shovel: "tool", armor_helmet: "helmet", armor_chestplate: "chestplate", armor_leggings: "leggings", armor_boots: "boots", food: "food", }; function clamp(v) { return Math.max(0, Math.min(255, Math.round(v))); } /** * Generate a recolored 16x16 RGBA pixel array from a template and a hex color. * * Shade levels map to color multipliers: * 1 (dark) = color * 0.45 * 2 (mid/base) = color * 0.75 * 3 (highlight) = color * 1.0 + 40 brightness boost */ function recolorTemplate(templateName, hexColor) { const template = TEMPLATES[templateName]; if (!template || template.length !== 256) { return undefined; } const { r, g, b } = (0, PngEncoder_1.parseHex)(hexColor); const pixels = new Uint8Array(16 * 16 * 4); for (let i = 0; i < 256; i++) { const shade = template.charCodeAt(i) - 48; // '0'=0, '1'=1, '2'=2, '3'=3 const idx = i * 4; if (shade === 0) { // Transparent pixels[idx] = 0; pixels[idx + 1] = 0; pixels[idx + 2] = 0; pixels[idx + 3] = 0; } else if (shade === 1) { // Dark shadow pixels[idx] = clamp(r * 0.45); pixels[idx + 1] = clamp(g * 0.45); pixels[idx + 2] = clamp(b * 0.45); pixels[idx + 3] = 255; } else if (shade === 2) { // Mid tone (base) pixels[idx] = clamp(r * 0.75); pixels[idx + 1] = clamp(g * 0.75); pixels[idx + 2] = clamp(b * 0.75); pixels[idx + 3] = 255; } else { // Highlight pixels[idx] = clamp(r + 40); pixels[idx + 1] = clamp(g + 40); pixels[idx + 2] = clamp(b + 40); pixels[idx + 3] = 255; } } return { pixels, width: 16, height: 16 }; } /** * Get the template name for an item based on its traits. * Returns undefined if no matching template exists. */ function getTemplateForTraits(traits) { if (!traits) return undefined; for (const traitId of traits) { const template = TRAIT_TO_TEMPLATE[traitId]; if (template) { return template; } } return undefined; } /** * Generate a recolored item texture PNG from traits and a color. * Returns PNG bytes, or undefined if no template matches or encoding fails. */ async function generateItemTextureFromTemplate(traits, hexColor) { const templateName = getTemplateForTraits(traits); if (!templateName) { return undefined; } const result = recolorTemplate(templateName, hexColor); if (!result) { return undefined; } // Try sync encoding (Node.js) const syncEncoded = ImageCodec_1.default.encodeToPngSync(result.pixels, result.width, result.height); if (syncEncoded) { return syncEncoded; } // Try async browser encoding (Canvas API) const browserEncoded = await ImageCodec_1.default.encodeToPngBrowser(result.pixels, result.width, result.height); if (browserEncoded) { return browserEncoded; } return undefined; }