mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-12-26 01:49:32 +01:00
Merge pull request 'Overhaul Texture_Converter.py and Conversion_Table.csv' (#4133) from Impulse/MineClone2:texture-conversion-120 into master
Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/4133 Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
This commit is contained in:
commit
b507838e13
15 changed files with 3030 additions and 1441 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -5,4 +5,6 @@
|
|||
*.blend3
|
||||
/.idea/
|
||||
*.xcf
|
||||
.Rproj.user
|
||||
.Rproj.user
|
||||
prompt.txt
|
||||
__pycache__
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,474 +1,42 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Texture Converter.
|
||||
# Converts Minecraft resource packs to Minetest texture packs.
|
||||
# See README.md.
|
||||
# cli.py
|
||||
|
||||
__author__ = "Wuzzy"
|
||||
__license__ = "MIT License"
|
||||
__status__ = "Development"
|
||||
import argparse
|
||||
import sys
|
||||
from libtextureconverter.gui import main as launch_gui
|
||||
from libtextureconverter.config import SUPPORTED_MINECRAFT_VERSION, working_dir, appname, home
|
||||
from libtextureconverter.utils import handle_default_minecraft_texture, find_all_minecraft_resourcepacks
|
||||
from libtextureconverter.common import convert_resource_packs
|
||||
|
||||
import shutil, csv, os, tempfile, sys, getopt
|
||||
def main():
|
||||
make_texture_pack = True
|
||||
parser = argparse.ArgumentParser(description=f"This is the official MineClone 2 Texture Converter. This will convert textures from Minecraft resource packs to a Minetest texture pack. Supported Minecraft version: {SUPPORTED_MINECRAFT_VERSION} (Java Edition)")
|
||||
parser.add_argument("-i", "--input", help="Directory of Minecraft resource pack to convert")
|
||||
parser.add_argument("-o", "--output", default=working_dir, help="Directory in which to put the resulting Minetest texture pack")
|
||||
parser.add_argument("-p", "--pixel-size", type=int, help="Size (in pixels) of the original textures")
|
||||
parser.add_argument("-d", "--dry-run", action="store_true", help="Pretend to convert textures without changing any files")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="Print out all copying actions")
|
||||
parser.add_argument("-def", "--default", action="store_true", help="Use the default Minecraft texture pack")
|
||||
parser.add_argument("-a", "--all", action="store_true", help="Convert all known Minecraft texturepacks")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Helper vars
|
||||
home = os.environ["HOME"]
|
||||
mineclone2_path = home + "/.minetest/games/mineclone2"
|
||||
working_dir = os.getcwd()
|
||||
appname = "Texture_Converter.py"
|
||||
if len(sys.argv) == 1:
|
||||
launch_gui()
|
||||
else:
|
||||
resource_packs = []
|
||||
if args.default:
|
||||
resource_packs.append(handle_default_minecraft_texture(home, args.output))
|
||||
elif args.all:
|
||||
resource_packs.extend(find_all_minecraft_resourcepacks())
|
||||
elif args.input:
|
||||
resource_packs.append(args.input)
|
||||
|
||||
### SETTINGS ###
|
||||
output_dir = working_dir
|
||||
if not resource_packs:
|
||||
print(f"ERROR: No valid resource packs specified. Use '{appname} -h' for help.")
|
||||
sys.exit(2)
|
||||
|
||||
base_dir = None
|
||||
convert_resource_packs(resource_packs, args.output, args.pixel_size, args.dry_run, args.verbose, make_texture_pack)
|
||||
|
||||
# If True, will only make console output but not convert anything.
|
||||
dry_run = False
|
||||
|
||||
# If True, textures will be put into a texture pack directory structure.
|
||||
# If False, textures will be put into MineClone 2 directories.
|
||||
make_texture_pack = True
|
||||
|
||||
# If True, prints all copying actions
|
||||
verbose = False
|
||||
|
||||
PXSIZE = 16
|
||||
|
||||
syntax_help = appname+""" -i <input dir> [-o <output dir>] [-d] [-v|-q] [-h]
|
||||
Mandatory argument:
|
||||
-i <input directory>
|
||||
Directory of Minecraft resource pack to convert
|
||||
|
||||
Optional arguments:
|
||||
-p <texture size>
|
||||
Specify the size (in pixels) of the original textures (default: 16)
|
||||
-o <output directory>
|
||||
Directory in which to put the resulting Minetest texture pack
|
||||
(default: working directory)
|
||||
-d
|
||||
Just pretend to convert textures and just print output, but do not actually
|
||||
change any files.
|
||||
-v
|
||||
Print out all copying actions
|
||||
-h
|
||||
Show this help and exit"""
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:],"hi:o:p:dv")
|
||||
except getopt.GetoptError:
|
||||
print(
|
||||
"""ERROR! The options you gave me make no sense!
|
||||
|
||||
Here's the syntax reference:""")
|
||||
print(syntax_help)
|
||||
sys.exit(2)
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
print(
|
||||
"""This is the official MineClone 2 Texture Converter.
|
||||
This will convert textures from Minecraft resource packs to
|
||||
a Minetest texture pack.
|
||||
|
||||
Supported Minecraft version: 1.12 (Java Edition)
|
||||
|
||||
Syntax:""")
|
||||
print(syntax_help)
|
||||
sys.exit()
|
||||
elif opt == "-d":
|
||||
dry_run = True
|
||||
elif opt == "-v":
|
||||
verbose = True
|
||||
elif opt == "-i":
|
||||
base_dir = arg
|
||||
elif opt == "-o":
|
||||
output_dir = arg
|
||||
elif opt == "-p":
|
||||
PXSIZE = int(arg)
|
||||
|
||||
if base_dir == None:
|
||||
print(
|
||||
"""ERROR: You didn't tell me the path to the Minecraft resource pack.
|
||||
Mind-reading has not been implemented yet.
|
||||
|
||||
Try this:
|
||||
"""+appname+""" -i <path to resource pack> -p <texture size>
|
||||
|
||||
For the full help, use:
|
||||
"""+appname+""" -h""")
|
||||
sys.exit(2);
|
||||
|
||||
### END OF SETTINGS ###
|
||||
|
||||
tex_dir = base_dir + "/assets/minecraft/textures"
|
||||
|
||||
# Get texture pack name (from directory name)
|
||||
bdir_split = base_dir.split("/")
|
||||
output_dir_name = bdir_split[-1]
|
||||
if len(output_dir_name) == 0:
|
||||
if len(bdir_split) >= 2:
|
||||
output_dir_name = base_dir.split("/")[-2]
|
||||
else:
|
||||
# Fallback
|
||||
output_dir_name = "New_MineClone_2_Texture_Pack"
|
||||
|
||||
# FUNCTION DEFINITIONS
|
||||
def colorize(colormap, source, colormap_pixel, texture_size, destination):
|
||||
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):
|
||||
colorize(colormap, source, colormap_pixel, texture_size, tempfile2.name)
|
||||
os.system("composite -compose Dst_In "+source+" "+tempfile2.name+" -alpha Set "+destination)
|
||||
|
||||
def target_dir(directory):
|
||||
if make_texture_pack:
|
||||
return output_dir + "/" + output_dir_name
|
||||
else:
|
||||
return mineclone2_path + directory
|
||||
|
||||
# Copy texture files
|
||||
def convert_textures():
|
||||
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
|
||||
for row in reader:
|
||||
# Skip first row
|
||||
if first_row:
|
||||
first_row = False
|
||||
continue
|
||||
|
||||
src_dir = row[0]
|
||||
src_filename = row[1]
|
||||
dst_dir = row[2]
|
||||
dst_filename = row[3]
|
||||
if row[4] != "":
|
||||
xs = int(row[4])
|
||||
ys = int(row[5])
|
||||
xl = int(row[6])
|
||||
yl = int(row[7])
|
||||
xt = int(row[8])
|
||||
yt = int(row[9])
|
||||
else:
|
||||
xs = None
|
||||
blacklisted = row[10]
|
||||
|
||||
if blacklisted == "y":
|
||||
# Skip blacklisted files
|
||||
continue
|
||||
|
||||
if make_texture_pack == False and dst_dir == "":
|
||||
# If destination dir is empty, this texture is not supposed to be used in MCL2
|
||||
# (but maybe an external mod). It should only be used in texture packs.
|
||||
# Otherwise, it must be ignored.
|
||||
# Example: textures for mcl_supplemental
|
||||
continue
|
||||
|
||||
src_file = base_dir + src_dir + "/" + src_filename # source file
|
||||
src_file_exists = os.path.isfile(src_file)
|
||||
dst_file = target_dir(dst_dir) + "/" + dst_filename # destination file
|
||||
|
||||
if src_file_exists == False:
|
||||
print("WARNING: Source file does not exist: "+src_file)
|
||||
failed_conversions = failed_conversions + 1
|
||||
continue
|
||||
|
||||
if xs != None:
|
||||
# Crop and copy images
|
||||
if not dry_run:
|
||||
os.system("convert "+src_file+" -crop "+xl+"x"+yl+"+"+xs+"+"+ys+" "+dst_file)
|
||||
if verbose:
|
||||
print(src_file + " → " + dst_file)
|
||||
else:
|
||||
# Copy image verbatim
|
||||
if not dry_run:
|
||||
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") + "/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"), "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"), "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"), "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"), "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"), "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"), "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"), "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"), "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"), "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"), "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"), "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.
|
||||
# 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_normal.png", "rail_normal_turned.png", "default_rail_t_junction.png", "default_rail_crossing.png"),
|
||||
("rail_golden.png", "rail_normal_turned.png", "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"),
|
||||
("rail_golden_powered.png", "rail_normal_turned.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"),
|
||||
("rail_detector.png", "rail_normal_turned.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"),
|
||||
("rail_detector_powered.png", "rail_normal_turned.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"),
|
||||
("rail_activator.png", "rail_normal_turned.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"),
|
||||
("rail_activator_powered.png", "rail_normal_turned.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+"/blocks/"+r[0]+" "+tex_dir+"/blocks/"+r[1]+" "+target_dir("/mods/ENTITIES/mcl_minecarts/textures")+"/"+r[2])
|
||||
os.system("convert "+tex_dir+"/blocks/"+r[0]+" -rotate 90 "+tempfile1.name)
|
||||
os.system("composite -compose Dst_Over "+tempfile1.name+" "+tex_dir+"/blocks/"+r[0]+" "+target_dir("/mods/ENTITIES/mcl_minecarts/textures")+"/"+r[3])
|
||||
|
||||
# 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")+"/"+"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
|
||||
grass_file = tex_dir + "/blocks/grass_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+"/blocks/leaves_oak.png", "116+143", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_leaves.png")
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_big_oak.png", "158+177", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_leaves_big_oak.png")
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_acacia.png", "40+255", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_acacia_leaves.png")
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_spruce.png", "226+230", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_leaves_spruce.png")
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_birch.png", "141+186", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_leaves_birch.png")
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_jungle.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_jungleleaves.png")
|
||||
|
||||
# Waterlily
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/waterlily.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/flowers_waterlily.png")
|
||||
|
||||
# Vines
|
||||
colorize_alpha(FOLIAG, tex_dir+"/blocks/vine.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_vine.png")
|
||||
|
||||
# Tall grass, fern (inventory images)
|
||||
pcol = "50+173" # Plains grass color
|
||||
colorize_alpha(GRASS, tex_dir+"/blocks/tallgrass.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_tallgrass_inv.png")
|
||||
colorize_alpha(GRASS, tex_dir+"/blocks/fern.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_fern_inv.png")
|
||||
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_fern_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_fern_inv.png")
|
||||
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_grass_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_grass_inv.png")
|
||||
|
||||
# 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("/mods/ITEMS/mcl_core/textures") + "/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
|
||||
if make_texture_pack:
|
||||
# Create description file
|
||||
description = "Texture pack for MineClone 2. Automatically converted from a Minecraft resource pack by the MineClone 2 Texture Converter. Size: "+str(PXSIZE)+"×"+str(PXSIZE)
|
||||
description_file = open(target_dir("/") + "/description.txt", "w")
|
||||
description_file.write(description)
|
||||
description_file.close()
|
||||
|
||||
# Create preview image (screenshot.png)
|
||||
os.system("convert -size 300x200 canvas:transparent "+target_dir("/") + "/screenshot.png")
|
||||
os.system("composite "+base_dir+"/pack.png "+target_dir("/") + "/screenshot.png -gravity center "+target_dir("/") + "/screenshot.png")
|
||||
|
||||
print("Textures conversion COMPLETE!")
|
||||
if failed_conversions > 0:
|
||||
print("WARNING: Number of missing files in original resource pack: "+str(failed_conversions))
|
||||
print("NOTE: Please keep in mind this script does not reliably convert all the textures yet.")
|
||||
if make_texture_pack:
|
||||
print("You can now retrieve the texture pack in "+output_dir+"/"+output_dir_name+"/")
|
||||
|
||||
# ENTRY POINT
|
||||
if make_texture_pack and not os.path.isdir(output_dir+"/"+output_dir_name):
|
||||
os.mkdir(output_dir+"/"+output_dir_name)
|
||||
|
||||
tempfile1 = tempfile.NamedTemporaryFile()
|
||||
tempfile2 = tempfile.NamedTemporaryFile()
|
||||
|
||||
convert_textures()
|
||||
|
||||
tempfile1.close()
|
||||
tempfile2.close()
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
0
tools/libtextureconverter/__init__.py
Normal file
0
tools/libtextureconverter/__init__.py
Normal file
68
tools/libtextureconverter/common.py
Normal file
68
tools/libtextureconverter/common.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
import shutil
|
||||
import csv
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
import argparse
|
||||
import glob
|
||||
from PIL import Image
|
||||
from collections import Counter
|
||||
|
||||
from libtextureconverter.utils import detect_pixel_size, target_dir, colorize, colorize_alpha, handle_default_minecraft_texture, find_all_minecraft_resourcepacks
|
||||
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):
|
||||
for base_dir in resource_packs:
|
||||
print(f"Converting resource pack: {base_dir}")
|
||||
|
||||
# Autodetect pixel size if not provided
|
||||
if not PXSIZE:
|
||||
pixel_size = detect_pixel_size(base_dir)
|
||||
else:
|
||||
pixel_size = PXSIZE
|
||||
# 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
|
||||
output_dir_name = os.path.basename(os.path.normpath(base_dir))
|
||||
|
||||
# Create the output directory if it doesn't exist
|
||||
output_path = os.path.join(output_dir, output_dir_name)
|
||||
if not os.path.isdir(output_path):
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
|
||||
# Temporary files for conversion (if needed by your conversion process)
|
||||
tempfile1 = tempfile.NamedTemporaryFile(delete=False)
|
||||
tempfile2 = tempfile.NamedTemporaryFile(delete=False)
|
||||
|
||||
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)
|
||||
finally:
|
||||
# Clean up temporary files
|
||||
tempfile1.close()
|
||||
os.unlink(tempfile1.name)
|
||||
tempfile2.close()
|
||||
os.unlink(tempfile2.name)
|
||||
|
||||
print(f"Finished converting resource pack: {base_dir}")
|
30
tools/libtextureconverter/config.py
Normal file
30
tools/libtextureconverter/config.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import os
|
||||
import platform
|
||||
|
||||
def get_minetest_directory():
|
||||
system = platform.system()
|
||||
|
||||
# Windows
|
||||
if system == 'Windows':
|
||||
return os.environ.get('MINETEST_USER_PATH', os.path.expandvars('%APPDATA%\\Minetest'))
|
||||
|
||||
# Linux
|
||||
elif system == 'Linux':
|
||||
return os.environ.get('MINETEST_USER_PATH', os.path.expanduser('~/.minetest'))
|
||||
|
||||
# macOS
|
||||
elif system == 'Darwin': # Darwin is the system name for macOS
|
||||
return os.environ.get('MINETEST_USER_PATH', os.path.expanduser('~/Library/Application Support/minetest'))
|
||||
|
||||
# Unsupported system
|
||||
else:
|
||||
return None
|
||||
|
||||
# Constants
|
||||
SUPPORTED_MINECRAFT_VERSION = "1.20"
|
||||
|
||||
# Helper vars
|
||||
home = os.environ["HOME"]
|
||||
mineclone2_path = os.path.join(get_minetest_directory(),"games","mineclone2")
|
||||
working_dir = os.getcwd()
|
||||
appname = "Texture_Converter.py"
|
134
tools/libtextureconverter/convert.py
Normal file
134
tools/libtextureconverter/convert.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
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
|
||||
import csv
|
||||
import os
|
||||
import tempfile
|
||||
import sys
|
||||
import argparse
|
||||
import glob
|
||||
|
||||
|
||||
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
|
||||
with open("Conversion_Table.csv", newline="") as csvfile:
|
||||
reader = csv.reader(csvfile, delimiter=",", quotechar='"')
|
||||
first_row = True
|
||||
for row in reader:
|
||||
# Skip first row
|
||||
if first_row:
|
||||
first_row = False
|
||||
continue
|
||||
src_dir = row[0]
|
||||
src_filename = row[1]
|
||||
dst_dir = './textures'
|
||||
dst_filename = row[2]
|
||||
if row[4] != "":
|
||||
xs = int(row[3])
|
||||
ys = int(row[4])
|
||||
xl = int(row[5])
|
||||
yl = int(row[6])
|
||||
xt = int(row[7])
|
||||
yt = int(row[8])
|
||||
else:
|
||||
xs = None
|
||||
blacklisted = row[9]
|
||||
|
||||
if blacklisted == "y":
|
||||
# Skip blacklisted files
|
||||
continue
|
||||
|
||||
if make_texture_pack == False and dst_dir == "":
|
||||
# If destination dir is empty, this texture is not supposed to be used in MCL2
|
||||
# (but maybe an external mod). It should only be used in texture packs.
|
||||
# Otherwise, it must be ignored.
|
||||
# Example: textures for mcl_supplemental
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
if src_file_exists == False:
|
||||
print("WARNING: Source file does not exist: " + src_file)
|
||||
failed_conversions = failed_conversions + 1
|
||||
continue
|
||||
if xs != None:
|
||||
# Crop and copy images
|
||||
if not dry_run:
|
||||
crop_width = int(xl)
|
||||
crop_height = int(yl)
|
||||
offset_x = int(xs)
|
||||
offset_y = int(ys)
|
||||
with Image(filename=src_file) as img:
|
||||
# Crop the image
|
||||
img.crop(left=offset_x, top=offset_y, width=crop_width, height=crop_height)
|
||||
# Save the result
|
||||
img.save(filename=dst_file)
|
||||
if verbose:
|
||||
print(src_file + " → " + dst_file)
|
||||
else:
|
||||
# Copy image verbatim
|
||||
if not dry_run:
|
||||
shutil.copy2(src_file, dst_file)
|
||||
if verbose:
|
||||
print(src_file + " → " + dst_file)
|
||||
return failed_conversions
|
||||
|
||||
|
||||
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
|
||||
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 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:
|
||||
# Create description file
|
||||
description = "Texture pack for MineClone 2. Automatically converted from a Minecraft resource pack by the MineClone 2 Texture Converter. Size: "+str(PXSIZE)+"×"+str(PXSIZE)
|
||||
description_file = open(target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/description.txt", "w")
|
||||
description_file.write(description)
|
||||
description_file.close()
|
||||
|
||||
# Create preview image (screenshot.png)
|
||||
os.system("convert -size 300x200 canvas:transparent "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png")
|
||||
os.system("composite "+base_dir+"/pack.png "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png -gravity center "+target_dir("/", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/screenshot.png")
|
||||
|
||||
print("Textures conversion COMPLETE!")
|
||||
if failed_conversions > 0:
|
||||
print("WARNING: Number of missing files in original resource pack: " + str(failed_conversions))
|
||||
print("NOTE: Please keep in mind this script does not reliably convert all the textures yet.")
|
||||
if make_texture_pack:
|
||||
print("You can now retrieve the texture pack in " + output_dir + "/" + output_dir_name + "/")
|
198
tools/libtextureconverter/gui.py
Normal file
198
tools/libtextureconverter/gui.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox, ttk, font
|
||||
from libtextureconverter.utils import handle_default_minecraft_texture, find_all_minecraft_resourcepacks
|
||||
from libtextureconverter.config import home, get_minetest_directory
|
||||
from libtextureconverter.common import convert_resource_packs
|
||||
|
||||
import time
|
||||
import os
|
||||
import threading
|
||||
|
||||
|
||||
class TextureConverterGUI:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("Choose resource packs to convert")
|
||||
|
||||
self.create_widgets()
|
||||
|
||||
def create_widgets(self):
|
||||
|
||||
# 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 folder?").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.heading('Convert', text='Convert')
|
||||
self.tree.heading('Description', text='Description')
|
||||
|
||||
# Inserting options into the table
|
||||
entries = [
|
||||
('all', 'Find Minecraft resource packs installed in your minecraft folders and convert those automatically'),
|
||||
('default', 'Convert the default resource pack'),
|
||||
('other', 'Choose a folder to convert manually')
|
||||
]
|
||||
|
||||
for entry in entries:
|
||||
self.tree.insert('', 'end', values=entry)
|
||||
|
||||
# Button Frame
|
||||
self.button_frame = tk.Frame(self.root)
|
||||
# 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.pack(side=tk.RIGHT, padx=5)
|
||||
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)
|
||||
|
||||
self.root.after(1, self.adjust_column_widths)
|
||||
|
||||
def adjust_column_widths(self):
|
||||
self.root.update_idletasks() # Update the geometry of the widgets
|
||||
|
||||
# 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
|
||||
|
||||
# Apply the column widths
|
||||
self.tree.column('Convert', width=convert_width, anchor='center')
|
||||
self.tree.column('Description', width=description_width, anchor='w')
|
||||
|
||||
# Calculate the height for each row
|
||||
row_height = tk.font.Font().metrics('linespace') + 2
|
||||
|
||||
# Adjust the Treeview height
|
||||
num_items = len(self.tree.get_children())
|
||||
tree_height = (row_height * num_items) * 1.8
|
||||
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
|
||||
|
||||
# Calculate the total width needed
|
||||
total_width = convert_width + description_width + 20
|
||||
|
||||
# Set the size of the window based on content
|
||||
self.root.geometry(f"{int(total_width)}x{int(total_height)}")
|
||||
|
||||
# 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
|
||||
self.root.update_idletasks()
|
||||
|
||||
def confirm_selection(self):
|
||||
self.cancel_button.config(state=tk.NORMAL)
|
||||
selected_item = self.tree.focus()
|
||||
selection = self.tree.item(selected_item)
|
||||
option = selection['values'][0]
|
||||
self.show_loading_screen(option)
|
||||
|
||||
def set_min_window_size(self):
|
||||
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.pack()
|
||||
|
||||
# Start the conversion process in a separate thread
|
||||
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
|
||||
self.ok_button.config(state=tk.DISABLED)
|
||||
self.cancel_button.config(state=tk.NORMAL)
|
||||
|
||||
def perform_conversion(self, option):
|
||||
# Set default values for pixelsize, dry_run, and verbose
|
||||
pixelsize = None
|
||||
dry_run = False
|
||||
verbose = False
|
||||
output_dir = os.path.join(get_minetest_directory(), "textures")
|
||||
make_texture_pack = True
|
||||
|
||||
# Determine the resource packs to convert based on the option
|
||||
if option == 'all':
|
||||
resource_packs = find_all_minecraft_resourcepacks()
|
||||
elif option == 'default':
|
||||
resource_packs = [
|
||||
handle_default_minecraft_texture(home, output_dir)]
|
||||
elif option == 'other':
|
||||
folder_selected = filedialog.askdirectory()
|
||||
if folder_selected:
|
||||
resource_packs = [folder_selected]
|
||||
else:
|
||||
# User canceled the folder selection
|
||||
self.loading_label.pack_forget()
|
||||
self.ok_button.config(state=tk.NORMAL)
|
||||
return
|
||||
|
||||
# Convert resource packs
|
||||
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.")
|
||||
|
||||
def convert_all(self):
|
||||
# Simulate a conversion process
|
||||
print("Converting all resource packs")
|
||||
time.sleep(2) # Simulate some time for conversion
|
||||
|
||||
def convert_default(self):
|
||||
# Simulate a conversion process
|
||||
print("Converting default resource pack")
|
||||
time.sleep(2) # Simulate some time for conversion
|
||||
|
||||
def open_folder_dialog(self):
|
||||
folder_selected = filedialog.askdirectory()
|
||||
if folder_selected:
|
||||
# Simulate a conversion process
|
||||
print(f"Folder selected for conversion: {folder_selected}")
|
||||
time.sleep(2) # Simulate some time for conversion
|
||||
|
||||
def cancel_conversion(self):
|
||||
# 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()
|
809
tools/libtextureconverter/special_convert_cases.py
Normal file
809
tools/libtextureconverter/special_convert_cases.py
Normal file
|
@ -0,0 +1,809 @@
|
|||
import os
|
||||
from .utils import target_dir, colorize, colorize_alpha
|
||||
import shutil
|
||||
import csv
|
||||
import tempfile
|
||||
import sys
|
||||
import argparse
|
||||
import glob
|
||||
from wand.image import Image
|
||||
from wand.color import Color
|
||||
from wand.display import display
|
||||
from wand.drawing import Drawing
|
||||
import warnings
|
||||
|
||||
# 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):
|
||||
destination_path = target_dir("/mods/ITEMS/mcl_maps/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/mcl_maps_map_background.png"
|
||||
|
||||
with Image(filename=map_background_file) as img:
|
||||
# Resize the image with 'point' filter
|
||||
img.resize(140, 140, filter='point')
|
||||
|
||||
# Save the result
|
||||
img.save(filename=destination_path)
|
||||
|
||||
|
||||
# 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]
|
||||
# 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 *
|
||||
2) +
|
||||
"+0 -crop " +
|
||||
str(APXSIZE *
|
||||
2) +
|
||||
"x" +
|
||||
str(APXSIZE) +
|
||||
"+0+0 \\) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" " +
|
||||
helmet)
|
||||
|
||||
|
||||
|
||||
# chestplate
|
||||
with Image(width=APXSIZE * 4, height=APXSIZE * 2, background=Color('none')) as img:
|
||||
# Load layer_1 and scale
|
||||
with Image(filename=layer_1) as layer1:
|
||||
layer1.resize(APXSIZE * 4, APXSIZE * 2)
|
||||
|
||||
# Define the crop geometry
|
||||
crop_width = int(APXSIZE * 2.5)
|
||||
crop_height = APXSIZE
|
||||
crop_x = APXSIZE
|
||||
crop_y = APXSIZE
|
||||
|
||||
# Crop the image
|
||||
layer1.crop(crop_x, crop_y, width=crop_width, height=crop_height)
|
||||
|
||||
# Composite layer1 over the transparent image
|
||||
img.composite(layer1, APXSIZE, APXSIZE)
|
||||
|
||||
# Apply channel operation
|
||||
img.fx("a > 0.0 ? 1.0 : 0.0", channel='alpha')
|
||||
|
||||
# Save the result
|
||||
img.save(filename=chestplate)
|
||||
with Image(width=APXSIZE * 4, height=APXSIZE * 2, background=Color('none')) as img:
|
||||
with Image(filename=layer_1) as layer1:
|
||||
# Scale the image
|
||||
layer1.resize(APXSIZE * 4, APXSIZE * 2)
|
||||
|
||||
# Crop the image
|
||||
crop_x = 0
|
||||
crop_y = APXSIZE
|
||||
crop_width = APXSIZE
|
||||
crop_height = APXSIZE
|
||||
layer1.crop(crop_x, crop_y, width=crop_width, height=crop_height)
|
||||
|
||||
# Composite the cropped image over the transparent image
|
||||
img.composite(layer1, 0, APXSIZE)
|
||||
|
||||
# Apply the channel operation
|
||||
img.fx("a > 0.0 ? 1.0 : 0.0", channel='alpha')
|
||||
|
||||
# Save the result
|
||||
img.save(filename=boots)
|
||||
|
||||
if os.path.isfile(layer_2):
|
||||
leggings = adir + "/" + a[5]
|
||||
with Image(width=APXSIZE * 4, height=APXSIZE * 2, background=Color('none')) as img:
|
||||
with Image(filename=layer_2) as layer2:
|
||||
# Scale the image
|
||||
layer2.resize(APXSIZE * 4, APXSIZE * 2)
|
||||
|
||||
# Apply geometry and crop
|
||||
crop_width = int(APXSIZE * 2.5)
|
||||
crop_height = APXSIZE
|
||||
crop_x = 0
|
||||
crop_y = APXSIZE
|
||||
layer2.crop(left=crop_x, top=crop_y, width=crop_width, height=crop_height)
|
||||
|
||||
# Composite the cropped image over the transparent image
|
||||
img.composite(layer2, 0, APXSIZE)
|
||||
|
||||
# Apply channel operation
|
||||
img.fx("a > 0.0 ? 1.0 : 0.0", channel='alpha')
|
||||
|
||||
# Save the result
|
||||
img.save(filename=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)
|
211
tools/libtextureconverter/utils.py
Normal file
211
tools/libtextureconverter/utils.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
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 collections import Counter
|
||||
import platform
|
||||
from wand.image import Image
|
||||
from wand.color import Color
|
||||
from wand.display import display
|
||||
import warnings
|
||||
|
||||
def detect_pixel_size(directory):
|
||||
from PIL import Image
|
||||
sizes = []
|
||||
for filename in glob.glob(directory + '/**/*.png', recursive=True):
|
||||
with Image.open(filename) as img:
|
||||
sizes.append(img.size)
|
||||
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]}")
|
||||
return most_common_size[0]
|
||||
|
||||
def target_dir(
|
||||
directory,
|
||||
make_texture_pack,
|
||||
output_dir,
|
||||
output_dir_name,
|
||||
mineclone2_path):
|
||||
if make_texture_pack:
|
||||
return output_dir + "/" + output_dir_name
|
||||
else:
|
||||
return mineclone2_path + directory
|
||||
|
||||
|
||||
def colorize(colormap, source, colormap_pixel, texture_size, destination, tempfile1_name):
|
||||
try:
|
||||
# Convert the colormap_pixel to integer coordinates
|
||||
x, y = map(int, colormap_pixel.split('+'))
|
||||
|
||||
# Define texture size as integer
|
||||
texture_size = int(texture_size)
|
||||
|
||||
with Image(filename=colormap) as img:
|
||||
# Crop the image
|
||||
img.crop(x, y, width=1, height=1)
|
||||
|
||||
# Set depth (This might be ignored by Wand as it manages depth automatically)
|
||||
img.depth = 8
|
||||
|
||||
# Resize the image
|
||||
img.resize(texture_size, texture_size)
|
||||
|
||||
# Save the result
|
||||
img.save(filename=tempfile1_name)
|
||||
|
||||
except Exception as e:
|
||||
warnings.warn(f"An error occurred during the first image processing operation: {e}")
|
||||
|
||||
try:
|
||||
# Load the images
|
||||
with Image(filename=tempfile1_name) as top_image:
|
||||
with Image(filename=source) as bottom_image:
|
||||
# Perform composite operation with Multiply blend mode
|
||||
bottom_image.composite(top_image, 0, 0, operator='multiply')
|
||||
|
||||
# Save the result
|
||||
bottom_image.save(filename=destination)
|
||||
|
||||
except Exception as e:
|
||||
warnings.warn(f"An error occurred during the second image processing operation: {e}")
|
||||
|
||||
|
||||
def colorize_alpha(
|
||||
colormap,
|
||||
source,
|
||||
colormap_pixel,
|
||||
texture_size,
|
||||
destination,
|
||||
tempfile2_name):
|
||||
colorize(colormap, source, colormap_pixel,
|
||||
texture_size, destination, tempfile2_name)
|
||||
try:
|
||||
with Image(filename=source) as source_image:
|
||||
with Image(filename=tempfile2_name) as tempfile2_image:
|
||||
# Perform composite operation with Dst_In blend mode
|
||||
tempfile2_image.composite(source_image, 0, 0, operator='dst_in')
|
||||
|
||||
# Set alpha channel
|
||||
tempfile2_image.alpha_channel = 'set'
|
||||
|
||||
# Save the result
|
||||
tempfile2_image.save(filename=destination)
|
||||
except Exception as e:
|
||||
warnings.warn(f"An error occurred during the second image processing operation: {e}")
|
||||
|
||||
|
||||
def find_highest_minecraft_version(home, supported_version):
|
||||
version_pattern = re.compile(re.escape(supported_version) + r"\.\d+")
|
||||
versions_dir = os.path.join(home, ".minecraft", "versions")
|
||||
highest_version = None
|
||||
if os.path.isdir(versions_dir):
|
||||
for folder in os.listdir(versions_dir):
|
||||
if version_pattern.match(folder):
|
||||
if not highest_version or folder > highest_version:
|
||||
highest_version = folder
|
||||
return highest_version
|
||||
|
||||
|
||||
def find_all_minecraft_resourcepacks():
|
||||
resourcepacks_dir = os.path.join(home, '.minecraft', 'resourcepacks')
|
||||
|
||||
if not os.path.isdir(resourcepacks_dir):
|
||||
print(f"Resource packs directory not found: {resourcepacks_dir}")
|
||||
return
|
||||
|
||||
resourcepacks = []
|
||||
for folder in os.listdir(resourcepacks_dir):
|
||||
folder_path = os.path.join(resourcepacks_dir, folder)
|
||||
if os.path.isdir(folder_path):
|
||||
pack_png_path = os.path.join(folder_path, 'pack.png')
|
||||
if os.path.isfile(pack_png_path):
|
||||
print(f"Adding resourcepack '{folder}'")
|
||||
resourcepacks.append(folder_path)
|
||||
else:
|
||||
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")
|
||||
if not os.path.isfile(jar_file):
|
||||
print("Minecraft JAR file not found.")
|
||||
sys.exit(1)
|
||||
|
||||
temp_zip = f"/tmp/mc-default-{version.replace('.', '')}.zip"
|
||||
shutil.copy2(jar_file, temp_zip)
|
||||
|
||||
extract_folder = temp_zip.replace(".zip", "")
|
||||
with zipfile.ZipFile(temp_zip, 'r') as zip_ref:
|
||||
zip_ref.extractall(extract_folder)
|
||||
|
||||
if not os.path.exists(extract_folder):
|
||||
print(f"Extraction failed, folder not found: {extract_folder}")
|
||||
sys.exit(1)
|
||||
|
||||
# Normalize the extract folder path
|
||||
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")
|
||||
|
||||
# Using glob to find all files
|
||||
all_files = glob.glob(f"{extract_folder}/**/*.*", recursive=True)
|
||||
|
||||
# Remove all non-png files except pack.mcmeta and pack.png in the root
|
||||
for file_path in all_files:
|
||||
if not file_path.endswith('.png') and not file_path.endswith(
|
||||
'pack.mcmeta') and not file_path.endswith('pack.png'):
|
||||
# print(f"Removing file: {file_path}")
|
||||
os.remove(file_path)
|
||||
|
||||
# Remove all directories in the root except 'assets'
|
||||
for item in os.listdir(extract_folder):
|
||||
item_path = os.path.join(extract_folder, item)
|
||||
if os.path.isdir(item_path) and item != "assets":
|
||||
# print(f"Removing directory: {item_path}")
|
||||
shutil.rmtree(item_path, ignore_errors=True)
|
||||
|
||||
# Remove directories in 'minecraft' except for 'textures'
|
||||
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":
|
||||
print(f"Removing directory: {item_path}")
|
||||
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):
|
||||
os.makedirs(os.path.dirname(output_textures_directory), 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']:
|
||||
file_path = os.path.join(extract_folder, file_name)
|
||||
if os.path.exists(file_path):
|
||||
shutil.copy(file_path, output_dir)
|
||||
|
||||
print(f"Filtered and extracted to: {extract_folder}")
|
||||
return extract_folder
|
2
tools/requirements.txt
Normal file
2
tools/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Pillow
|
||||
Wand
|
|
@ -0,0 +1,38 @@
|
|||
import csv
|
||||
import os
|
||||
|
||||
def validate_csv(file_path):
|
||||
with open(file_path, newline='') as csvfile:
|
||||
reader = csv.reader(csvfile, delimiter=',', quotechar='"')
|
||||
line_num = 1
|
||||
for row in reader:
|
||||
# Skip the header
|
||||
if line_num == 1:
|
||||
line_num += 1
|
||||
continue
|
||||
|
||||
# Check if row has correct number of columns
|
||||
if len(row) != 10:
|
||||
print(f"Warning: Line {line_num} is not a valid CSV row.")
|
||||
line_num += 1
|
||||
continue
|
||||
|
||||
# Validate source path
|
||||
if "/assets/minecraft/" not in row[0]:
|
||||
print(f"Warning: Line {line_num} does not contain '/assets/minecraft/' in the source path.")
|
||||
|
||||
# Validate Source file and Target file
|
||||
if not row[1].endswith('.png'):
|
||||
print(f"Warning: Line {line_num} has an invalid or missing Source file. It should end with '.png'.")
|
||||
if not row[2].endswith('.png'):
|
||||
print(f"Warning: Line {line_num} has an invalid or missing Target file. It should end with '.png'.")
|
||||
|
||||
line_num += 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
csv_file = 'Conversion_Table.csv'
|
||||
if os.path.exists(csv_file):
|
||||
validate_csv(csv_file)
|
||||
print("Validated CSV, if no warnings or errors, your good!")
|
||||
else:
|
||||
print(f"Error: The file '{csv_file}' does not exist.")
|
40
tools/texture_conversion_extra_tools/new_table_conversion.py
Normal file
40
tools/texture_conversion_extra_tools/new_table_conversion.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
import csv
|
||||
|
||||
def read_csv(file_path):
|
||||
with open(file_path, mode='r', encoding='utf-8') as file:
|
||||
return list(csv.reader(file))
|
||||
|
||||
def write_csv(file_path, data):
|
||||
with open(file_path, mode='w', encoding='utf-8', newline='') as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerows(data)
|
||||
|
||||
def merge_tables(original_csv, new_csv):
|
||||
# Convert the lists to dictionaries for easier manipulation
|
||||
original_dict = {row[3]: row for row in original_csv}
|
||||
new_dict = {row[3]: row for row in new_csv}
|
||||
|
||||
# Update or add new entries
|
||||
for key in new_dict:
|
||||
original_dict[key] = new_dict[key]
|
||||
|
||||
# Convert the dictionary back to a list
|
||||
merged_data = list(original_dict.values())
|
||||
|
||||
return merged_data
|
||||
|
||||
def main():
|
||||
original_csv_path = './Conversion_Table.csv'
|
||||
new_csv_path = './Conversion_Table_New.csv'
|
||||
|
||||
original_csv = read_csv(original_csv_path)
|
||||
new_csv = read_csv(new_csv_path)
|
||||
|
||||
# Skip the header row in new_csv
|
||||
merged_data = merge_tables(original_csv, new_csv[1:])
|
||||
|
||||
write_csv(original_csv_path, merged_data)
|
||||
print("Conversion tables have been merged and updated successfully.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
36
tools/texture_conversion_extra_tools/outstanding_conv.py
Normal file
36
tools/texture_conversion_extra_tools/outstanding_conv.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import csv
|
||||
|
||||
def read_missing_textures(file_path):
|
||||
with open(file_path, 'r') as file:
|
||||
return [line.strip().split('/')[-1] for line in file.readlines()]
|
||||
|
||||
def read_conversion_table(file_path):
|
||||
with open(file_path, 'r') as file:
|
||||
return list(csv.reader(file))
|
||||
|
||||
def find_outstanding_entries(missing_textures, conversion_table):
|
||||
outstanding_entries = []
|
||||
for row in conversion_table:
|
||||
if row[1] in missing_textures:
|
||||
outstanding_entries.append(row)
|
||||
return outstanding_entries
|
||||
|
||||
def write_outstanding_entries(file_path, outstanding_entries):
|
||||
with open(file_path, 'w', newline='') as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerows(outstanding_entries)
|
||||
|
||||
def main():
|
||||
missing_textures_file = './missing_textures_filtered.txt'
|
||||
conversion_table_file = './Conversion_Table.csv'
|
||||
output_file = './Conversion_Table_Outstanding.csv'
|
||||
|
||||
missing_textures = read_missing_textures(missing_textures_file)
|
||||
conversion_table = read_conversion_table(conversion_table_file)
|
||||
outstanding_entries = find_outstanding_entries(missing_textures, conversion_table)
|
||||
|
||||
write_outstanding_entries(output_file, outstanding_entries)
|
||||
print("Outstanding conversion table entries written to:", output_file)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
15
tools/texture_conversion_extra_tools/remove_null_lines.py
Normal file
15
tools/texture_conversion_extra_tools/remove_null_lines.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
def remove_null_lines(input_file, output_file):
|
||||
with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
|
||||
for line in infile:
|
||||
if "NULL" not in line:
|
||||
outfile.write(line)
|
||||
|
||||
def main():
|
||||
input_file = './Conversion_Table.csv' # Replace with your input file path
|
||||
output_file = './Conversion_Table_New.csv' # Replace with your output file path
|
||||
|
||||
remove_null_lines(input_file, output_file)
|
||||
print("File processed successfully, NULL lines removed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue