mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-21 18:21:04 +01:00
Refactored code: split up into different functions and modules.
This commit is contained in:
parent
a25bdfbc90
commit
a6753806cc
6 changed files with 385 additions and 375 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,3 +6,5 @@
|
||||||
/.idea/
|
/.idea/
|
||||||
*.xcf
|
*.xcf
|
||||||
.Rproj.user
|
.Rproj.user
|
||||||
|
prompt.txt
|
||||||
|
__pycache__
|
||||||
|
|
|
@ -12,25 +12,9 @@ import shutil, csv, os, tempfile, sys, argparse, glob
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
# Constants
|
from libtextureconverter.utils import detect_pixel_size, target_dir, colorize, colorize_alpha
|
||||||
SUPPORTED_MINECRAFT_VERSION="1.20"
|
from libtextureconverter.convert import convert_textures
|
||||||
|
from libtextureconverter.config import SUPPORTED_MINECRAFT_VERSION, working_dir, mineclone2_path, appname
|
||||||
# Helper vars
|
|
||||||
home = os.environ["HOME"]
|
|
||||||
mineclone2_path = home + "/.minetest/games/mineclone2"
|
|
||||||
working_dir = os.getcwd()
|
|
||||||
appname = "Texture_Converter.py"
|
|
||||||
|
|
||||||
def detect_pixel_size(directory):
|
|
||||||
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]
|
|
||||||
|
|
||||||
# Argument parsing
|
# Argument parsing
|
||||||
description_text = f"""This is the official MineClone 2 Texture Converter.
|
description_text = f"""This is the official MineClone 2 Texture Converter.
|
||||||
|
@ -88,360 +72,6 @@ if len(output_dir_name) == 0:
|
||||||
# Fallback
|
# Fallback
|
||||||
output_dir_name = "New_MineClone_2_Texture_Pack"
|
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 = './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) + "/" + 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.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")+"/"+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")+"/"+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 + "/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")+"/default_leaves.png")
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/dark_oak_leaves.png", "158+177", str(PXSIZE), target_dir("/textures")+"/mcl_core_leaves_big_oak.png")
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/acacia_leaves.png", "40+255", str(PXSIZE), target_dir("/textures")+"/default_acacia_leaves.png")
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/spruce_leaves.png", "226+230", str(PXSIZE), target_dir("/textures")+"/mcl_core_leaves_spruce.png")
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/birch_leaves.png", "141+186", str(PXSIZE), target_dir("/textures")+"/mcl_core_leaves_birch.png")
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/jungle_leaves.png", "16+39", str(PXSIZE), target_dir("/textures")+"/default_jungleleaves.png")
|
|
||||||
|
|
||||||
# Waterlily
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/lily_pad.png", "16+39", str(PXSIZE), target_dir("/textures")+"/flowers_waterlily.png")
|
|
||||||
|
|
||||||
# Vines
|
|
||||||
colorize_alpha(FOLIAG, tex_dir+"/block/vine.png", "16+39", str(PXSIZE), target_dir("/textures")+"/mcl_core_vine.png")
|
|
||||||
|
|
||||||
# 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")+"/mcl_flowers_tallgrass_inv.png")
|
|
||||||
colorize_alpha(GRASS, tex_dir+"/block/fern.png", pcol, str(PXSIZE), target_dir("/textures")+"/mcl_flowers_fern_inv.png")
|
|
||||||
colorize_alpha(GRASS, tex_dir+"/block/large_fern_top.png", pcol, str(PXSIZE), target_dir("/textures")+"/mcl_flowers_double_plant_fern_inv.png")
|
|
||||||
colorize_alpha(GRASS, tex_dir+"/block/tall_grass_top.png", pcol, str(PXSIZE), target_dir("/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("/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
|
# ENTRY POINT
|
||||||
if make_texture_pack and not os.path.isdir(output_dir+"/"+output_dir_name):
|
if make_texture_pack and not os.path.isdir(output_dir+"/"+output_dir_name):
|
||||||
os.mkdir(output_dir+"/"+output_dir_name)
|
os.mkdir(output_dir+"/"+output_dir_name)
|
||||||
|
@ -449,7 +79,7 @@ if make_texture_pack and not os.path.isdir(output_dir+"/"+output_dir_name):
|
||||||
tempfile1 = tempfile.NamedTemporaryFile()
|
tempfile1 = tempfile.NamedTemporaryFile()
|
||||||
tempfile2 = tempfile.NamedTemporaryFile()
|
tempfile2 = tempfile.NamedTemporaryFile()
|
||||||
|
|
||||||
convert_textures()
|
convert_textures(make_texture_pack, dry_run, verbose, base_dir, tex_dir, tempfile1, tempfile2, output_dir, output_dir_name, mineclone2_path, PXSIZE)
|
||||||
|
|
||||||
tempfile1.close()
|
tempfile1.close()
|
||||||
tempfile2.close()
|
tempfile2.close()
|
||||||
|
|
0
tools/libtextureconverter/__init__.py
Normal file
0
tools/libtextureconverter/__init__.py
Normal file
9
tools/libtextureconverter/config.py
Normal file
9
tools/libtextureconverter/config.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import os
|
||||||
|
# Constants
|
||||||
|
SUPPORTED_MINECRAFT_VERSION = "1.20"
|
||||||
|
|
||||||
|
# Helper vars
|
||||||
|
home = os.environ["HOME"]
|
||||||
|
mineclone2_path = home + "/.minetest/games/mineclone2"
|
||||||
|
working_dir = os.getcwd()
|
||||||
|
appname = "Texture_Converter.py"
|
341
tools/libtextureconverter/convert.py
Normal file
341
tools/libtextureconverter/convert.py
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
from .utils import target_dir, colorize, colorize_alpha
|
||||||
|
import shutil, csv, os, tempfile, sys, argparse, 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):
|
||||||
|
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 = './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:
|
||||||
|
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", make_texture_pack, output_dir, output_dir_name, mineclone2_path) + "/mcl_maps_map_background.png")
|
||||||
|
|
||||||
|
# Convert armor textures (requires ImageMagick)
|
||||||
|
armor_files = [
|
||||||
|
[ tex_dir + "/models/armor/leather_layer_1.png", tex_dir + "/models/armor/leather_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_leather.png", "mcl_armor_chestplate_leather.png", "mcl_armor_leggings_leather.png", "mcl_armor_boots_leather.png" ],
|
||||||
|
[ tex_dir + "/models/armor/chainmail_layer_1.png", tex_dir + "/models/armor/chainmail_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_chain.png", "mcl_armor_chestplate_chain.png", "mcl_armor_leggings_chain.png", "mcl_armor_boots_chain.png" ],
|
||||||
|
[ tex_dir + "/models/armor/gold_layer_1.png", tex_dir + "/models/armor/gold_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_gold.png", "mcl_armor_chestplate_gold.png", "mcl_armor_leggings_gold.png", "mcl_armor_boots_gold.png" ],
|
||||||
|
[ tex_dir + "/models/armor/iron_layer_1.png", tex_dir + "/models/armor/iron_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_iron.png", "mcl_armor_chestplate_iron.png", "mcl_armor_leggings_iron.png", "mcl_armor_boots_iron.png" ],
|
||||||
|
[ tex_dir + "/models/armor/diamond_layer_1.png", tex_dir + "/models/armor/diamond_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_diamond.png", "mcl_armor_chestplate_diamond.png", "mcl_armor_leggings_diamond.png", "mcl_armor_boots_diamond.png" ],
|
||||||
|
[ tex_dir + "/models/armor/netherite_layer_1.png", tex_dir + "/models/armor/netherite_layer_2.png", target_dir("/mods/ITEMS/mcl_armor/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_armor_helmet_netherite.png", "mcl_armor_chestplate_netherite.png", "mcl_armor_leggings_netherite.png", "mcl_armor_boots_netherite.png" ]
|
||||||
|
]
|
||||||
|
for a in armor_files:
|
||||||
|
APXSIZE = 16 # for some reason MineClone2 requires this
|
||||||
|
layer_1 = a[0]
|
||||||
|
layer_2 = a[1]
|
||||||
|
adir = a[2]
|
||||||
|
if os.path.isfile(layer_1):
|
||||||
|
helmet = adir + "/" + a[3]
|
||||||
|
chestplate = adir + "/" + a[4]
|
||||||
|
boots = adir + "/" + a[6]
|
||||||
|
os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE * 2)+"+0 -crop "+str(APXSIZE * 2)+"x"+str(APXSIZE)+"+0+0 \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+helmet)
|
||||||
|
os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +"+str(APXSIZE)+"+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+"+str(APXSIZE)+"+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+chestplate)
|
||||||
|
os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_1+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+boots)
|
||||||
|
if os.path.isfile(layer_2):
|
||||||
|
leggings = adir + "/" + a[5]
|
||||||
|
os.system("convert -size "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" xc:none \\( "+layer_2+" -scale "+str(APXSIZE * 4)+"x"+str(APXSIZE * 2)+" -geometry +0+"+str(APXSIZE)+" -crop "+str(APXSIZE * 2.5)+"x"+str(APXSIZE)+"+0+"+str(APXSIZE)+" \) -composite -channel A -fx \"(a > 0.0) ? 1.0 : 0.0\" "+leggings)
|
||||||
|
|
||||||
|
# Convert chest textures (requires ImageMagick)
|
||||||
|
chest_files = [
|
||||||
|
[ tex_dir + "/entity/chest/normal.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "default_chest_top.png", "mcl_chests_chest_bottom.png", "default_chest_front.png", "mcl_chests_chest_left.png", "mcl_chests_chest_right.png", "mcl_chests_chest_back.png" ],
|
||||||
|
[ tex_dir + "/entity/chest/trapped.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png", "mcl_chests_chest_trapped_front.png", "mcl_chests_chest_trapped_left.png", "mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_back.png" ],
|
||||||
|
[ tex_dir + "/entity/chest/ender.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png", "mcl_chests_ender_chest_front.png", "mcl_chests_ender_chest_left.png", "mcl_chests_ender_chest_right.png", "mcl_chests_ender_chest_back.png" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
for c in chest_files:
|
||||||
|
chest_file = c[0]
|
||||||
|
if os.path.isfile(chest_file):
|
||||||
|
PPX = (PXSIZE/16)
|
||||||
|
CHPX = (PPX * 14) # Chest width
|
||||||
|
LIDPX = (PPX * 5) # Lid height
|
||||||
|
LIDLOW = (PPX * 10) # Lower lid section height
|
||||||
|
LOCKW = (PPX * 6) # Lock width
|
||||||
|
LOCKH = (PPX * 5) # Lock height
|
||||||
|
|
||||||
|
cdir = c[1]
|
||||||
|
top = cdir + "/" + c[2]
|
||||||
|
bottom = cdir + "/" + c[3]
|
||||||
|
front = cdir + "/" + c[4]
|
||||||
|
left = cdir + "/" + c[5]
|
||||||
|
right = cdir + "/" + c[6]
|
||||||
|
back = cdir + "/" + c[7]
|
||||||
|
# Top
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX)+"x"+str(CHPX)+" "+top)
|
||||||
|
# Bottom
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(CHPX)+"+"+str(CHPX*2)+"+"+str(CHPX+LIDPX)+" \) -geometry +0+0 -composite -extent "+str(CHPX)+"x"+str(CHPX)+" "+bottom)
|
||||||
|
# Front
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
|
||||||
|
-extent "+str(CHPX)+"x"+str(CHPX)+" "+front)
|
||||||
|
# TODO: Add lock
|
||||||
|
|
||||||
|
# Left, right back (use same texture, we're lazy
|
||||||
|
files = [ left, right, back ]
|
||||||
|
for f in files:
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
|
||||||
|
-extent "+str(CHPX)+"x"+str(CHPX)+" "+f)
|
||||||
|
|
||||||
|
# Double chests
|
||||||
|
|
||||||
|
chest_files = [
|
||||||
|
[ tex_dir + "/entity/chest/normal_double.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "default_chest_front_big.png", "default_chest_top_big.png", "default_chest_side_big.png" ],
|
||||||
|
[ tex_dir + "/entity/chest/trapped_double.png", target_dir("/mods/ITEMS/mcl_chests/textures", make_texture_pack, output_dir, output_dir_name, mineclone2_path), "mcl_chests_chest_trapped_front_big.png", "mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_side_big.png" ]
|
||||||
|
]
|
||||||
|
for c in chest_files:
|
||||||
|
chest_file = c[0]
|
||||||
|
if os.path.isfile(chest_file):
|
||||||
|
PPX = (PXSIZE/16)
|
||||||
|
CHPX = (PPX * 14) # Chest width (short side)
|
||||||
|
CHPX2 = (PPX * 15) # Chest width (long side)
|
||||||
|
LIDPX = (PPX * 5) # Lid height
|
||||||
|
LIDLOW = (PPX * 10) # Lower lid section height
|
||||||
|
LOCKW = (PPX * 6) # Lock width
|
||||||
|
LOCKH = (PPX * 5) # Lock height
|
||||||
|
|
||||||
|
cdir = c[1]
|
||||||
|
front = cdir + "/" + c[2]
|
||||||
|
top = cdir + "/" + c[3]
|
||||||
|
side = cdir + "/" + c[4]
|
||||||
|
# Top
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX2)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX2)+"x"+str(CHPX)+" "+top)
|
||||||
|
# Front
|
||||||
|
# TODO: Add lock
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
|
||||||
|
\( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
|
||||||
|
-extent "+str(CHPX2)+"x"+str(CHPX)+" "+front)
|
||||||
|
# Side
|
||||||
|
os.system("convert " + chest_file + " \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
|
||||||
|
\( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
|
||||||
|
-extent "+str(CHPX)+"x"+str(CHPX)+" "+side)
|
||||||
|
|
||||||
|
|
||||||
|
# Generate railway crossings and t-junctions. Note: They may look strange.
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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+"/")
|
28
tools/libtextureconverter/utils.py
Normal file
28
tools/libtextureconverter/utils.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import shutil, csv, os, tempfile, sys, argparse, glob
|
||||||
|
from PIL import Image
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
def detect_pixel_size(directory):
|
||||||
|
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):
|
||||||
|
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)
|
Loading…
Reference in a new issue