From ddfbd331c9b61980f58aeac790ca6a63327feb5f Mon Sep 17 00:00:00 2001 From: James David Clarke Date: Wed, 10 Jan 2024 08:16:36 +0000 Subject: [PATCH] Refactor convert_textures code, seperated special cases into its own module --- tools/Texture_Converter.py | 2 +- tools/libtextureconverter/common.py | 33 +- tools/libtextureconverter/convert.py | 335 ++----- tools/libtextureconverter/gui.py | 65 +- .../special_convert_cases.py | 817 ++++++++++++++++++ tools/libtextureconverter/utils.py | 100 ++- 6 files changed, 1027 insertions(+), 325 deletions(-) create mode 100644 tools/libtextureconverter/special_convert_cases.py diff --git a/tools/Texture_Converter.py b/tools/Texture_Converter.py index 2e47baf28..a0eef8565 100755 --- a/tools/Texture_Converter.py +++ b/tools/Texture_Converter.py @@ -36,7 +36,7 @@ def main(): print(f"ERROR: No valid resource packs specified. Use '{appname} -h' for help.") sys.exit(2) - convert_resource_packs(resource_packs, args.output, args.pixelsize, args.dry_run, args.verbose, make_texture_pack) + convert_resource_packs(resource_packs, args.output, args.pixel_size, args.dry_run, args.verbose, make_texture_pack) if __name__ == "__main__": main() diff --git a/tools/libtextureconverter/common.py b/tools/libtextureconverter/common.py index 863fcc606..1c28efdda 100644 --- a/tools/libtextureconverter/common.py +++ b/tools/libtextureconverter/common.py @@ -1,4 +1,10 @@ -import shutil, csv, os, tempfile, sys, argparse, glob +import shutil +import csv +import os +import tempfile +import sys +import argparse +import glob from PIL import Image from collections import Counter @@ -6,7 +12,14 @@ from libtextureconverter.utils import detect_pixel_size, target_dir, colorize, c from libtextureconverter.convert import convert_textures from libtextureconverter.config import SUPPORTED_MINECRAFT_VERSION, working_dir, mineclone2_path, appname, home -def convert_resource_packs(resource_packs, output_dir, PXSIZE, dry_run, verbose, make_texture_pack): + +def convert_resource_packs( + resource_packs, + output_dir, + PXSIZE, + dry_run, + verbose, + make_texture_pack): for base_dir in resource_packs: print(f"Converting resource pack: {base_dir}") @@ -18,7 +31,8 @@ def convert_resource_packs(resource_packs, output_dir, PXSIZE, dry_run, verbose, # Construct the path to the textures within the resource pack tex_dir = os.path.join(base_dir, "assets", "minecraft", "textures") - # Determine the name of the output directory for the converted texture pack + # Determine the name of the output directory for the converted texture + # pack output_dir_name = os.path.basename(os.path.normpath(base_dir)) # Create the output directory if it doesn't exist @@ -32,7 +46,18 @@ def convert_resource_packs(resource_packs, output_dir, PXSIZE, dry_run, verbose, try: # Perform the actual conversion - convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, pixel_size) + convert_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + pixel_size) finally: # Clean up temporary files tempfile1.close() diff --git a/tools/libtextureconverter/convert.py b/tools/libtextureconverter/convert.py index aea137877..f664ba7ef 100644 --- a/tools/libtextureconverter/convert.py +++ b/tools/libtextureconverter/convert.py @@ -1,11 +1,27 @@ +from .special_convert_cases import convert_map_textures, convert_armor_textures, convert_chest_textures, convert_rail_textures, convert_banner_overlays, convert_grass_textures from .utils import target_dir, colorize, colorize_alpha -import shutil, csv, os, tempfile, sys, argparse, glob +import shutil +import csv +import os +import tempfile +import sys +import argparse +import glob -# Copy texture files -def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE): +def convert_standard_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): failed_conversions = 0 - print("Texture conversion BEGINS NOW!") with open("Conversion_Table.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",", quotechar='"') first_row = True @@ -14,7 +30,6 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem if first_row: first_row = False continue - src_dir = row[0] src_filename = row[1] dst_dir = './textures' @@ -41,19 +56,21 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem # Example: textures for mcl_supplemental continue - src_file = base_dir + src_dir + "/" + src_filename # source file + src_file = base_dir + src_dir + "/" + src_filename # source file src_file_exists = os.path.isfile(src_file) - dst_file = target_dir(dst_dir, make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/" + dst_filename # destination file + dst_file = target_dir(dst_dir, make_texture_pack, output_dir, output_dir_name, + mineclone2_path) + "/" + dst_filename # destination file if src_file_exists == False: - print("WARNING: Source file does not exist: "+src_file) + print("WARNING: Source file does not exist: " + src_file) failed_conversions = failed_conversions + 1 continue if xs != None: # Crop and copy images if not dry_run: - os.system("convert "+src_file+" -crop "+xl+"x"+yl+"+"+xs+"+"+ys+" "+dst_file) + os.system("convert " + src_file + " -crop " + xl + + "x" + yl + "+" + xs + "+" + ys + " " + dst_file) if verbose: print(src_file + " → " + dst_file) else: @@ -62,280 +79,50 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem shutil.copy2(src_file, dst_file) if verbose: print(src_file + " → " + dst_file) - - # Convert map background - map_background_file = tex_dir + "/map/map_background.png" - if os.path.isfile(map_background_file): - os.system("convert " + map_background_file + " -interpolate Integer -filter point -resize \"140x140\" " + target_dir("/mods/ITEMS/mcl_maps/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/mcl_maps_map_background.png") - - # Convert armor textures (requires ImageMagick) - armor_files = [ - [ tex_dir + "/models/armor/leather_layer_1.png", tex_dir + "/models/armor/leather_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_leather.png", "mcl_armor_chestplate_leather.png", "mcl_armor_leggings_leather.png", "mcl_armor_boots_leather.png" ], - [ tex_dir + "/models/armor/chainmail_layer_1.png", tex_dir + "/models/armor/chainmail_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_chain.png", "mcl_armor_chestplate_chain.png", "mcl_armor_leggings_chain.png", "mcl_armor_boots_chain.png" ], - [ tex_dir + "/models/armor/gold_layer_1.png", tex_dir + "/models/armor/gold_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_gold.png", "mcl_armor_chestplate_gold.png", "mcl_armor_leggings_gold.png", "mcl_armor_boots_gold.png" ], - [ tex_dir + "/models/armor/iron_layer_1.png", tex_dir + "/models/armor/iron_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_iron.png", "mcl_armor_chestplate_iron.png", "mcl_armor_leggings_iron.png", "mcl_armor_boots_iron.png" ], - [ tex_dir + "/models/armor/diamond_layer_1.png", tex_dir + "/models/armor/diamond_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_diamond.png", "mcl_armor_chestplate_diamond.png", "mcl_armor_leggings_diamond.png", "mcl_armor_boots_diamond.png" ], - [ tex_dir + "/models/armor/netherite_layer_1.png", tex_dir + "/models/armor/netherite_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_netherite.png", "mcl_armor_chestplate_netherite.png", "mcl_armor_leggings_netherite.png", "mcl_armor_boots_netherite.png" ] - ] - for a in armor_files: - APXSIZE = 16 # for some reason MineClone2 requires this - layer_1 = a[0] - layer_2 = a[1] - adir = a[2] - if os.path.isfile(layer_1): - helmet = adir + "/" + a[3] - chestplate = adir + "/" + a[4] - boots = adir + "/" + a[6] - os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE * 2)+"+0 -crop "+str(APXSIZE * 2)+"x"+str(APXSIZE)+"+0+0 \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+helmet) - os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE)+"+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+"+str(APXSIZE)+"+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+chestplate) - os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+boots) - if os.path.isfile(layer_2): - leggings = adir + "/" + a[5] - os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_2+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+leggings) - - # Convert chest textures (requires ImageMagick) - chest_files = [ - [ tex_dir + "/entity/chest/normal.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "default_chest_top.png", "mcl_chests_chest_bottom.png", "default_chest_front.png", "mcl_chests_chest_left.png", "mcl_chests_chest_right.png", "mcl_chests_chest_back.png" ], - [ tex_dir + "/entity/chest/trapped.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png", "mcl_chests_chest_trapped_front.png", "mcl_chests_chest_trapped_left.png", "mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_back.png" ], - [ tex_dir + "/entity/chest/ender.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png", "mcl_chests_ender_chest_front.png", "mcl_chests_ender_chest_left.png", "mcl_chests_ender_chest_right.png", "mcl_chests_ender_chest_back.png" ] - ] - - for c in chest_files: - chest_file = c[0] - if os.path.isfile(chest_file): - PPX = (PXSIZE/16) - CHPX = (PPX * 14) # Chest width - LIDPX = (PPX * 5) # Lid height - LIDLOW = (PPX * 10) # Lower lid section height - LOCKW = (PPX * 6) # Lock width - LOCKH = (PPX * 5) # Lock height - - cdir = c[1] - top = cdir + "/" + c[2] - bottom = cdir + "/" + c[3] - front = cdir + "/" + c[4] - left = cdir + "/" + c[5] - right = cdir + "/" + c[6] - back = cdir + "/" + c[7] - # Top - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX)+"x"+str(CHPX)+" "+top) - # Bottom - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(CHPX)+"+"+str(CHPX*2)+"+"+str(CHPX+LIDPX)+" \) -geometry +0+0 -composite -extent "+str(CHPX)+"x"+str(CHPX)+" "+bottom) - # Front - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \ --extent "+str(CHPX)+"x"+str(CHPX)+" "+front) - # TODO: Add lock - - # Left, right back (use same texture, we're lazy - files = [ left, right, back ] - for f in files: - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \ --extent "+str(CHPX)+"x"+str(CHPX)+" "+f) - - # Double chests - - chest_files = [ - [ tex_dir + "/entity/chest/normal_double.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "default_chest_front_big.png", "default_chest_top_big.png", "default_chest_side_big.png" ], - [ tex_dir + "/entity/chest/trapped_double.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_chest_trapped_front_big.png", "mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_side_big.png" ] - ] - for c in chest_files: - chest_file = c[0] - if os.path.isfile(chest_file): - PPX = (PXSIZE/16) - CHPX = (PPX * 14) # Chest width (short side) - CHPX2 = (PPX * 15) # Chest width (long side) - LIDPX = (PPX * 5) # Lid height - LIDLOW = (PPX * 10) # Lower lid section height - LOCKW = (PPX * 6) # Lock width - LOCKH = (PPX * 5) # Lock height - - cdir = c[1] - front = cdir + "/" + c[2] - top = cdir + "/" + c[3] - side = cdir + "/" + c[4] - # Top - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX2)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX2)+"x"+str(CHPX)+" "+top) - # Front - # TODO: Add lock - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \ -\( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \ --extent "+str(CHPX2)+"x"+str(CHPX)+" "+front) - # Side - os.system("convert " + chest_file + " \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \ -\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \ --extent "+str(CHPX)+"x"+str(CHPX)+" "+side) + return failed_conversions - # Generate railway crossings and t-junctions. Note: They may look strange. - # Note: these may be only a temporary solution, as crossings and t-junctions do not occour in MC. - # TODO: Curves - rails = [ - # (Straigt src, curved src, t-junction dest, crossing dest) - ("rail.png", "rail_corner.png", "default_rail_t_junction.png", "default_rail_crossing.png"), - ("powered_rail.png", "rail_corner.png", "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"), - ("powered_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"), - ("detector_rail.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"), - ("detector_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"), - ("activator_rail.png", "rail_corner.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"), - ("activator_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_activator_d_t_junction.png", "mcl_minecarts_rail_activator_powered_crossing.png"), - ] - for r in rails: - os.system("composite -compose Dst_Over "+tex_dir+"/block/"+r[0]+" "+tex_dir+"/block/"+r[1]+" "+target_dir("/mods/ENTITIES/mcl_minecarts/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/"+r[2]) - os.system("convert "+tex_dir+"/block/"+r[0]+" -rotate 90 "+tempfile1.name) - os.system("composite -compose Dst_Over "+tempfile1.name+" "+tex_dir+"/block/"+r[0]+" "+target_dir("/mods/ENTITIES/mcl_minecarts/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/"+r[3]) +def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE): + print("Texture conversion BEGINS NOW!") - # Convert banner overlays - overlays = [ - "base", - "border", - "bricks", - "circle", - "creeper", - "cross", - "curly_border", - "diagonal_left", - "diagonal_right", - "diagonal_up_left", - "diagonal_up_right", - "flower", - "gradient", - "gradient_up", - "half_horizontal_bottom", - "half_horizontal", - "half_vertical", - "half_vertical_right", - "rhombus", - "mojang", - "skull", - "small_stripes", - "straight_cross", - "stripe_bottom", - "stripe_center", - "stripe_downleft", - "stripe_downright", - "stripe_left", - "stripe_middle", - "stripe_right", - "stripe_top", - "square_bottom_left", - "square_bottom_right", - "square_top_left", - "square_top_right", - "triangle_bottom", - "triangles_bottom", - "triangle_top", - "triangles_top", - ] - for o in overlays: - orig = tex_dir + "/entity/banner/" + o + ".png" - if os.path.isfile(orig): - if o == "mojang": - o = "thing" - dest = target_dir("/mods/ITEMS/mcl_banners/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/"+"mcl_banners_"+o+".png" - os.system("convert "+orig+" -transparent-color white -background black -alpha remove -alpha copy -channel RGB -white-threshold 0 "+dest) + # Convert textures listed in the Conversion_Table.csv + failed_conversions = convert_standard_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, + tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Convert grass - grass_file = tex_dir + "/block/grass_block_top.png" - if os.path.isfile(grass_file): - FOLIAG = tex_dir+"/colormap/foliage.png" - GRASS = tex_dir+"/colormap/grass.png" + # Conversion of map backgrounds + convert_map_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, + tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE) + # Convert armor textures + convert_armor_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Leaves - colorize_alpha(FOLIAG, tex_dir+"/block/oak_leaves.png", "116+143", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/default_leaves.png",tempfile2.name) - colorize_alpha(FOLIAG, tex_dir+"/block/dark_oak_leaves.png", "158+177", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_core_leaves_big_oak.png",tempfile2.name) - colorize_alpha(FOLIAG, tex_dir+"/block/acacia_leaves.png", "40+255", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/default_acacia_leaves.png",tempfile2.name) - colorize_alpha(FOLIAG, tex_dir+"/block/spruce_leaves.png", "226+230", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_core_leaves_spruce.png",tempfile2.name) - colorize_alpha(FOLIAG, tex_dir+"/block/birch_leaves.png", "141+186", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_core_leaves_birch.png",tempfile2.name) - colorize_alpha(FOLIAG, tex_dir+"/block/jungle_leaves.png", "16+39", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/default_jungleleaves.png",tempfile2.name) + # Convert chest textures + convert_chest_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Waterlily - colorize_alpha(FOLIAG, tex_dir+"/block/lily_pad.png", "16+39", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/flowers_waterlily.png",tempfile2.name) + # Generate railway crossings and t-junctions + convert_rail_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Vines - colorize_alpha(FOLIAG, tex_dir+"/block/vine.png", "16+39", str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_core_vine.png",tempfile2.name) + # Convert banner overlays + convert_banner_overlays(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Tall grass, fern (inventory images) - pcol = "50+173" # Plains grass color - # TODO: TALLGRASS.png does no longer exist - colorize_alpha(GRASS, tex_dir+"/block/tallgrass.png", pcol, str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_flowers_tallgrass_inv.png",tempfile2.name) - colorize_alpha(GRASS, tex_dir+"/block/fern.png", pcol, str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_flowers_fern_inv.png",tempfile2.name) - colorize_alpha(GRASS, tex_dir+"/block/large_fern_top.png", pcol, str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_flowers_double_plant_fern_inv.png",tempfile2.name) - colorize_alpha(GRASS, tex_dir+"/block/tall_grass_top.png", pcol, str(PXSIZE), target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path)+"/mcl_flowers_double_plant_grass_inv.png",tempfile2.name) + # Convert grass and related textures + convert_grass_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE) - # Convert grass palette: https://minecraft.fandom.com/wiki/Tint - grass_colors = [ - # [Coords or #Color, AdditionalTint], # Index - Minecraft biome name (MineClone2 biome names) - ["50+173"], # 0 - Plains (flat, Plains, Plains_beach, Plains_ocean, End) - ["0+255"], # 1 - Savanna (Savanna, Savanna_beach, Savanna_ocean) - ["255+255"], # 2 - Ice Spikes (IcePlainsSpikes, IcePlainsSpikes_ocean) - ["255+255"], # 3 - Snowy Taiga (ColdTaiga, ColdTaiga_beach, ColdTaiga_beach_water, ColdTaiga_ocean) - ["178+193"], # 4 - Giant Tree Taiga (MegaTaiga, MegaTaiga_ocean) - ["178+193"], # 5 - Giant Tree Taiga (MegaSpruceTaiga, MegaSpruceTaiga_ocean) - ["203+239"], # 6 - Montains (ExtremeHills, ExtremeHills_beach, ExtremeHills_ocean) - ["203+239"], # 7 - Montains (ExtremeHillsM, ExtremeHillsM_ocean) - ["203+239"], # 8 - Montains (ExtremeHills+, ExtremeHills+_snowtop, ExtremeHills+_ocean) - ["50+173"], # 9 - Beach (StoneBeach, StoneBeach_ocean) - ["255+255"], # 10 - Snowy Tundra (IcePlains, IcePlains_ocean) - ["50+173"], # 11 - Sunflower Plains (SunflowerPlains, SunflowerPlains_ocean) - ["191+203"], # 12 - Taiga (Taiga, Taiga_beach, Taiga_ocean) - ["76+112"], # 13 - Forest (Forest, Forest_beach, Forest_ocean) - ["76+112"], # 14 - Flower Forest (FlowerForest, FlowerForest_beach, FlowerForest_ocean) - ["101+163"], # 15 - Birch Forest (BirchForest, BirchForest_ocean) - ["101+163"], # 16 - Birch Forest Hills (BirchForestM, BirchForestM_ocean) - ["0+255"], # 17 - Desert and Nether (Desert, Desert_ocean, Nether) - ["76+112", "#28340A"], # 18 - Dark Forest (RoofedForest, RoofedForest_ocean) - ["#90814d"], # 19 - Mesa (Mesa, Mesa_sandlevel, Mesa_ocean, ) - ["#90814d"], # 20 - Mesa (MesaBryce, MesaBryce_sandlevel, MesaBryce_ocean) - ["#90814d"], # 21 - Mesa (MesaPlateauF, MesaPlateauF_grasstop, MesaPlateauF_sandlevel, MesaPlateauF_ocean) - ["#90814d"], # 22 - Mesa (MesaPlateauFM, MesaPlateauFM_grasstop, MesaPlateauFM_sandlevel, MesaPlateauFM_ocean) - ["0+255"], # 23 - Shattered Savanna (or Savanna Plateau ?) (SavannaM, SavannaM_ocean) - ["12+36"], # 24 - Jungle (Jungle, Jungle_shore, Jungle_ocean) - ["12+36"], # 25 - Modified Jungle (JungleM, JungleM_shore, JungleM_ocean) - ["12+61"], # 26 - Jungle Edge (JungleEdge, JungleEdge_ocean) - ["12+61"], # 27 - Modified Jungle Edge (JungleEdgeM, JungleEdgeM_ocean) - ["#6A7039"], # 28 - Swamp (Swampland, Swampland_shore, Swampland_ocean) - ["25+25"], # 29 - Mushroom Fields and Mushroom Field Shore (MushroomIsland, MushroomIslandShore, MushroomIsland_ocean) - ] + # Metadata + if make_texture_pack: + # Create description file + description = "Texture pack for MineClone 2. Automatically converted from a Minecraft resource pack by the MineClone 2 Texture Converter. Size: "+str(PXSIZE)+"×"+str(PXSIZE) + description_file = open(target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/description.txt", "w") + description_file.write(description) + description_file.close() - grass_palette_file = target_dir("/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/mcl_core_palette_grass.png" - os.system("convert -size 16x16 canvas:transparent " + grass_palette_file) + # Create preview image (screenshot.png) + os.system("convert -size 300x200 canvas:transparent "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png") + os.system("composite "+base_dir+"/pack.png "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png -gravity center "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png") - for i, color in enumerate(grass_colors): - if color[0][0] == "#": - os.system("convert -size 1x1 xc:\"" + color[0] + "\" " + tempfile1.name + ".png") - else: - os.system("convert " + GRASS + " -crop 1x1+" + color[0] + " " + tempfile1.name + ".png") - - if len(color) > 1: - os.system("convert " + tempfile1.name + ".png \\( -size 1x1 xc:\"" + color[1] + "\" \\) -compose blend -define compose:args=50,50 -composite " + tempfile1.name + ".png") - - os.system("convert " + grass_palette_file + " \\( " + tempfile1.name + ".png -geometry +" + str(i % 16) + "+" + str(int(i / 16)) + " \\) -composite " + grass_palette_file) - - # Metadata - if make_texture_pack: - # Create description file - description = "Texture pack for MineClone 2. Automatically converted from a Minecraft resource pack by the MineClone 2 Texture Converter. Size: "+str(PXSIZE)+"×"+str(PXSIZE) - description_file = open(target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/description.txt", "w") - description_file.write(description) - description_file.close() - - # Create preview image (screenshot.png) - os.system("convert -size 300x200 canvas:transparent "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png") - os.system("composite "+base_dir+"/pack.png "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png -gravity center "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png") - - print("Textures conversion COMPLETE!") - if failed_conversions > 0: - print("WARNING: Number of missing files in original resource pack: "+str(failed_conversions)) - print("NOTE: Please keep in mind this script does not reliably convert all the textures yet.") - if make_texture_pack: - print("You can now retrieve the texture pack in "+output_dir+"/"+output_dir_name+"/") + print("Textures conversion COMPLETE!") + if failed_conversions > 0: + print("WARNING: Number of missing files in original resource pack: " + str(failed_conversions)) + print("NOTE: Please keep in mind this script does not reliably convert all the textures yet.") + if make_texture_pack: + print("You can now retrieve the texture pack in " + output_dir + "/" + output_dir_name + "/") diff --git a/tools/libtextureconverter/gui.py b/tools/libtextureconverter/gui.py index 07b1904b2..9afa15667 100644 --- a/tools/libtextureconverter/gui.py +++ b/tools/libtextureconverter/gui.py @@ -3,7 +3,10 @@ from tkinter import filedialog, messagebox, ttk, font from libtextureconverter.utils import handle_default_minecraft_texture, find_all_minecraft_resourcepacks from libtextureconverter.config import home from libtextureconverter.common import convert_resource_packs -import time, os, threading +import time +import os +import threading + class TextureConverterGUI: def __init__(self, root): @@ -17,10 +20,16 @@ class TextureConverterGUI: # Frame for instructions self.instruction_frame = tk.Frame(self.root) self.instruction_frame.pack(fill='x', padx=10, pady=10) - tk.Label(self.instruction_frame, text="Do you want to convert installed resource packs, or convert a single zip file?").pack(side='left', fill='x', expand=True) + tk.Label( + self.instruction_frame, + text="Do you want to convert installed resource packs, or convert a single zip file?").pack( + side='left', + fill='x', + expand=True) # Table-like structure using Treeview - self.tree = ttk.Treeview(self.root, columns=('Convert', 'Description'), show='headings') + self.tree = ttk.Treeview(self.root, columns=( + 'Convert', 'Description'), show='headings') self.tree.heading('Convert', text='Convert') self.tree.heading('Description', text='Description') @@ -34,15 +43,16 @@ class TextureConverterGUI: for entry in entries: self.tree.insert('', 'end', values=entry) - - # Button Frame self.button_frame = tk.Frame(self.root) - self.button_frame.pack(fill='x', padx=10, pady=10, side='bottom') # Ensure the buttons are at the bottom + # Ensure the buttons are at the bottom + self.button_frame.pack(fill='x', padx=10, pady=10, side='bottom') # Create and pack the buttons separately - self.ok_button = tk.Button(self.button_frame, text="OK", command=self.confirm_selection) + self.ok_button = tk.Button( + self.button_frame, text="OK", command=self.confirm_selection) self.ok_button.pack(side=tk.RIGHT, padx=5) - self.cancel_button = tk.Button(self.button_frame, text="Cancel", command=self.cancel_conversion) + self.cancel_button = tk.Button( + self.button_frame, text="Cancel", command=self.cancel_conversion) self.cancel_button.pack(side=tk.RIGHT) self.tree.pack(fill='both', expand=True, padx=10, pady=10) @@ -55,8 +65,10 @@ class TextureConverterGUI: # Measure and set the column widths convert_width = tk.font.Font().measure('Convert') + 20 description_width = max( - tk.font.Font().measure(self.tree.set(item, 'Description')) for item in self.tree.get_children() - ) + 20 + tk.font.Font().measure( + self.tree.set( + item, + 'Description')) for item in self.tree.get_children()) + 20 # Apply the column widths self.tree.column('Convert', width=convert_width, anchor='center') @@ -71,7 +83,8 @@ class TextureConverterGUI: self.tree.config(height=num_items) # Calculate the total height needed - total_height = self.instruction_frame.winfo_height() + self.button_frame.winfo_height() + tree_height + 20 + total_height = self.instruction_frame.winfo_height( + ) + self.button_frame.winfo_height() + tree_height + 20 # Calculate the total width needed total_width = convert_width + description_width + 20 @@ -82,10 +95,10 @@ class TextureConverterGUI: # Prevent the window from resizing smaller than it should self.root.minsize(int(total_width), int(total_height)) - # Update the idle tasks to recalculate sizes, may help to remove extra space + # Update the idle tasks to recalculate sizes, may help to remove extra + # space self.root.update_idletasks() - def confirm_selection(self): self.cancel_button.config(state=tk.NORMAL) selected_item = self.tree.focus() @@ -97,15 +110,15 @@ class TextureConverterGUI: self.root.update_idletasks() # Update the geometry of the widgets self.root.minsize(self.root.winfo_width(), self.root.winfo_height()) - - def show_loading_screen(self, option): # Display a non-blocking loading message - self.loading_label = tk.Label(self.root, text="Converting textures, please wait...", fg="blue") + self.loading_label = tk.Label( + self.root, text="Converting textures, please wait...", fg="blue") self.loading_label.pack() # Start the conversion process in a separate thread - conversion_thread = threading.Thread(target=self.perform_conversion, args=(option,), daemon=True) + conversion_thread = threading.Thread( + target=self.perform_conversion, args=(option,), daemon=True) conversion_thread.start() # Disable the OK button while the conversion is in progress @@ -119,12 +132,13 @@ class TextureConverterGUI: verbose = False output_dir = os.path.join(home, ".minetest", "textures") make_texture_pack = True - + # Determine the resource packs to convert based on the option if option == 'all': resource_packs = find_all_minecraft_resourcepacks() elif option == 'default': - resource_packs = [handle_default_minecraft_texture(home, output_dir)] + resource_packs = [ + handle_default_minecraft_texture(home, output_dir)] elif option == 'other': folder_selected = filedialog.askdirectory() if folder_selected: @@ -136,13 +150,15 @@ class TextureConverterGUI: return # Convert resource packs - convert_resource_packs(resource_packs, output_dir, pixelsize, dry_run, verbose, make_texture_pack) + convert_resource_packs(resource_packs, output_dir, + pixelsize, dry_run, verbose, make_texture_pack) # Update the GUI after conversion self.loading_label.pack_forget() self.ok_button.config(state=tk.NORMAL) - messagebox.showinfo("Conversion Complete", f"Resource Packs '{', '.join(resource_packs)}' converted.") - + messagebox.showinfo( + "Conversion Complete", + f"Resource Packs '{', '.join(resource_packs)}' converted.") def convert_all(self): # Simulate a conversion process @@ -162,17 +178,20 @@ class TextureConverterGUI: time.sleep(2) # Simulate some time for conversion def cancel_conversion(self): - # Placeholder for cancel action, you may need to implement actual cancellation logic + # Placeholder for cancel action, you may need to implement actual + # cancellation logic print("Conversion cancelled by user.") self.loading_label.pack_forget() self.ok_button.config(state=tk.NORMAL) self.cancel_button.config(state=tk.DISABLED) + def main(): root = tk.Tk() app = TextureConverterGUI(root) app.adjust_column_widths() root.mainloop() + if __name__ == "__main__": main() diff --git a/tools/libtextureconverter/special_convert_cases.py b/tools/libtextureconverter/special_convert_cases.py new file mode 100644 index 000000000..71af2b6f2 --- /dev/null +++ b/tools/libtextureconverter/special_convert_cases.py @@ -0,0 +1,817 @@ +import os +from .utils import target_dir, colorize, colorize_alpha +import shutil +import csv +import tempfile +import sys +import argparse +import glob + +# Conversion of map backgrounds +def convert_map_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Convert map background + map_background_file = tex_dir + "/map/map_background.png" + if os.path.isfile(map_background_file): + os.system( + "convert " + + map_background_file + + " -interpolate Integer -filter point -resize \"140x140\" " + + target_dir( + "/mods/ITEMS/mcl_maps/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_maps_map_background.png") + +# Convert armor textures + + +def convert_armor_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Convert armor textures (requires ImageMagick) + armor_files = [[tex_dir + "/models/armor/leather_layer_1.png", + tex_dir + "/models/armor/leather_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_leather.png", + "mcl_armor_chestplate_leather.png", + "mcl_armor_leggings_leather.png", + "mcl_armor_boots_leather.png"], + [tex_dir + "/models/armor/chainmail_layer_1.png", + tex_dir + "/models/armor/chainmail_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_chain.png", + "mcl_armor_chestplate_chain.png", + "mcl_armor_leggings_chain.png", + "mcl_armor_boots_chain.png"], + [tex_dir + "/models/armor/gold_layer_1.png", + tex_dir + "/models/armor/gold_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_gold.png", + "mcl_armor_chestplate_gold.png", + "mcl_armor_leggings_gold.png", + "mcl_armor_boots_gold.png"], + [tex_dir + "/models/armor/iron_layer_1.png", + tex_dir + "/models/armor/iron_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_iron.png", + "mcl_armor_chestplate_iron.png", + "mcl_armor_leggings_iron.png", + "mcl_armor_boots_iron.png"], + [tex_dir + "/models/armor/diamond_layer_1.png", + tex_dir + "/models/armor/diamond_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_diamond.png", + "mcl_armor_chestplate_diamond.png", + "mcl_armor_leggings_diamond.png", + "mcl_armor_boots_diamond.png"], + [tex_dir + "/models/armor/netherite_layer_1.png", + tex_dir + "/models/armor/netherite_layer_2.png", + target_dir("/mods/ITEMS/mcl_armor/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_armor_helmet_netherite.png", + "mcl_armor_chestplate_netherite.png", + "mcl_armor_leggings_netherite.png", + "mcl_armor_boots_netherite.png"]] + for a in armor_files: + APXSIZE = 16 # for some reason MineClone2 requires this + layer_1 = a[0] + layer_2 = a[1] + adir = a[2] + if os.path.isfile(layer_1): + helmet = adir + "/" + a[3] + chestplate = adir + "/" + a[4] + boots = adir + "/" + a[6] + os.system("convert -size " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " xc:none \\( " + + layer_1 + + " -scale " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " -geometry +" + + str(APXSIZE * + 2) + + "+0 -crop " + + str(APXSIZE * + 2) + + "x" + + str(APXSIZE) + + "+0+0 \\) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" " + + helmet) + os.system("convert -size " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " xc:none \\( " + + layer_1 + + " -scale " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " -geometry +" + + str(APXSIZE) + + "+" + + str(APXSIZE) + + " -crop " + + str(APXSIZE * + 2.5) + + "x" + + str(APXSIZE) + + "+" + + str(APXSIZE) + + "+" + + str(APXSIZE) + + " \\) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" " + + chestplate) + os.system("convert -size " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " xc:none \\( " + + layer_1 + + " -scale " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " -geometry +0+" + + str(APXSIZE) + + " -crop " + + str(APXSIZE) + + "x" + + str(APXSIZE) + + "+0+" + + str(APXSIZE) + + " \\) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" " + + boots) + if os.path.isfile(layer_2): + leggings = adir + "/" + a[5] + os.system("convert -size " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " xc:none \\( " + + layer_2 + + " -scale " + + str(APXSIZE * + 4) + + "x" + + str(APXSIZE * + 2) + + " -geometry +0+" + + str(APXSIZE) + + " -crop " + + str(APXSIZE * + 2.5) + + "x" + + str(APXSIZE) + + "+0+" + + str(APXSIZE) + + " \\) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" " + + leggings) + +# Convert chest textures + + +def convert_chest_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Convert chest textures (requires ImageMagick) + chest_files = [[tex_dir + "/entity/chest/normal.png", + target_dir("/mods/ITEMS/mcl_chests/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "default_chest_top.png", + "mcl_chests_chest_bottom.png", + "default_chest_front.png", + "mcl_chests_chest_left.png", + "mcl_chests_chest_right.png", + "mcl_chests_chest_back.png"], + [tex_dir + "/entity/chest/trapped.png", + target_dir("/mods/ITEMS/mcl_chests/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_chests_chest_trapped_top.png", + "mcl_chests_chest_trapped_bottom.png", + "mcl_chests_chest_trapped_front.png", + "mcl_chests_chest_trapped_left.png", + "mcl_chests_chest_trapped_right.png", + "mcl_chests_chest_trapped_back.png"], + [tex_dir + "/entity/chest/ender.png", + target_dir("/mods/ITEMS/mcl_chests/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_chests_ender_chest_top.png", + "mcl_chests_ender_chest_bottom.png", + "mcl_chests_ender_chest_front.png", + "mcl_chests_ender_chest_left.png", + "mcl_chests_ender_chest_right.png", + "mcl_chests_ender_chest_back.png"]] + + for c in chest_files: + chest_file = c[0] + if os.path.isfile(chest_file): + PPX = (PXSIZE / 16) + CHPX = (PPX * 14) # Chest width + LIDPX = (PPX * 5) # Lid height + LIDLOW = (PPX * 10) # Lower lid section height + LOCKW = (PPX * 6) # Lock width + LOCKH = (PPX * 5) # Lock height + + cdir = c[1] + top = cdir + "/" + c[2] + bottom = cdir + "/" + c[3] + front = cdir + "/" + c[4] + left = cdir + "/" + c[5] + right = cdir + "/" + c[6] + back = cdir + "/" + c[7] + # Top + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(CHPX) + "+" + str(CHPX) + "+0 \\) -geometry +0+0 -composite -extent " + str(CHPX) + "x" + str(CHPX) + " " + top) + # Bottom + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(CHPX) + "+" + str(CHPX * 2) + "+" + str(CHPX + LIDPX) + " \\) -geometry +0+0 -composite -extent " + str(CHPX) + "x" + str(CHPX) + " " + bottom) + # Front + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDPX) + "+" + str(CHPX) + "+" + str(CHPX) + " \\) -geometry +0+0 -composite \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDLOW) + "+" + str(CHPX) + "+" + str(CHPX * 2 + LIDPX) + " \\) -geometry +0+" + str(LIDPX - PPX) + " -composite \ +-extent " + str(CHPX) + "x" + str(CHPX) + " " + front) + # TODO: Add lock + + # Left, right back (use same texture, we're lazy + files = [left, right, back] + for f in files: + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDPX) + "+" + str(0) + "+" + str(CHPX) + " \\) -geometry +0+0 -composite \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDLOW) + "+" + str(0) + "+" + str(CHPX * 2 + LIDPX) + " \\) -geometry +0+" + str(LIDPX - PPX) + " -composite \ +-extent " + str(CHPX) + "x" + str(CHPX) + " " + f) + + # Double chests + + chest_files = [[tex_dir + "/entity/chest/normal_double.png", + target_dir("/mods/ITEMS/mcl_chests/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "default_chest_front_big.png", + "default_chest_top_big.png", + "default_chest_side_big.png"], + [tex_dir + "/entity/chest/trapped_double.png", + target_dir("/mods/ITEMS/mcl_chests/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path), + "mcl_chests_chest_trapped_front_big.png", + "mcl_chests_chest_trapped_top_big.png", + "mcl_chests_chest_trapped_side_big.png"]] + for c in chest_files: + chest_file = c[0] + if os.path.isfile(chest_file): + PPX = (PXSIZE / 16) + CHPX = (PPX * 14) # Chest width (short side) + CHPX2 = (PPX * 15) # Chest width (long side) + LIDPX = (PPX * 5) # Lid height + LIDLOW = (PPX * 10) # Lower lid section height + LOCKW = (PPX * 6) # Lock width + LOCKH = (PPX * 5) # Lock height + + cdir = c[1] + front = cdir + "/" + c[2] + top = cdir + "/" + c[3] + side = cdir + "/" + c[4] + # Top + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX2) + "x" + str(CHPX) + "+" + str(CHPX) + "+0 \\) -geometry +0+0 -composite -extent " + str(CHPX2) + "x" + str(CHPX) + " " + top) + # Front + # TODO: Add lock + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX2) + "x" + str(LIDPX) + "+" + str(CHPX) + "+" + str(CHPX) + " \\) -geometry +0+0 -composite \ +\\( -clone 0 -crop " + str(CHPX2) + "x" + str(LIDLOW) + "+" + str(CHPX) + "+" + str(CHPX * 2 + LIDPX) + " \\) -geometry +0+" + str(LIDPX - PPX) + " -composite \ +-extent " + str(CHPX2) + "x" + str(CHPX) + " " + front) + # Side + os.system("convert " + chest_file + " \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDPX) + "+" + str(0) + "+" + str(CHPX) + " \\) -geometry +0+0 -composite \ +\\( -clone 0 -crop " + str(CHPX) + "x" + str(LIDLOW) + "+" + str(0) + "+" + str(CHPX * 2 + LIDPX) + " \\) -geometry +0+" + str(LIDPX - PPX) + " -composite \ +-extent " + str(CHPX) + "x" + str(CHPX) + " " + side) + +# Generate railway crossings and t-junctions + + +def convert_rail_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Generate railway crossings and t-junctions. Note: They may look strange. + # Note: these may be only a temporary solution, as crossings and t-junctions do not occour in MC. + # TODO: Curves + rails = [ + # (Straigt src, curved src, t-junction dest, crossing dest) + ("rail.png", "rail_corner.png", + "default_rail_t_junction.png", "default_rail_crossing.png"), + ("powered_rail.png", "rail_corner.png", + "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"), + ("powered_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_golden_t_junction_powered.png", + "mcl_minecarts_rail_golden_crossing_powered.png"), + ("detector_rail.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction.png", + "mcl_minecarts_rail_detector_crossing.png"), + ("detector_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction_powered.png", + "mcl_minecarts_rail_detector_crossing_powered.png"), + ("activator_rail.png", "rail_corner.png", "mcl_minecarts_rail_activator_t_junction.png", + "mcl_minecarts_rail_activator_crossing.png"), + ("activator_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_activator_d_t_junction.png", + "mcl_minecarts_rail_activator_powered_crossing.png"), + ] + for r in rails: + os.system( + "composite -compose Dst_Over " + + tex_dir + + "/block/" + + r[0] + + " " + + tex_dir + + "/block/" + + r[1] + + " " + + target_dir( + "/mods/ENTITIES/mcl_minecarts/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/" + + r[2]) + os.system("convert " + tex_dir + "/block/" + + r[0] + " -rotate 90 " + tempfile1.name) + os.system( + "composite -compose Dst_Over " + + tempfile1.name + + " " + + tex_dir + + "/block/" + + r[0] + + " " + + target_dir( + "/mods/ENTITIES/mcl_minecarts/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/" + + r[3]) + +# Convert banner overlays + + +def convert_banner_overlays( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Convert banner overlays + overlays = [ + "base", + "border", + "bricks", + "circle", + "creeper", + "cross", + "curly_border", + "diagonal_left", + "diagonal_right", + "diagonal_up_left", + "diagonal_up_right", + "flower", + "gradient", + "gradient_up", + "half_horizontal_bottom", + "half_horizontal", + "half_vertical", + "half_vertical_right", + "rhombus", + "mojang", + "skull", + "small_stripes", + "straight_cross", + "stripe_bottom", + "stripe_center", + "stripe_downleft", + "stripe_downright", + "stripe_left", + "stripe_middle", + "stripe_right", + "stripe_top", + "square_bottom_left", + "square_bottom_right", + "square_top_left", + "square_top_right", + "triangle_bottom", + "triangles_bottom", + "triangle_top", + "triangles_top", + ] + for o in overlays: + orig = tex_dir + "/entity/banner/" + o + ".png" + if os.path.isfile(orig): + if o == "mojang": + o = "thing" + dest = target_dir( + "/mods/ITEMS/mcl_banners/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + "/" + "mcl_banners_" + o + ".png" + os.system( + "convert " + + orig + + " -transparent-color white -background black -alpha remove -alpha copy -channel RGB -white-threshold 0 " + + dest) + +# Convert grass and related textures + + +def convert_grass_textures( + make_texture_pack, + dry_run, + verbose, + base_dir, + tex_dir, + tempfile1, + tempfile2, + output_dir, + output_dir_name, + mineclone2_path, + PXSIZE): + # Convert grass + grass_file = tex_dir + "/block/grass_block_top.png" + if os.path.isfile(grass_file): + FOLIAG = tex_dir + "/colormap/foliage.png" + GRASS = tex_dir + "/colormap/grass.png" + + # Leaves + colorize_alpha( + FOLIAG, + tex_dir + + "/block/oak_leaves.png", + "116+143", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/default_leaves.png", + tempfile2.name) + colorize_alpha( + FOLIAG, + tex_dir + + "/block/dark_oak_leaves.png", + "158+177", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_core_leaves_big_oak.png", + tempfile2.name) + colorize_alpha( + FOLIAG, + tex_dir + + "/block/acacia_leaves.png", + "40+255", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/default_acacia_leaves.png", + tempfile2.name) + colorize_alpha( + FOLIAG, + tex_dir + + "/block/spruce_leaves.png", + "226+230", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_core_leaves_spruce.png", + tempfile2.name) + colorize_alpha( + FOLIAG, + tex_dir + + "/block/birch_leaves.png", + "141+186", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_core_leaves_birch.png", + tempfile2.name) + colorize_alpha( + FOLIAG, + tex_dir + + "/block/jungle_leaves.png", + "16+39", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/default_jungleleaves.png", + tempfile2.name) + + # Waterlily + colorize_alpha( + FOLIAG, + tex_dir + + "/block/lily_pad.png", + "16+39", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/flowers_waterlily.png", + tempfile2.name) + + # Vines + colorize_alpha( + FOLIAG, + tex_dir + + "/block/vine.png", + "16+39", + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_core_vine.png", + tempfile2.name) + + # Tall grass, fern (inventory images) + pcol = "50+173" # Plains grass color + # TODO: TALLGRASS.png does no longer exist + colorize_alpha( + GRASS, + tex_dir + + "/block/tallgrass.png", + pcol, + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_flowers_tallgrass_inv.png", + tempfile2.name) + colorize_alpha( + GRASS, + tex_dir + + "/block/fern.png", + pcol, + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_flowers_fern_inv.png", + tempfile2.name) + colorize_alpha( + GRASS, + tex_dir + + "/block/large_fern_top.png", + pcol, + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_flowers_double_plant_fern_inv.png", + tempfile2.name) + colorize_alpha( + GRASS, + tex_dir + + "/block/tall_grass_top.png", + pcol, + str(PXSIZE), + target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + + "/mcl_flowers_double_plant_grass_inv.png", + tempfile2.name) + + # Convert grass palette: https://minecraft.fandom.com/wiki/Tint + grass_colors = [ + # [Coords or #Color, AdditionalTint], # Index - Minecraft biome name (MineClone2 biome names) + # 0 - Plains (flat, Plains, Plains_beach, Plains_ocean, End) + ["50+173"], + # 1 - Savanna (Savanna, Savanna_beach, Savanna_ocean) + ["0+255"], + # 2 - Ice Spikes (IcePlainsSpikes, IcePlainsSpikes_ocean) + ["255+255"], + # 3 - Snowy Taiga (ColdTaiga, ColdTaiga_beach, ColdTaiga_beach_water, ColdTaiga_ocean) + ["255+255"], + # 4 - Giant Tree Taiga (MegaTaiga, MegaTaiga_ocean) + ["178+193"], + # 5 - Giant Tree Taiga (MegaSpruceTaiga, MegaSpruceTaiga_ocean) + ["178+193"], + # 6 - Montains (ExtremeHills, ExtremeHills_beach, ExtremeHills_ocean) + ["203+239"], + # 7 - Montains (ExtremeHillsM, ExtremeHillsM_ocean) + ["203+239"], + # 8 - Montains (ExtremeHills+, ExtremeHills+_snowtop, ExtremeHills+_ocean) + ["203+239"], + ["50+173"], # 9 - Beach (StoneBeach, StoneBeach_ocean) + ["255+255"], # 10 - Snowy Tundra (IcePlains, IcePlains_ocean) + # 11 - Sunflower Plains (SunflowerPlains, SunflowerPlains_ocean) + ["50+173"], + ["191+203"], # 12 - Taiga (Taiga, Taiga_beach, Taiga_ocean) + ["76+112"], # 13 - Forest (Forest, Forest_beach, Forest_ocean) + # 14 - Flower Forest (FlowerForest, FlowerForest_beach, FlowerForest_ocean) + ["76+112"], + # 15 - Birch Forest (BirchForest, BirchForest_ocean) + ["101+163"], + # 16 - Birch Forest Hills (BirchForestM, BirchForestM_ocean) + ["101+163"], + # 17 - Desert and Nether (Desert, Desert_ocean, Nether) + ["0+255"], + # 18 - Dark Forest (RoofedForest, RoofedForest_ocean) + ["76+112", "#28340A"], + ["#90814d"], # 19 - Mesa (Mesa, Mesa_sandlevel, Mesa_ocean, ) + # 20 - Mesa (MesaBryce, MesaBryce_sandlevel, MesaBryce_ocean) + ["#90814d"], + # 21 - Mesa (MesaPlateauF, MesaPlateauF_grasstop, MesaPlateauF_sandlevel, MesaPlateauF_ocean) + ["#90814d"], + # 22 - Mesa (MesaPlateauFM, MesaPlateauFM_grasstop, MesaPlateauFM_sandlevel, MesaPlateauFM_ocean) + ["#90814d"], + # 23 - Shattered Savanna (or Savanna Plateau ?) (SavannaM, SavannaM_ocean) + ["0+255"], + ["12+36"], # 24 - Jungle (Jungle, Jungle_shore, Jungle_ocean) + # 25 - Modified Jungle (JungleM, JungleM_shore, JungleM_ocean) + ["12+36"], + ["12+61"], # 26 - Jungle Edge (JungleEdge, JungleEdge_ocean) + # 27 - Modified Jungle Edge (JungleEdgeM, JungleEdgeM_ocean) + ["12+61"], + # 28 - Swamp (Swampland, Swampland_shore, Swampland_ocean) + ["#6A7039"], + # 29 - Mushroom Fields and Mushroom Field Shore (MushroomIsland, MushroomIslandShore, MushroomIsland_ocean) + ["25+25"], + ] + + grass_palette_file = target_dir( + "/textures", + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path) + "/mcl_core_palette_grass.png" + os.system("convert -size 16x16 canvas:transparent " + + grass_palette_file) + + for i, color in enumerate(grass_colors): + if color[0][0] == "#": + os.system("convert -size 1x1 xc:\"" + + color[0] + "\" " + tempfile1.name + ".png") + else: + os.system("convert " + GRASS + " -crop 1x1+" + + color[0] + " " + tempfile1.name + ".png") + + if len(color) > 1: + os.system( + "convert " + + tempfile1.name + + ".png \\( -size 1x1 xc:\"" + + color[1] + + "\" \\) -compose blend -define compose:args=50,50 -composite " + + tempfile1.name + + ".png") + + os.system("convert " + + grass_palette_file + + " \\( " + + tempfile1.name + + ".png -geometry +" + + str(i % + 16) + + "+" + + str(int(i / + 16)) + + " \\) -composite " + + grass_palette_file) diff --git a/tools/libtextureconverter/utils.py b/tools/libtextureconverter/utils.py index a54e87e1d..7f9bdb390 100644 --- a/tools/libtextureconverter/utils.py +++ b/tools/libtextureconverter/utils.py @@ -1,8 +1,17 @@ -import shutil, csv, os, tempfile, sys, argparse, glob, re, zipfile +import shutil +import csv +import os +import tempfile +import sys +import argparse +import glob +import re +import zipfile from .config import SUPPORTED_MINECRAFT_VERSION, home from PIL import Image from collections import Counter + def detect_pixel_size(directory): sizes = [] for filename in glob.glob(directory + '/**/*.png', recursive=True): @@ -11,22 +20,57 @@ def detect_pixel_size(directory): if not sizes: return 16 # Default to 16x16 if no PNG files are found most_common_size = Counter(sizes).most_common(1)[0][0] - print(f"Autodetected pixel size: {most_common_size[0]}x{most_common_size[1]}") + print( + f"Autodetected pixel size: {most_common_size[0]}x{most_common_size[1]}") return most_common_size[0] -def target_dir(directory, make_texture_pack, output_dir, output_dir_name, mineclone2_path): - if make_texture_pack: - return output_dir + "/" + output_dir_name - else: - return mineclone2_path + directory -def colorize(colormap, source, colormap_pixel, texture_size, destination, tempfile1_name): - os.system("convert "+colormap+" -crop 1x1+"+colormap_pixel+" -depth 8 -resize "+texture_size+"x"+texture_size+" "+tempfile1_name) - os.system("composite -compose Multiply "+tempfile1_name+" "+source+" "+destination) +def target_dir( + directory, + make_texture_pack, + output_dir, + output_dir_name, + mineclone2_path): + if make_texture_pack: + return output_dir + "/" + output_dir_name + else: + return mineclone2_path + directory + + +def colorize( + colormap, + source, + colormap_pixel, + texture_size, + destination, + tempfile1_name): + os.system( + "convert " + + colormap + + " -crop 1x1+" + + colormap_pixel + + " -depth 8 -resize " + + texture_size + + "x" + + texture_size + + " " + + tempfile1_name) + os.system("composite -compose Multiply " + + tempfile1_name + " " + source + " " + destination) + + +def colorize_alpha( + colormap, + source, + colormap_pixel, + texture_size, + destination, + tempfile2_name): + colorize(colormap, source, colormap_pixel, + texture_size, destination, tempfile2_name) + os.system("composite -compose Dst_In " + source + " " + + tempfile2_name + " -alpha Set " + destination) -def colorize_alpha(colormap, source, colormap_pixel, texture_size, destination, tempfile2_name): - colorize(colormap, source, colormap_pixel, texture_size, destination, tempfile2_name) - os.system("composite -compose Dst_In "+source+" "+tempfile2_name+" -alpha Set "+destination) def find_highest_minecraft_version(home, supported_version): version_pattern = re.compile(re.escape(supported_version) + r"\.\d+") @@ -39,6 +83,7 @@ def find_highest_minecraft_version(home, supported_version): highest_version = folder return highest_version + def find_all_minecraft_resourcepacks(): resourcepacks_dir = os.path.join(home, '.minecraft', 'resourcepacks') @@ -55,17 +100,20 @@ def find_all_minecraft_resourcepacks(): print(f"Adding resourcepack '{folder}'") resourcepacks.append(folder_path) else: - print(f"pack.png not found in resourcepack '{folder}', not converting") + print( + f"pack.png not found in resourcepack '{folder}', not converting") return resourcepacks + def handle_default_minecraft_texture(home, output_dir): version = find_highest_minecraft_version(home, SUPPORTED_MINECRAFT_VERSION) if not version: print("No suitable Minecraft version found.") sys.exit(1) - jar_file = os.path.join(home, ".minecraft", "versions", version, f"{version}.jar") + jar_file = os.path.join( + home, ".minecraft", "versions", version, f"{version}.jar") if not os.path.isfile(jar_file): print("Minecraft JAR file not found.") sys.exit(1) @@ -85,26 +133,29 @@ def handle_default_minecraft_texture(home, output_dir): extract_folder = os.path.normpath(extract_folder) # Define the textures directory and normalize it - textures_directory = os.path.normpath(f"{extract_folder}/assets/minecraft/textures") + textures_directory = os.path.normpath( + f"{extract_folder}/assets/minecraft/textures") # Using glob to find all files all_files = glob.glob(f"{extract_folder}/**/*.*", recursive=True) # Remove all non-png files except pack.mcmeta and pack.png in the root for file_path in all_files: - if not file_path.endswith('.png') and not file_path.endswith('pack.mcmeta') and not file_path.endswith('pack.png'): - #print(f"Removing file: {file_path}") + if not file_path.endswith('.png') and not file_path.endswith( + 'pack.mcmeta') and not file_path.endswith('pack.png'): + # print(f"Removing file: {file_path}") os.remove(file_path) # Remove all directories in the root except 'assets' for item in os.listdir(extract_folder): item_path = os.path.join(extract_folder, item) if os.path.isdir(item_path) and item != "assets": - #print(f"Removing directory: {item_path}") + # print(f"Removing directory: {item_path}") shutil.rmtree(item_path, ignore_errors=True) # Remove directories in 'minecraft' except for 'textures' - minecraft_directory = os.path.normpath(f"{extract_folder}/assets/minecraft") + minecraft_directory = os.path.normpath( + f"{extract_folder}/assets/minecraft") for item in os.listdir(minecraft_directory): item_path = os.path.join(minecraft_directory, item) if os.path.isdir(item_path) and item != "textures": @@ -112,10 +163,13 @@ def handle_default_minecraft_texture(home, output_dir): shutil.rmtree(item_path, ignore_errors=True) # Copy the textures directory to the output directory - output_textures_directory = os.path.join(output_dir, 'assets/minecraft/textures') - if os.path.exists(textures_directory) and not os.path.exists(output_textures_directory): + output_textures_directory = os.path.join( + output_dir, 'assets/minecraft/textures') + if os.path.exists(textures_directory) and not os.path.exists( + output_textures_directory): os.makedirs(os.path.dirname(output_textures_directory), exist_ok=True) - shutil.copytree(textures_directory, output_textures_directory, dirs_exist_ok=True) + shutil.copytree(textures_directory, + output_textures_directory, dirs_exist_ok=True) # Copy pack.mcmeta and pack.png file if exists for file_name in ['pack.mcmeta', 'pack.png']: