From ced8db96336da5d6d4b1bd465aced098979dcaa9 Mon Sep 17 00:00:00 2001 From: Martin Prokoph Date: Sun, 20 Jul 2025 16:41:26 +0200 Subject: [PATCH] clean: modularize & improve code --- .gitignore | 2 +- Better-Leaves-9.2.zip | Bin 1298099 -> 1298099 bytes gen_pack.py | 497 +------------------ src/betterleaves_json.py | 13 + src/blockstate_generator.py | 67 +++ src/carpet_generator.py | 37 ++ src/data/blockstate_data.py | 21 + src/data/carpetblock.py | 7 + src/data/leafblock.py | 27 + download_helper.py => src/download_helper.py | 0 src/generator.py | 149 ++++++ src/json_utils.py | 18 + src/mod_utils.py | 28 ++ src/model_generator.py | 69 +++ src/texture_generator.py | 77 +++ src/texturepack_utils.py | 28 ++ src/utilities.py | 3 + src/zip_utils.py | 20 + 18 files changed, 570 insertions(+), 493 deletions(-) create mode 100644 src/betterleaves_json.py create mode 100644 src/blockstate_generator.py create mode 100644 src/carpet_generator.py create mode 100644 src/data/blockstate_data.py create mode 100644 src/data/carpetblock.py create mode 100644 src/data/leafblock.py rename download_helper.py => src/download_helper.py (100%) create mode 100644 src/generator.py create mode 100644 src/json_utils.py create mode 100644 src/mod_utils.py create mode 100644 src/model_generator.py create mode 100644 src/texture_generator.py create mode 100644 src/texturepack_utils.py create mode 100644 src/utilities.py create mode 100644 src/zip_utils.py diff --git a/.gitignore b/.gitignore index ad2363c..0376640 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /.alt -/__pycache__ +*__pycache__ diff --git a/Better-Leaves-9.2.zip b/Better-Leaves-9.2.zip index 23fef06f6cd9ee700b7fa25412c1d02120cd61a0..04da90eb3120dbef5fb7e2c59b0fdfd3f0a09411 100644 GIT binary patch delta 89002 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 delta 89002 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 E53YTId;kCd diff --git a/gen_pack.py b/gen_pack.py index 62be121..faf367e 100644 --- a/gen_pack.py +++ b/gen_pack.py @@ -6,481 +6,13 @@ # Depencency imports import argparse import json -import os -import zipfile -import shutil import time -import random -from PIL import Image -from distutils.dir_util import copy_tree # Local imports -from download_helper import downloadFromModrinth - -minify = False - -# Utility functions -def printGreen(out): print("\033[92m{}\033[00m".format(out)) -def printCyan(out): print("\033[96m{}\033[00m" .format(out)) -def printOverride(out): print(" -> {}".format(out)) -def dumpJson(data, f): json.dump(data, f, separators=(',', ':')) if minify else json.dump(data, f, indent=4) - -class BlockStateData: - def __init__(self, namespace, block_name, state): - self.namespace = namespace - self.block_name = block_name - self.state = state - def fromFile(leaf, root, infile): - with open(os.path.join(root, infile), "r") as f: - printOverride("Loading blockstate data from: "+f.name) - return BlockStateData.fromJson(leaf, json.load(f).get("blockStateData")) - def fromJson(leaf, data): return BlockStateData(data["block"].split(":")[0], data["block"].split(":")[1], data["state"]) if "block" in data else BlockStateData(leaf.getId().split(":")[0], leaf.getId().split(":")[1], data["state"]) - -class LeafBlock: - def __init__(self, namespace, block_name, texture_name): - self.namespace = namespace - self.block_name = block_name - self.texture_name = texture_name - base_model = "leaves" - has_carpet = False - has_no_tint = False - has_texture_override = False - should_generate_item_model = False - use_legacy_model = False - texture_prefix = "" - overlay_texture_id = "" - block_id_override = None - texture_id_override = None - dynamictrees_namespace = None - blockstate_data = None - sprite_overrides = None - def getId(self): - if (self.block_id_override != None): return self.block_id_override - return self.namespace+":"+self.block_name - def getTextureId(self): - if (self.texture_id_override != None): return self.texture_id_override - return self.namespace+":block/"+self.texture_prefix+self.texture_name -class CarpetBlock: - def __init__(self, carpet_id, leaf): - self.carpet_id = carpet_id - self.leaf = leaf - if (leaf.has_no_tint): self.base_model = "leaf_carpet_notint" - base_model = "leaf_carpet" - -# This is where the magic happens -def autoGen(jsonData, args): - notint_overrides = jsonData["noTint"] - block_texture_overrides = jsonData["blockTextures"] - overlay_textures = jsonData["overlayTextures"] - compileonly_textures = jsonData["compileOnly"] - block_id_overrides = jsonData["blockIds"] - leaves_with_carpet = jsonData["leavesWithCarpet"] - dynamictrees_namespaces = jsonData["dynamicTreesNamespaces"] - generate_itemmodels_overrides = jsonData["generateItemModels"] - block_state_copies = jsonData["blockStateCopies"] - print("Generating assets...") - if (os.path.exists("./assets")): shutil.rmtree("./assets") - copy_tree("./base/assets/", "./assets/") - if minify: minifyJsonFiles() - - filecount = 0 - if (args.programmer): unpackTexturepacks("./input/programmer_art") - unpackTexturepacks() - unpackMods() - scanModsForTextures() - - for root, dirs, files in os.walk("./input/assets"): - for infile in files: - if infile.endswith(".png") and (len(root.split("/")) > 3): - texture_name = infile.replace(".png", "") - - leaf = LeafBlock(root.split("/")[3], texture_name, texture_name) - - # Handle leaf textures in subfolders - if (len(root.split("/")) > 6): - leaf.texture_prefix = root.split("/")[6]+"/" - if (leaf.block_name == "leaves"): # For mods that use a structure like "texture/woodtype/leaves.png" - leaf.block_name = leaf.texture_prefix.replace("/", "_")+leaf.block_name - printGreen(leaf.getId()) - printOverride("Auto-redirected from "+leaf.getId()) - else: # For mods that use a structure like "texture/natural/some_leaves.png" - printGreen(leaf.getId()) - printOverride("Prefix: "+ leaf.texture_prefix); - else: printGreen(leaf.getId()) - - # We don't want to generate assets for overlay textures - if (leaf.getTextureId()) in overlay_textures.values(): - printOverride("Skipping overlay texture") - continue - # We don't want to generate assets for compile-only textures - if (leaf.getTextureId()) in compileonly_textures: - printOverride("Skipping compile-only texture") - continue - - texture = Image.open(os.path.join(root, infile)) - leaf.use_legacy_model = texture.size[0] != texture.size[1] - if leaf.use_legacy_model: printOverride("Animated – using legacy model") - if args.legacy: - leaf.use_legacy_model = True - printOverride("Using legacy model as requested") - - # Generate texture - if not leaf.use_legacy_model: generateTexture(root, infile, args.programmer) - - # Set block id and apply overrides - if leaf.getId() in block_id_overrides: - leaf.block_id_override = block_id_overrides[leaf.getId()] - printOverride("ID Override: "+leaf.getId()) - - # Set texture id and apply overrides - leaf.has_texture_override = leaf.getId() in block_texture_overrides - if leaf.has_texture_override: - leaf.texture_id_override = block_texture_overrides[leaf.getId()] - printOverride("Texture Override: "+leaf.getTextureId()) - - # Check if the block appears in the notint overrides - leaf.has_no_tint = leaf.getId() in notint_overrides - if leaf.use_legacy_model: - leaf.base_model = "leaves_legacy" - elif leaf.has_no_tint: - leaf.base_model = "leaves_notint" - printOverride("No tint") - - # Check if the block has an additional overlay texture - if leaf.getId() in overlay_textures: - leaf.base_model = "leaves_overlay" - leaf.overlay_texture_id = overlay_textures[leaf.getId()] - printOverride("Has overlay texture: "+leaf.overlay_texture_id) - - # Check if the block has a dynamic trees addon namespace - - if (leaf.namespace) in dynamictrees_namespaces: - leaf.dynamictrees_namespace = dynamictrees_namespaces[leaf.namespace] - - # Check if the block should generate an item model - if leaf.getId() in generate_itemmodels_overrides: - leaf.should_generate_item_model = True - printOverride("Also generating item model") - - # Check for blockstate data - if infile.replace(".png", ".betterleaves.json") in files: - with open(os.path.join(root, infile.replace(".png", ".betterleaves.json")), "r") as f: - jsonFile = json.load(f) - if "blockStateData" in jsonFile: - leaf.blockstate_data = BlockStateData.fromFile(leaf, root, infile.replace(".png", ".betterleaves.json")) - if "spriteOverrides" in jsonFile: - leaf.sprite_overrides = jsonFile["spriteOverrides"] - - # Generate blockstates & models - generateBlockstate(leaf, block_state_copies) - generateBlockModels(leaf) - generateItemModel(leaf) - - # Certain mods contain leaf carpets. - # Because we change the leaf texture, we need to fix the carpet models. - if (leaf.getId()) in leaves_with_carpet: - carpet_ids = leaves_with_carpet[leaf.getId()] - if not isinstance(carpet_ids, list): carpet_ids = [carpet_ids] # In case only one carpet is provided (as a string), turn it into a list - for carpet_id in carpet_ids: - carpet = CarpetBlock(carpet_id, leaf) - generateCarpetAssets(carpet) - printOverride(f"Generating leaf carpet: {carpet.carpet_id}") - - filecount += 1 - # End of autoGen - print() - if (args.programmer): cleanupTexturepacks("./input/programmer_art") - cleanupTexturepacks() - cleanupMods() - printCyan("Processed {} leaf blocks".format(filecount)) - -def unpackMods(): - for root, dirs, files in os.walk("./input/mods"): - for infile in files: - if infile.endswith(".jar"): - print("Unpacking mod: "+infile) - zf = zipfile.ZipFile(os.path.join(root, infile), 'r') - zf.extractall(os.path.join(root, infile.replace(".jar", "_temp"))) - zf.close() - -def cleanupMods(): - if (os.path.exists("./input/mods")): shutil.rmtree("./input/mods") - os.makedirs("./input/mods") - -def scanModsForTextures(): - for root, dirs, files in os.walk("./input/mods"): - for infile in files: - if len(root.split("assets")) > 1: - assetpath = root.split("assets")[1][1:] - modid = assetpath.split("textures")[0].replace("/", "") - if "textures/block" in root and infile.endswith(".png") and "leaves" in infile: - print(f"Found texture {assetpath}/{infile} in mod {modid}") - inputfolder = os.path.join("./input/assets/", assetpath) - os.makedirs(inputfolder, exist_ok=True) - shutil.copyfile(os.path.join(root, infile), os.path.join(inputfolder, infile)) - - -def unpackTexturepacks(rootFolder="./input/texturepacks"): - for root, dirs, files in os.walk(rootFolder): - for infile in files: - if infile.endswith(".zip"): - print("Unpacking texturepack: "+infile) - zf = zipfile.ZipFile(os.path.join(root, infile), 'r') - zf.extractall(os.path.join(root, infile.replace(".zip", "_temp"))) - zf.close() - -def cleanupTexturepacks(rootFolder="./input/texturepacks"): - for root, dirs, files in os.walk(rootFolder): - for folder in dirs: - if folder.endswith("_temp"): - shutil.rmtree(os.path.join(root, folder)) - -def scanPacksForTexture(baseRoot, baseInfile, rootFolder="./input/texturepacks"): - for root, dirs, files in os.walk(rootFolder): - for infile in files: - if "assets" in root and "assets" in baseRoot: - if infile.endswith(".png") and (len(root.split("/")) > 3) and (baseInfile == infile) and (root.split("assets")[1] == baseRoot.split("assets")[1]): - printCyan(" Using texture from: " + root.split("assets")[0].replace(rootFolder, "")) - return root; - return baseRoot - -def generateTexture(root, infile, useProgrammerArt=False): - outfolder = root.replace("assets", "").replace("input", "assets") - os.makedirs(outfolder, exist_ok=True) - - # Check for texture stitching data - textureMap = {} - if os.path.isfile(os.path.join(root, infile.replace(".png", ".betterleaves.json"))): - with open(os.path.join(root, infile.replace(".png", ".betterleaves.json")), "r") as f: - json_data = json.load(f) - if "textureStitching" in json_data: - printOverride("Using texture stitching data from: " + f.name) - # Create texture map from stitching data - for key, value in json_data["textureStitching"].items(): - if "-" in key: - for i in range(int(key.split("-")[0]), int(key.split("-")[1])+1): textureMap[str(i)] = value - else: textureMap[key] = value - # Turn texture map into absolute paths - for key, value in textureMap.items(): - textureRoot = f"./input/assets/{value.split(':')[0]}/textures/" - textureFile = value.split(":")[1] + ".png" - if "/" in textureFile: - textureRoot += textureFile.rsplit("/")[0] - textureFile = textureFile[len(textureFile.rsplit("/")[0])+1:] # The rest of the string, starting behind the first '/' - textureRoot = scanPacksForTexture(textureRoot, textureFile) - if useProgrammerArt: root = scanPacksForTexture(textureRoot, textureFile, "./input/programmer_art") - textureMap[key] = os.path.join(textureRoot, textureFile) - - root = scanPacksForTexture(root, infile) - if useProgrammerArt: root = scanPacksForTexture(root, infile, "./input/programmer_art") - - outfile = os.path.splitext(os.path.join(outfolder, infile))[0] + ".png" - if infile != outfile: - try: - # First, let's open the regular texture - vanilla = Image.open(os.path.join(root, infile)) - width, height = vanilla.size - # Second, let's generate a transparent texture that's twice the size - transparent = Image.new("RGBA", [int(2 * s) for s in vanilla.size], (255, 255, 255, 0)) - out = transparent.copy() - - # Now we paste the regular texture in a 3x3 grid, centered in the middle - for x in range(-1, 2): - for y in range(-1, 2): - texture = vanilla - index = (x + 2) + (y + 1) * 3 # Turns coordinates into a number from 1 to 9 - if str(index) in textureMap: # Load texture from texture stitching map - texture = Image.open(textureMap[str(index)]) - out.paste(texture, (int(width / 2 + width * x), int(height / 2 + height * y))) - - # As the last step, we apply our custom mask to round the edges and smoothen things out - mask_location = f"input/masks/{width}px" # If possible, use a mask designed for the texture's size - if not os.path.isdir(mask_location) or len(os.listdir(mask_location)) == 0: mask_location = "input/masks/16px" - random.seed(infile) # Use the filename as a seed. This ensures we always get the same mask per block. - mask_location += f"/{random.choice(os.listdir(mask_location))}" # Choose a random mask to get some variation between the different types of leaves - mask = Image.open(mask_location).convert('L').resize(out.size, resample=Image.NEAREST) - out = Image.composite(out, transparent, mask) - - # Finally, we save the texture to the assets folder - out.save(outfile, vanilla.format) - except IOError: - print("Error while generating texture for '%s'" % infile) - - -def generateBlockstate(leaf, block_state_copies): - mod_namespace = leaf.getId().split(":")[0] - block_name = leaf.getId().split(":")[1] - - block_state_namespace = mod_namespace - block_state_name = block_name - - state = "" - if leaf.blockstate_data != None: # In case custom blockstate data is defined - block_state_namespace = leaf.blockstate_data.namespace - block_state_name = leaf.blockstate_data.block_name - state = leaf.blockstate_data.state - - # Create structure for blockstate file - block_state_file = f"assets/{block_state_namespace}/blockstates/{block_state_name}.json" - block_state_data = { - "variants": { - f"{state}": [] - } - } - if os.path.exists(block_state_file): # In case the blockstate file already exists, we want to add to it - with open(block_state_file, "r") as f: - block_state_data = json.load(f) - if state not in block_state_data["variants"]: block_state_data["variants"][state] = [] - - # Add four rotations for each of the four individual leaf models - for i in range(1, 5): - block_state_data["variants"][state] += { "model": f"{mod_namespace}:block/{block_name}{i}" }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 90 }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 180 }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 270 }, - - # Create blockstates folder if it doesn't exist already - os.makedirs("assets/{}/blockstates/".format(block_state_namespace), exist_ok=True) - - # Write blockstate file - with open(block_state_file, "w") as f: - dumpJson(block_state_data, f) - - # Do the same for the dynamic trees namespace - if leaf.dynamictrees_namespace != None: - dyntrees_block_state_file = f"assets/{leaf.dynamictrees_namespace}/blockstates/{block_name}.json" - os.makedirs("assets/{}/blockstates/".format(leaf.dynamictrees_namespace), exist_ok=True) - - # Write blockstate file - with open(dyntrees_block_state_file, "w") as f: - dumpJson(block_state_data, f) - - # Additional block state copies - if (leaf.getId()) in block_state_copies: - block_state_copy_ids = block_state_copies[leaf.getId()] - if not isinstance(block_state_copy_ids, list): block_state_copy_ids = [block_state_copy_ids] # In case only one blockstate is provided (as a string), turn it into a list - for block_state_copy_id in block_state_copy_ids: - block_state_copy_namespace = block_state_copy_id.split(":")[0] - block_state_copy_name = block_state_copy_id.split(":")[1] - - block_state_copy_file = f"assets/{block_state_copy_namespace}/blockstates/{block_state_copy_name}.json" - os.makedirs("assets/{}/blockstates/".format(block_state_copy_namespace), exist_ok=True) - - # Write blockstate file - with open(block_state_copy_file, "w") as f: - dumpJson(block_state_data, f) - printOverride(f"Writing blockstate copy: {block_state_copy_id}") - - -def generateBlockModels(leaf): - mod_namespace = leaf.getId().split(":")[0] - block_name = leaf.getId().split(":")[1] - # Create models folder if it doesn't exist already - os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) - - # Create the four individual leaf models - for i in range(1, 5): - # Create structure for block model file - block_model_file = f"assets/{mod_namespace}/models/block/{block_name}{i}.json" - block_model_data = { - "parent": f"betterleaves:block/{leaf.base_model}{i}", - "textures": { - "all": f"{leaf.getTextureId()}" - } - } - # Add overlay texture on request - if (leaf.overlay_texture_id != ""): - block_model_data["textures"]["overlay"] = leaf.overlay_texture_id - - # Add additional textures - if (leaf.sprite_overrides): - for key in leaf.sprite_overrides: - block_model_data["textures"][key] = leaf.sprite_overrides[key]; - - # Write block model file - with open(block_model_file, "w") as f: - dumpJson(block_model_data, f) - -def generateItemModel(leaf): - mod_namespace = leaf.getId().split(":")[0] - block_name = leaf.getId().split(":")[1] - - # Create models folder if it doesn't exist already - os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) - - block_item_model_file = f"assets/{mod_namespace}/models/block/{block_name}.json" - - if leaf.has_texture_override: # Used for items that have a different texture than the block model - item_model_data = { - "parent": f"betterleaves:block/{leaf.base_model}", - "textures": { - "all": f"{mod_namespace}:block/{block_name}" - } - } - else: # By default, the regular block texture is used - item_model_data = { - "parent": f"betterleaves:block/{leaf.base_model}", - "textures": { - "all": f"{leaf.getTextureId()}" - } - } - # Add overlay texture on request - if (leaf.overlay_texture_id != ""): - item_model_data["textures"]["overlay"] = leaf.overlay_texture_id - - with open(block_item_model_file, "w") as f: - dumpJson(item_model_data, f) - - if leaf.should_generate_item_model: - # Create models folder if it doesn't exist already - os.makedirs("assets/{}/models/item/".format(mod_namespace), exist_ok=True) - - item_model_file = f"assets/{mod_namespace}/models/item/{block_name}.json" - with open(item_model_file, "w") as f: - dumpJson(item_model_data, f) - -def generateCarpetAssets(carpet): - mod_namespace = carpet.carpet_id.split(":")[0] - block_name = carpet.carpet_id.split(":")[1] - # Create blockstate folder if it doesn't exist already - os.makedirs("assets/{}/blockstates/".format(mod_namespace), exist_ok=True) - - # Create structure for blockstate file - block_state_file = f"assets/{mod_namespace}/blockstates/{block_name}.json" - block_state_data = { - "variants": { - "": [] - } - } - # Add four rotations for the carpet model - block_state_data["variants"][""] += { "model": f"{mod_namespace}:block/{block_name}" }, { "model": f"{mod_namespace}:block/{block_name}", "y": 90 }, { "model": f"{mod_namespace}:block/{block_name}", "y": 180 }, { "model": f"{mod_namespace}:block/{block_name}", "y": 270 }, - - # Write blockstate file - with open(block_state_file, "w") as f: - dumpJson(block_state_data, f) - - # Create models folder if it doesn't exist already - os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) - - # Create structure for block model file - block_model_file = f"assets/{mod_namespace}/models/block/{block_name}.json" - block_model_data = { - "parent": f"betterleaves:block/{carpet.base_model}", - "textures": { - "wool": f"{carpet.leaf.getTextureId()}" - } - } - # Save the carpet block model file - with open(block_model_file, "w") as f: - dumpJson(block_model_data, f) - -def minifyJsonFiles(rootDir="./assets"): - for root, dirs, files in os.walk(rootDir): - for infile in files: - if infile.endswith(".json"): - minifyExistingJson(root, infile) -def minifyExistingJson(root, infile): - with open(os.path.join(root, infile), "r") as rf: - data = json.load(rf) - with open(os.path.join(root, infile), "w") as wf: - json.dump(data, wf, separators=(',', ':')) +from src.generator import autoGen +from src.download_helper import downloadFromModrinth +from src.zip_utils import makeZip +import src.json_utils def writeMetadata(args): edition = args.edition @@ -490,25 +22,6 @@ def writeMetadata(args): line = line.replace("${version}", args.version).replace("${edition}", edition).replace("${year}", str(time.localtime().tm_year)) outfile.write(line) -# See https://stackoverflow.com/a/1855118 -def zipdir(path, ziph): - # ziph is zipfile handle - for root, dirs, files in os.walk(path): - for file in files: - ziph.write(os.path.join(root, file), - os.path.relpath(os.path.join(root, file), - os.path.join(path, '..'))) - -# Creates a compressed zip file -def makeZip(filename, programmer_art=False): - with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf: - zipdir('assets/', zipf) - zipf.write('pack.mcmeta') - zipf.write('pack_programmer_art.png', arcname='pack.png') if programmer_art else zipf.write('pack.png') - zipf.write('LICENSE') - zipf.write('README.md') - - # This is the main entry point, executed when the script is run if __name__ == '__main__': start_time = time.perf_counter() @@ -529,7 +42,7 @@ if __name__ == '__main__': print("Motschen's Better Leaves Lite") print("https://github.com/TeamMidnightDust/BetterLeavesLite") print() - if args.minify: minify = True + if args.minify: src.json_utils.minify = True if args.download != None: downloadFromModrinth(args.download) # Loads overrides from the json file diff --git a/src/betterleaves_json.py b/src/betterleaves_json.py new file mode 100644 index 0000000..44abefb --- /dev/null +++ b/src/betterleaves_json.py @@ -0,0 +1,13 @@ +import json +import os +from src.data.leafblock import LeafBlock +from src.data.blockstate_data import BlockStateData + +def applyJson(leaf: LeafBlock, root, infile, files): + if infile.replace(".png", ".betterleaves.json") in files: + with open(os.path.join(root, infile.replace(".png", ".betterleaves.json")), "r") as f: + jsonFile = json.load(f) + if "blockStateData" in jsonFile: + leaf.blockstate_data = BlockStateData.fromFile(leaf, root, infile.replace(".png", ".betterleaves.json")) + if "spriteOverrides" in jsonFile: + leaf.sprite_overrides = jsonFile["spriteOverrides"] diff --git a/src/blockstate_generator.py b/src/blockstate_generator.py new file mode 100644 index 0000000..1f76a54 --- /dev/null +++ b/src/blockstate_generator.py @@ -0,0 +1,67 @@ +import os +import json + +from src.json_utils import dumpJson +from src.utilities import printOverride + +def generateBlockstate(leaf, block_state_copies): + mod_namespace = leaf.getId().split(":")[0] + block_name = leaf.getId().split(":")[1] + + block_state_namespace = mod_namespace + block_state_name = block_name + + state = "" + if leaf.blockstate_data != None: # In case custom blockstate data is defined + block_state_namespace = leaf.blockstate_data.namespace + block_state_name = leaf.blockstate_data.block_name + state = leaf.blockstate_data.state + + # Create structure for blockstate file + block_state_file = f"assets/{block_state_namespace}/blockstates/{block_state_name}.json" + block_state_data = { + "variants": { + f"{state}": [] + } + } + + if os.path.exists(block_state_file): # In case the blockstate file already exists, we want to add to it + with open(block_state_file, "r") as f: + block_state_data = json.load(f) + if state not in block_state_data["variants"]: block_state_data["variants"][state] = [] + + # Add four rotations for each of the four individual leaf models + for i in range(1, 5): + block_state_data["variants"][state] += { "model": f"{mod_namespace}:block/{block_name}{i}" }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 90 }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 180 }, { "model": f"{mod_namespace}:block/{block_name}{i}", "y": 270 }, + + # Create blockstates folder if it doesn't exist already + os.makedirs("assets/{}/blockstates/".format(block_state_namespace), exist_ok=True) + + # Write blockstate file + with open(block_state_file, "w") as f: + dumpJson(block_state_data, f) + + # Do the same for the dynamic trees namespace + if leaf.dynamictrees_namespace != None: + dyntrees_block_state_file = f"assets/{leaf.dynamictrees_namespace}/blockstates/{block_name}.json" + os.makedirs("assets/{}/blockstates/".format(leaf.dynamictrees_namespace), exist_ok=True) + + # Write blockstate file + with open(dyntrees_block_state_file, "w") as f: + dumpJson(block_state_data, f) + + # Additional block state copies + if (leaf.getId()) in block_state_copies: + block_state_copy_ids = block_state_copies[leaf.getId()] + if not isinstance(block_state_copy_ids, list): block_state_copy_ids = [block_state_copy_ids] # In case only one blockstate is provided (as a string), turn it into a list + for block_state_copy_id in block_state_copy_ids: + block_state_copy_namespace = block_state_copy_id.split(":")[0] + block_state_copy_name = block_state_copy_id.split(":")[1] + + block_state_copy_file = f"assets/{block_state_copy_namespace}/blockstates/{block_state_copy_name}.json" + os.makedirs("assets/{}/blockstates/".format(block_state_copy_namespace), exist_ok=True) + + # Write blockstate file + with open(block_state_copy_file, "w") as f: + dumpJson(block_state_data, f) + printOverride(f"Writing blockstate copy: {block_state_copy_id}") diff --git a/src/carpet_generator.py b/src/carpet_generator.py new file mode 100644 index 0000000..b822c14 --- /dev/null +++ b/src/carpet_generator.py @@ -0,0 +1,37 @@ +import os +from src.json_utils import dumpJson + +def generateCarpetAssets(carpet): + mod_namespace = carpet.carpet_id.split(":")[0] + block_name = carpet.carpet_id.split(":")[1] + # Create blockstate folder if it doesn't exist already + os.makedirs("assets/{}/blockstates/".format(mod_namespace), exist_ok=True) + + # Create structure for blockstate file + block_state_file = f"assets/{mod_namespace}/blockstates/{block_name}.json" + block_state_data = { + "variants": { + "": [] + } + } + # Add four rotations for the carpet model + block_state_data["variants"][""] += { "model": f"{mod_namespace}:block/{block_name}" }, { "model": f"{mod_namespace}:block/{block_name}", "y": 90 }, { "model": f"{mod_namespace}:block/{block_name}", "y": 180 }, { "model": f"{mod_namespace}:block/{block_name}", "y": 270 }, + + # Write blockstate file + with open(block_state_file, "w") as f: + dumpJson(block_state_data, f) + + # Create models folder if it doesn't exist already + os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) + + # Create structure for block model file + block_model_file = f"assets/{mod_namespace}/models/block/{block_name}.json" + block_model_data = { + "parent": f"betterleaves:block/{carpet.base_model}", + "textures": { + "wool": f"{carpet.leaf.getTextureId()}" + } + } + # Save the carpet block model file + with open(block_model_file, "w") as f: + dumpJson(block_model_data, f) diff --git a/src/data/blockstate_data.py b/src/data/blockstate_data.py new file mode 100644 index 0000000..5dfdb9c --- /dev/null +++ b/src/data/blockstate_data.py @@ -0,0 +1,21 @@ +import os +import json + +from src.data.leafblock import LeafBlock +from src.utilities import printOverride + +class BlockStateData: + def __init__(self, namespace, block_name, state): + self.namespace = namespace + self.block_name = block_name + self.state = state + + @classmethod # https://stackoverflow.com/a/682545 + def fromJson(cls, leaf: LeafBlock, data): + return cls(data["block"].split(":")[0], data["block"].split(":")[1], data["state"]) if "block" in data else cls(leaf.getId().split(":")[0], leaf.getId().split(":")[1], data["state"]) + + @classmethod + def fromFile(cls, leaf: LeafBlock, root, infile): + with open(os.path.join(root, infile), "r") as f: + printOverride("Loading blockstate data from: "+f.name) + return BlockStateData.fromJson(leaf, json.load(f).get("blockStateData")) diff --git a/src/data/carpetblock.py b/src/data/carpetblock.py new file mode 100644 index 0000000..1172c1a --- /dev/null +++ b/src/data/carpetblock.py @@ -0,0 +1,7 @@ +class CarpetBlock: + def __init__(self, carpet_id, leaf): + self.carpet_id = carpet_id + self.leaf = leaf + if (leaf.has_no_tint): self.base_model = "leaf_carpet_notint" + + base_model = "leaf_carpet" diff --git a/src/data/leafblock.py b/src/data/leafblock.py new file mode 100644 index 0000000..f9669e6 --- /dev/null +++ b/src/data/leafblock.py @@ -0,0 +1,27 @@ +class LeafBlock: + def __init__(self, namespace, block_name, texture_name): + self.namespace = namespace + self.block_name = block_name + self.texture_name = texture_name + + base_model = "leaves" + has_carpet = False + has_no_tint = False + has_texture_override = False + should_generate_item_model = False + use_legacy_model = False + texture_prefix = "" + overlay_texture_id = "" + block_id_override = None + texture_id_override = None + dynamictrees_namespace = None + blockstate_data = None + sprite_overrides = None + + def getId(self): + if (self.block_id_override != None): return self.block_id_override + return self.namespace+":"+self.block_name + + def getTextureId(self): + if (self.texture_id_override != None): return self.texture_id_override + return self.namespace+":block/"+self.texture_prefix+self.texture_name diff --git a/download_helper.py b/src/download_helper.py similarity index 100% rename from download_helper.py rename to src/download_helper.py diff --git a/src/generator.py b/src/generator.py new file mode 100644 index 0000000..860ef8c --- /dev/null +++ b/src/generator.py @@ -0,0 +1,149 @@ +# Depencency imports +import os +import shutil +from PIL import Image +from setuptools._distutils.dir_util import copy_tree + +# Local imports +from src.data.leafblock import LeafBlock +from src.data.carpetblock import CarpetBlock +from src.mod_utils import unpackMods, cleanupMods, scanModsForTextures +from src.texturepack_utils import unpackTexturepacks, cleanupTexturepacks +from src.utilities import printCyan, printGreen, printOverride +from src.texture_generator import generateTexture +from src.model_generator import generateBlockModels, generateItemModel +from src.blockstate_generator import generateBlockstate +from src.carpet_generator import generateCarpetAssets +from src.json_utils import minifyJsonFiles, minify +from src.betterleaves_json import applyJson + +# This is where the magic happens +def autoGen(jsonData, args): + print("Generating assets...") + if (os.path.exists("./assets")): shutil.rmtree("./assets") + copy_tree("./base/assets/", "./assets/") + if minify: minifyJsonFiles() + + filecount = 0 + if (args.programmer): unpackTexturepacks("./input/programmer_art") + unpackTexturepacks() + unpackMods() + scanModsForTextures() + + for root, dirs, files in os.walk("./input/assets"): + for infile in files: + if infile.endswith(".png") and (len(root.split("/")) > 3): + filecount += processLeaf(root, files, infile, jsonData, args) + + print() + if (args.programmer): cleanupTexturepacks("./input/programmer_art") + cleanupTexturepacks() + cleanupMods() + printCyan("Processed {} leaf blocks".format(filecount)) + +def processLeaf(root, files, infile, jsonData, args) -> int: + texture_name = infile.replace(".png", "") + leaf = LeafBlock(root.split("/")[3], texture_name, texture_name) + + notint_overrides = jsonData["noTint"] + block_texture_overrides = jsonData["blockTextures"] + overlay_textures = jsonData["overlayTextures"] + compileonly_textures = jsonData["compileOnly"] + block_id_overrides = jsonData["blockIds"] + leaves_with_carpet = jsonData["leavesWithCarpet"] + dynamictrees_namespaces = jsonData["dynamicTreesNamespaces"] + generate_itemmodels_overrides = jsonData["generateItemModels"] + block_state_copies = jsonData["blockStateCopies"] + + # Handle leaf textures in subfolders + if (len(root.split("/")) > 6): + leaf.texture_prefix = root.split("/")[6]+"/" + if (leaf.block_name == "leaves"): # For mods that use a structure like "texture/woodtype/leaves.png" + leaf.block_name = leaf.texture_prefix.replace("/", "_")+leaf.block_name + printGreen(leaf.getId()) + printOverride("Auto-redirected from "+leaf.getId()) + else: # For mods that use a structure like "texture/natural/some_leaves.png" + printGreen(leaf.getId()) + printOverride("Prefix: "+ leaf.texture_prefix); + else: printGreen(leaf.getId()) + + # We don't want to generate assets for compile-only or overlay textures + if leaf.getTextureId() in compileonly_textures or leaf.getTextureId() in overlay_textures.values(): + printOverride(f"Skipping {"compile-only" if leaf.getTextureId() in compileonly_textures else "overlay"} texture") + return 0 + + leaf.use_legacy_model = shouldUseLegacyModel(leaf, root, infile, args) + + # Generate texture + if not leaf.use_legacy_model: generateTexture(root, infile, args.programmer) + + # Set block id and apply overrides + if leaf.getId() in block_id_overrides: + leaf.block_id_override = block_id_overrides[leaf.getId()] + printOverride("ID Override: "+leaf.getId()) + + # Set texture id and apply overrides + leaf.has_texture_override = leaf.getId() in block_texture_overrides + if leaf.has_texture_override: + leaf.texture_id_override = block_texture_overrides[leaf.getId()] + printOverride("Texture Override: "+leaf.getTextureId()) + + # Check if the block appears in the notint overrides + leaf.has_no_tint = leaf.getId() in notint_overrides + if leaf.use_legacy_model: + leaf.base_model = "leaves_legacy" + elif leaf.has_no_tint: + leaf.base_model = "leaves_notint" + printOverride("No tint") + + # Check if the block has an additional overlay texture + if leaf.getId() in overlay_textures: + leaf.base_model = "leaves_overlay" + leaf.overlay_texture_id = overlay_textures[leaf.getId()] + printOverride("Has overlay texture: "+leaf.overlay_texture_id) + + # Check if the block has a dynamic trees addon namespace + + if (leaf.namespace) in dynamictrees_namespaces: + leaf.dynamictrees_namespace = dynamictrees_namespaces[leaf.namespace] + + # Check if the block should generate an item model + if leaf.getId() in generate_itemmodels_overrides: + leaf.should_generate_item_model = True + printOverride("Also generating item model") + + # Check for blockstate data + applyJson(leaf, root, infile, files) + + # Generate blockstates & models + generateBlockstate(leaf, block_state_copies) + generateBlockModels(leaf) + generateItemModel(leaf) + + # Certain mods contain leaf carpets. + # Because we change the leaf texture, we need to fix the carpet models. + generateCarpet(leaf, leaves_with_carpet) + + return 1 + +def shouldUseLegacyModel(leaf, root, infile, args) -> bool: + texture = Image.open(os.path.join(root, infile)) + if texture.size[0] != texture.size[1]: + printOverride("Animated – using legacy model") + return True + if args.legacy: + printOverride("Using legacy model as requested") + return True + return False + +def generateCarpet(leaf, leaves_with_carpet): + if (leaf.getId()) not in leaves_with_carpet: return + + carpet_ids = leaves_with_carpet[leaf.getId()] + # In case only one carpet is provided (as a string), turn it into a list + if not isinstance(carpet_ids, list): carpet_ids = [carpet_ids] + + for carpet_id in carpet_ids: + carpet = CarpetBlock(carpet_id, leaf) + generateCarpetAssets(carpet) + printOverride(f"Generating leaf carpet: {carpet.carpet_id}") diff --git a/src/json_utils.py b/src/json_utils.py new file mode 100644 index 0000000..5dea74c --- /dev/null +++ b/src/json_utils.py @@ -0,0 +1,18 @@ +import os +import json + +minify = False + +def minifyJsonFiles(rootDir="./assets"): + for root, dirs, files in os.walk(rootDir): + for infile in files: + if infile.endswith(".json"): + minifyExistingJson(root, infile) +def minifyExistingJson(root, infile): + with open(os.path.join(root, infile), "r") as rf: + data = json.load(rf) + with open(os.path.join(root, infile), "w") as wf: + json.dump(data, wf, separators=(',', ':')) + +def dumpJson(data, f): + json.dump(data, f, separators=(',', ':')) if minify else json.dump(data, f, indent=4) diff --git a/src/mod_utils.py b/src/mod_utils.py new file mode 100644 index 0000000..1eae6cb --- /dev/null +++ b/src/mod_utils.py @@ -0,0 +1,28 @@ +import os +import zipfile +import shutil + +def unpackMods(): + for root, dirs, files in os.walk("./input/mods"): + for infile in files: + if infile.endswith(".jar"): + print("Unpacking mod: "+infile) + zf = zipfile.ZipFile(os.path.join(root, infile), 'r') + zf.extractall(os.path.join(root, infile.replace(".jar", "_temp"))) + zf.close() + +def cleanupMods(): + if (os.path.exists("./input/mods")): shutil.rmtree("./input/mods") + os.makedirs("./input/mods") + +def scanModsForTextures(): + for root, dirs, files in os.walk("./input/mods"): + for infile in files: + if len(root.split("assets")) > 1: + assetpath = root.split("assets")[1][1:] + modid = assetpath.split("textures")[0].replace("/", "") + if "textures/block" in root and infile.endswith(".png") and "leaves" in infile: + print(f"Found texture {assetpath}/{infile} in mod {modid}") + inputfolder = os.path.join("./input/assets/", assetpath) + os.makedirs(inputfolder, exist_ok=True) + shutil.copyfile(os.path.join(root, infile), os.path.join(inputfolder, infile)) diff --git a/src/model_generator.py b/src/model_generator.py new file mode 100644 index 0000000..88effa0 --- /dev/null +++ b/src/model_generator.py @@ -0,0 +1,69 @@ +import os +from src.json_utils import dumpJson + +def generateBlockModels(leaf): + mod_namespace = leaf.getId().split(":")[0] + block_name = leaf.getId().split(":")[1] + # Create models folder if it doesn't exist already + os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) + + # Create the four individual leaf models + for i in range(1, 5): + # Create structure for block model file + block_model_file = f"assets/{mod_namespace}/models/block/{block_name}{i}.json" + block_model_data = { + "parent": f"betterleaves:block/{leaf.base_model}{i}", + "textures": { + "all": f"{leaf.getTextureId()}" + } + } + # Add overlay texture on request + if (leaf.overlay_texture_id != ""): + block_model_data["textures"]["overlay"] = leaf.overlay_texture_id + + # Add additional textures + if (leaf.sprite_overrides): + for key in leaf.sprite_overrides: + block_model_data["textures"][key] = leaf.sprite_overrides[key]; + + # Write block model file + with open(block_model_file, "w") as f: + dumpJson(block_model_data, f) + +def generateItemModel(leaf): + mod_namespace = leaf.getId().split(":")[0] + block_name = leaf.getId().split(":")[1] + + # Create models folder if it doesn't exist already + os.makedirs("assets/{}/models/block/".format(mod_namespace), exist_ok=True) + + block_item_model_file = f"assets/{mod_namespace}/models/block/{block_name}.json" + + if leaf.has_texture_override: # Used for items that have a different texture than the block model + item_model_data = { + "parent": f"betterleaves:block/{leaf.base_model}", + "textures": { + "all": f"{mod_namespace}:block/{block_name}" + } + } + else: # By default, the regular block texture is used + item_model_data = { + "parent": f"betterleaves:block/{leaf.base_model}", + "textures": { + "all": f"{leaf.getTextureId()}" + } + } + # Add overlay texture on request + if (leaf.overlay_texture_id != ""): + item_model_data["textures"]["overlay"] = leaf.overlay_texture_id + + with open(block_item_model_file, "w") as f: + dumpJson(item_model_data, f) + + if leaf.should_generate_item_model: + # Create models folder if it doesn't exist already + os.makedirs("assets/{}/models/item/".format(mod_namespace), exist_ok=True) + + item_model_file = f"assets/{mod_namespace}/models/item/{block_name}.json" + with open(item_model_file, "w") as f: + dumpJson(item_model_data, f) diff --git a/src/texture_generator.py b/src/texture_generator.py new file mode 100644 index 0000000..105858b --- /dev/null +++ b/src/texture_generator.py @@ -0,0 +1,77 @@ +import json +import os +import random +from PIL import Image + +# Local imports +from src.texturepack_utils import scanPacksForTexture +from src.utilities import printOverride + +def generateTexture(root, infile, useProgrammerArt=False): + outfolder = root.replace("assets", "").replace("input", "assets") + os.makedirs(outfolder, exist_ok=True) + + # Check for texture stitching data + textureMap = createTextureMap(root, infile, useProgrammerArt) + + root = scanPacksForTexture(root, infile) + if useProgrammerArt: root = scanPacksForTexture(root, infile, "./input/programmer_art") + + outfile = os.path.splitext(os.path.join(outfolder, infile))[0] + ".png" + if infile != outfile: + try: + stitchTexture(textureMap, root, infile, outfile) + except IOError: + print("Error while generating texture for '%s'" % infile) + +def createTextureMap(root, infile, useProgrammerArt): + textureMap = {} + if os.path.isfile(os.path.join(root, infile.replace(".png", ".betterleaves.json"))): + with open(os.path.join(root, infile.replace(".png", ".betterleaves.json")), "r") as f: + json_data = json.load(f) + if "textureStitching" in json_data: + printOverride("Using texture stitching data from: " + f.name) + # Create texture map from stitching data + for key, value in json_data["textureStitching"].items(): + if "-" in key: + for i in range(int(key.split("-")[0]), int(key.split("-")[1])+1): textureMap[str(i)] = value + else: textureMap[key] = value + # Turn texture map into absolute paths + for key, value in textureMap.items(): + textureRoot = f"./input/assets/{value.split(':')[0]}/textures/" + textureFile = value.split(":")[1] + ".png" + if "/" in textureFile: + textureRoot += textureFile.rsplit("/")[0] + textureFile = textureFile[len(textureFile.rsplit("/")[0])+1:] # The rest of the string, starting behind the first '/' + textureRoot = scanPacksForTexture(textureRoot, textureFile) + if useProgrammerArt: root = scanPacksForTexture(textureRoot, textureFile, "./input/programmer_art") + textureMap[key] = os.path.join(textureRoot, textureFile) + return textureMap + +def stitchTexture(textureMap, root, infile, outfile): + # First, let's open the regular texture + vanilla = Image.open(os.path.join(root, infile)) + width, height = vanilla.size + # Second, let's generate a transparent texture that's twice the size + transparent = Image.new("RGBA", [int(2 * s) for s in vanilla.size], (255, 255, 255, 0)) + out = transparent.copy() + + # Now we paste the regular texture in a 3x3 grid, centered in the middle + for x in range(-1, 2): + for y in range(-1, 2): + texture = vanilla + index = (x + 2) + (y + 1) * 3 # Turns coordinates into a number from 1 to 9 + if str(index) in textureMap: # Load texture from texture stitching map + texture = Image.open(textureMap[str(index)]) + out.paste(texture, (int(width / 2 + width * x), int(height / 2 + height * y))) + + # As the last step, we apply our custom mask to round the edges and smoothen things out + mask_location = f"input/masks/{width}px" # If possible, use a mask designed for the texture's size + if not os.path.isdir(mask_location) or len(os.listdir(mask_location)) == 0: mask_location = "input/masks/16px" + random.seed(infile) # Use the filename as a seed. This ensures we always get the same mask per block. + mask_location += f"/{random.choice(os.listdir(mask_location))}" # Choose a random mask to get some variation between the different types of leaves + mask = Image.open(mask_location).convert('L').resize(out.size, resample=Image.NEAREST) + out = Image.composite(out, transparent, mask) + + # Finally, we save the texture to the assets folder + out.save(outfile, vanilla.format) diff --git a/src/texturepack_utils.py b/src/texturepack_utils.py new file mode 100644 index 0000000..562a9a8 --- /dev/null +++ b/src/texturepack_utils.py @@ -0,0 +1,28 @@ +import os +import zipfile +import shutil +from src.utilities import printCyan + +def unpackTexturepacks(rootFolder="./input/texturepacks"): + for root, dirs, files in os.walk(rootFolder): + for infile in files: + if infile.endswith(".zip"): + print("Unpacking texturepack: "+infile) + zf = zipfile.ZipFile(os.path.join(root, infile), 'r') + zf.extractall(os.path.join(root, infile.replace(".zip", "_temp"))) + zf.close() + +def cleanupTexturepacks(rootFolder="./input/texturepacks"): + for root, dirs, files in os.walk(rootFolder): + for folder in dirs: + if folder.endswith("_temp"): + shutil.rmtree(os.path.join(root, folder)) + +def scanPacksForTexture(baseRoot, baseInfile, rootFolder="./input/texturepacks"): + for root, dirs, files in os.walk(rootFolder): + for infile in files: + if "assets" in root and "assets" in baseRoot: + if infile.endswith(".png") and (len(root.split("/")) > 3) and (baseInfile == infile) and (root.split("assets")[1] == baseRoot.split("assets")[1]): + printCyan(" Using texture from: " + root.split("assets")[0].replace(rootFolder, "")) + return root; + return baseRoot diff --git a/src/utilities.py b/src/utilities.py new file mode 100644 index 0000000..b61efae --- /dev/null +++ b/src/utilities.py @@ -0,0 +1,3 @@ +def printGreen(out): print("\033[92m{}\033[00m".format(out)) +def printCyan(out): print("\033[96m{}\033[00m" .format(out)) +def printOverride(out): print(" -> {}".format(out)) diff --git a/src/zip_utils.py b/src/zip_utils.py new file mode 100644 index 0000000..ef97d54 --- /dev/null +++ b/src/zip_utils.py @@ -0,0 +1,20 @@ +import os +import zipfile + +# See https://stackoverflow.com/a/1855118 +def zipdir(path, ziph): + # ziph is zipfile handle + for root, dirs, files in os.walk(path): + for file in files: + ziph.write(os.path.join(root, file), + os.path.relpath(os.path.join(root, file), + os.path.join(path, '..'))) + +# Creates a compressed zip file +def makeZip(filename, programmer_art=False): + with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf: + zipdir('assets/', zipf) + zipf.write('pack.mcmeta') + zipf.write('pack_programmer_art.png', arcname='pack.png') if programmer_art else zipf.write('pack.png') + zipf.write('LICENSE') + zipf.write('README.md')