1
0
Fork 0

Refactor convert_textures code, seperated special cases into its own module

This commit is contained in:
James Clarke 2024-01-10 08:16:36 +00:00 committed by the-real-herowl
parent 47b6bd5539
commit ddfbd331c9
6 changed files with 1027 additions and 325 deletions

View File

@ -36,7 +36,7 @@ def main():
print(f"ERROR: No valid resource packs specified. Use '{appname} -h' for help.") print(f"ERROR: No valid resource packs specified. Use '{appname} -h' for help.")
sys.exit(2) 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__": if __name__ == "__main__":
main() main()

View File

@ -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 PIL import Image
from collections import Counter 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.convert import convert_textures
from libtextureconverter.config import SUPPORTED_MINECRAFT_VERSION, working_dir, mineclone2_path, appname, home 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: for base_dir in resource_packs:
print(f"Converting resource pack: {base_dir}") 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 # Construct the path to the textures within the resource pack
tex_dir = os.path.join(base_dir, "assets", "minecraft", "textures") 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)) output_dir_name = os.path.basename(os.path.normpath(base_dir))
# Create the output directory if it doesn't exist # 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: try:
# Perform the actual conversion # 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: finally:
# Clean up temporary files # Clean up temporary files
tempfile1.close() tempfile1.close()

View File

@ -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 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_standard_textures(
def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE): make_texture_pack,
dry_run,
verbose,
base_dir,
tex_dir,
tempfile1,
tempfile2,
output_dir,
output_dir_name,
mineclone2_path,
PXSIZE):
failed_conversions = 0 failed_conversions = 0
print("Texture conversion BEGINS NOW!")
with open("Conversion_Table.csv", newline="") as csvfile: with open("Conversion_Table.csv", newline="") as csvfile:
reader = csv.reader(csvfile, delimiter=",", quotechar='"') reader = csv.reader(csvfile, delimiter=",", quotechar='"')
first_row = True first_row = True
@ -14,7 +30,6 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem
if first_row: if first_row:
first_row = False first_row = False
continue continue
src_dir = row[0] src_dir = row[0]
src_filename = row[1] src_filename = row[1]
dst_dir = './textures' dst_dir = './textures'
@ -43,7 +58,8 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem
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) 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: if src_file_exists == False:
print("WARNING: Source file does not exist: " + src_file) print("WARNING: Source file does not exist: " + src_file)
@ -53,7 +69,8 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem
if xs != None: if xs != None:
# Crop and copy images # Crop and copy images
if not dry_run: 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: if verbose:
print(src_file + "" + dst_file) print(src_file + "" + dst_file)
else: else:
@ -62,264 +79,34 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem
shutil.copy2(src_file, dst_file) shutil.copy2(src_file, dst_file)
if verbose: if verbose:
print(src_file + "" + dst_file) print(src_file + "" + dst_file)
return failed_conversions
# 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)
# Generate railway crossings and t-junctions. Note: They may look strange. def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE):
# Note: these may be only a temporary solution, as crossings and t-junctions do not occour in MC. print("Texture conversion BEGINS NOW!")
# TODO: Curves
rails = [ # Convert textures listed in the Conversion_Table.csv
# (Straigt src, curved src, t-junction dest, crossing dest) failed_conversions = convert_standard_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir,
("rail.png", "rail_corner.png", "default_rail_t_junction.png", "default_rail_crossing.png"), tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE)
("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"), # Conversion of map backgrounds
("detector_rail.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"), convert_map_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir,
("detector_rail_on.png", "rail_corner.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"), tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE)
("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"), # 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)
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]) # Convert chest textures
os.system("convert "+tex_dir+"/block/"+r[0]+" -rotate 90 "+tempfile1.name) convert_chest_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE)
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])
# 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)
# Convert banner overlays # Convert banner overlays
overlays = [ convert_banner_overlays(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE)
"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 # Convert grass and related textures
grass_file = tex_dir + "/block/grass_block_top.png" convert_grass_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2,output_dir, output_dir_name, mineclone2_path, PXSIZE)
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)
["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)
]
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)
# Metadata # Metadata
if make_texture_pack: if make_texture_pack:

View File

@ -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.utils import handle_default_minecraft_texture, find_all_minecraft_resourcepacks
from libtextureconverter.config import home from libtextureconverter.config import home
from libtextureconverter.common import convert_resource_packs from libtextureconverter.common import convert_resource_packs
import time, os, threading import time
import os
import threading
class TextureConverterGUI: class TextureConverterGUI:
def __init__(self, root): def __init__(self, root):
@ -17,10 +20,16 @@ class TextureConverterGUI:
# Frame for instructions # Frame for instructions
self.instruction_frame = tk.Frame(self.root) self.instruction_frame = tk.Frame(self.root)
self.instruction_frame.pack(fill='x', padx=10, pady=10) 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 # 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('Convert', text='Convert')
self.tree.heading('Description', text='Description') self.tree.heading('Description', text='Description')
@ -34,15 +43,16 @@ class TextureConverterGUI:
for entry in entries: for entry in entries:
self.tree.insert('', 'end', values=entry) self.tree.insert('', 'end', values=entry)
# Button Frame # Button Frame
self.button_frame = tk.Frame(self.root) 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 # 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.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.cancel_button.pack(side=tk.RIGHT)
self.tree.pack(fill='both', expand=True, padx=10, pady=10) self.tree.pack(fill='both', expand=True, padx=10, pady=10)
@ -55,8 +65,10 @@ class TextureConverterGUI:
# Measure and set the column widths # Measure and set the column widths
convert_width = tk.font.Font().measure('Convert') + 20 convert_width = tk.font.Font().measure('Convert') + 20
description_width = max( description_width = max(
tk.font.Font().measure(self.tree.set(item, 'Description')) for item in self.tree.get_children() tk.font.Font().measure(
) + 20 self.tree.set(
item,
'Description')) for item in self.tree.get_children()) + 20
# Apply the column widths # Apply the column widths
self.tree.column('Convert', width=convert_width, anchor='center') self.tree.column('Convert', width=convert_width, anchor='center')
@ -71,7 +83,8 @@ class TextureConverterGUI:
self.tree.config(height=num_items) self.tree.config(height=num_items)
# Calculate the total height needed # 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 # Calculate the total width needed
total_width = convert_width + description_width + 20 total_width = convert_width + description_width + 20
@ -82,10 +95,10 @@ class TextureConverterGUI:
# Prevent the window from resizing smaller than it should # Prevent the window from resizing smaller than it should
self.root.minsize(int(total_width), int(total_height)) 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() self.root.update_idletasks()
def confirm_selection(self): def confirm_selection(self):
self.cancel_button.config(state=tk.NORMAL) self.cancel_button.config(state=tk.NORMAL)
selected_item = self.tree.focus() selected_item = self.tree.focus()
@ -97,15 +110,15 @@ class TextureConverterGUI:
self.root.update_idletasks() # Update the geometry of the widgets self.root.update_idletasks() # Update the geometry of the widgets
self.root.minsize(self.root.winfo_width(), self.root.winfo_height()) self.root.minsize(self.root.winfo_width(), self.root.winfo_height())
def show_loading_screen(self, option): def show_loading_screen(self, option):
# Display a non-blocking loading message # 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() self.loading_label.pack()
# Start the conversion process in a separate thread # 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() conversion_thread.start()
# Disable the OK button while the conversion is in progress # Disable the OK button while the conversion is in progress
@ -124,7 +137,8 @@ class TextureConverterGUI:
if option == 'all': if option == 'all':
resource_packs = find_all_minecraft_resourcepacks() resource_packs = find_all_minecraft_resourcepacks()
elif option == 'default': elif option == 'default':
resource_packs = [handle_default_minecraft_texture(home, output_dir)] resource_packs = [
handle_default_minecraft_texture(home, output_dir)]
elif option == 'other': elif option == 'other':
folder_selected = filedialog.askdirectory() folder_selected = filedialog.askdirectory()
if folder_selected: if folder_selected:
@ -136,13 +150,15 @@ class TextureConverterGUI:
return return
# Convert resource packs # 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 # Update the GUI after conversion
self.loading_label.pack_forget() self.loading_label.pack_forget()
self.ok_button.config(state=tk.NORMAL) 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): def convert_all(self):
# Simulate a conversion process # Simulate a conversion process
@ -162,17 +178,20 @@ class TextureConverterGUI:
time.sleep(2) # Simulate some time for conversion time.sleep(2) # Simulate some time for conversion
def cancel_conversion(self): 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.") print("Conversion cancelled by user.")
self.loading_label.pack_forget() self.loading_label.pack_forget()
self.ok_button.config(state=tk.NORMAL) self.ok_button.config(state=tk.NORMAL)
self.cancel_button.config(state=tk.DISABLED) self.cancel_button.config(state=tk.DISABLED)
def main(): def main():
root = tk.Tk() root = tk.Tk()
app = TextureConverterGUI(root) app = TextureConverterGUI(root)
app.adjust_column_widths() app.adjust_column_widths()
root.mainloop() root.mainloop()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -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)

View File

@ -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 .config import SUPPORTED_MINECRAFT_VERSION, home
from PIL import Image from PIL import Image
from collections import Counter from collections import Counter
def detect_pixel_size(directory): def detect_pixel_size(directory):
sizes = [] sizes = []
for filename in glob.glob(directory + '/**/*.png', recursive=True): for filename in glob.glob(directory + '/**/*.png', recursive=True):
@ -11,22 +20,57 @@ def detect_pixel_size(directory):
if not sizes: if not sizes:
return 16 # Default to 16x16 if no PNG files are found return 16 # Default to 16x16 if no PNG files are found
most_common_size = Counter(sizes).most_common(1)[0][0] 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] return most_common_size[0]
def target_dir(directory, make_texture_pack, output_dir, output_dir_name, mineclone2_path):
def target_dir(
directory,
make_texture_pack,
output_dir,
output_dir_name,
mineclone2_path):
if make_texture_pack: if make_texture_pack:
return output_dir + "/" + output_dir_name return output_dir + "/" + output_dir_name
else: else:
return mineclone2_path + directory 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): def colorize(
colorize(colormap, source, colormap_pixel, texture_size, destination, tempfile2_name) colormap,
os.system("composite -compose Dst_In "+source+" "+tempfile2_name+" -alpha Set "+destination) 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 find_highest_minecraft_version(home, supported_version): def find_highest_minecraft_version(home, supported_version):
version_pattern = re.compile(re.escape(supported_version) + r"\.\d+") 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 highest_version = folder
return highest_version return highest_version
def find_all_minecraft_resourcepacks(): def find_all_minecraft_resourcepacks():
resourcepacks_dir = os.path.join(home, '.minecraft', 'resourcepacks') resourcepacks_dir = os.path.join(home, '.minecraft', 'resourcepacks')
@ -55,17 +100,20 @@ def find_all_minecraft_resourcepacks():
print(f"Adding resourcepack '{folder}'") print(f"Adding resourcepack '{folder}'")
resourcepacks.append(folder_path) resourcepacks.append(folder_path)
else: 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 return resourcepacks
def handle_default_minecraft_texture(home, output_dir): def handle_default_minecraft_texture(home, output_dir):
version = find_highest_minecraft_version(home, SUPPORTED_MINECRAFT_VERSION) version = find_highest_minecraft_version(home, SUPPORTED_MINECRAFT_VERSION)
if not version: if not version:
print("No suitable Minecraft version found.") print("No suitable Minecraft version found.")
sys.exit(1) 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): if not os.path.isfile(jar_file):
print("Minecraft JAR file not found.") print("Minecraft JAR file not found.")
sys.exit(1) sys.exit(1)
@ -85,14 +133,16 @@ def handle_default_minecraft_texture(home, output_dir):
extract_folder = os.path.normpath(extract_folder) extract_folder = os.path.normpath(extract_folder)
# Define the textures directory and normalize it # 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 # Using glob to find all files
all_files = glob.glob(f"{extract_folder}/**/*.*", recursive=True) all_files = glob.glob(f"{extract_folder}/**/*.*", recursive=True)
# Remove all non-png files except pack.mcmeta and pack.png in the root # Remove all non-png files except pack.mcmeta and pack.png in the root
for file_path in all_files: 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'): 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}") # print(f"Removing file: {file_path}")
os.remove(file_path) os.remove(file_path)
@ -104,7 +154,8 @@ def handle_default_minecraft_texture(home, output_dir):
shutil.rmtree(item_path, ignore_errors=True) shutil.rmtree(item_path, ignore_errors=True)
# Remove directories in 'minecraft' except for 'textures' # 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): for item in os.listdir(minecraft_directory):
item_path = os.path.join(minecraft_directory, item) item_path = os.path.join(minecraft_directory, item)
if os.path.isdir(item_path) and item != "textures": 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) shutil.rmtree(item_path, ignore_errors=True)
# Copy the textures directory to the output directory # Copy the textures directory to the output directory
output_textures_directory = os.path.join(output_dir, 'assets/minecraft/textures') output_textures_directory = os.path.join(
if os.path.exists(textures_directory) and not os.path.exists(output_textures_directory): 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) 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 # Copy pack.mcmeta and pack.png file if exists
for file_name in ['pack.mcmeta', 'pack.png']: for file_name in ['pack.mcmeta', 'pack.png']: