Refactor convert_textures code, seperated special cases into its own module

This commit is contained in:
James David 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

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

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

@ -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'
@ -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_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)
@ -53,7 +69,8 @@ def convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tem
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,264 +79,34 @@ 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 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)
# 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)
# 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)
# 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
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_banner_overlays(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)
["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)
# 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)
# Metadata
if make_texture_pack:

@ -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
@ -124,7 +137,8 @@ class TextureConverterGUI:
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()

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

@ -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):
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(
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 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,14 +133,16 @@ 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'):
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)
@ -104,7 +154,8 @@ def handle_default_minecraft_texture(home, output_dir):
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']: