Update doc_items (fake items)
[MineClone/MineClone2.git] / tools / Texture_Converter.py
blob6cb6cdb4f01396e70a417b20efbea51c900a7953
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Texture Converter.
4 # Converts Minecraft resource packs to Minetest texture packs.
5 # See README.md.
7 __author__ = "Wuzzy"
8 __license__ = "MIT License"
9 __status__ = "Development"
11 import shutil, csv, os, tempfile, sys, getopt
13 # Helper vars
14 home = os.environ["HOME"]
15 mineclone2_path = home + "/.minetest/games/mineclone2"
16 working_dir = os.getcwd()
17 output_dir_name = "New_MineClone_2_Texture_Pack"
18 appname = "Texture_Converter.py"
20 ### SETTINGS ###
21 output_dir = working_dir
23 base_dir = None
25 # If True, will only make console output but not convert anything.
26 dry_run = False
28 # If True, textures will be put into a texture pack directory structure.
29 # If False, textures will be put into MineClone 2 directories.
30 make_texture_pack = True
32 # If True, prints all copying actions
33 verbose = False
35 PXSIZE = 16
37 syntax_help = appname+""" -i <input dir> [-o <output dir>] [-d] [-v|-q] [-h]
38 Mandatory argument:
39 -i <input directory>
40 Directory of Minecraft resource pack to convert
42 Optional arguments:
43 -p <texture size>
44 Specify the size (in pixels) of the original textures (default: 16)
45 -o <output directory>
46 Directory in which to put the resulting Minetest texture pack
47 (default: working directory)
49 Just pretend to convert textures and just print output, but do not actually
50 change any files.
52 Print out all copying actions
54 Show this help and exit"""
55 try:
56 opts, args = getopt.getopt(sys.argv[1:],"hi:o:p:dv")
57 except getopt.GetoptError:
58 print(
59 """ERROR! The options you gave me make no sense!
61 Here's the syntax reference:""")
62 print(syntax_help)
63 sys.exit(2)
64 for opt, arg in opts:
65 if opt == "-h":
66 print(
67 """This is the official MineClone 2 Texture Converter.
68 This will convert textures from Minecraft resource packs to
69 a Minetest texture pack.
71 Supported Minecraft version: 1.12 (Java Edition)
73 Syntax:""")
74 print(syntax_help)
75 sys.exit()
76 elif opt == "-d":
77 dry_run = True
78 elif opt == "-v":
79 verbose = True
80 elif opt == "-i":
81 base_dir = arg
82 elif opt == "-o":
83 output_dir = arg
84 elif opt == "-p":
85 PXSIZE = int(arg)
87 if base_dir == None:
88 print(
89 """ERROR: You didn't tell me the path to the Minecraft resource pack.
90 Mind-reading has not been implemented yet.
92 Try this:
93 """+appname+""" -i <path to resource pack> -p <texture size>
95 For the full help, use:
96 """+appname+""" -h""")
97 sys.exit(2);
99 ### END OF SETTINGS ###
101 tex_dir = base_dir + "/assets/minecraft/textures"
103 # FUNCTION DEFINITIONS
104 def colorize(colormap, source, colormap_pixel, texture_size, destination):
105 os.system("convert "+colormap+" -crop 1x1+"+colormap_pixel+" -depth 8 -resize "+texture_size+"x"+texture_size+" "+tempfile1.name)
106 os.system("composite -compose Multiply "+tempfile1.name+" "+source+" "+destination)
108 def colorize_alpha(colormap, source, colormap_pixel, texture_size, destination):
109 colorize(colormap, source, colormap_pixel, texture_size, tempfile2.name)
110 os.system("composite -compose Dst_In "+source+" "+tempfile2.name+" -alpha Set "+destination)
112 # This function is unused atm.
113 # TODO: Implemnt colormap extraction
114 def extract_colormap(colormap, colormap_pixel, positions):
115 os.system("convert -size 16x16 canvas:black "+tempfile1.name)
118 for p in positions:
119 os.system("convert "+colormap+" -crop 1x1+"+colormap_pixel+" -depth 8 "+tempfile2.name)
120 os.system("composite -geometry 16x16+"+x+"+"+y+" "+tempfile2.name)
121 x = x+1
123 def target_dir(directory):
124 if make_texture_pack:
125 return output_dir + "/" + output_dir_name
126 else:
127 return mineclone2_path + directory
129 # Copy texture files
130 def convert_textures():
131 failed_conversions = 0
132 print("Texture conversion BEGINS NOW!")
133 with open("Conversion_Table.csv", newline="") as csvfile:
134 reader = csv.reader(csvfile, delimiter=",", quotechar='"')
135 first_row = True
136 for row in reader:
137 # Skip first row
138 if first_row:
139 first_row = False
140 continue
142 src_dir = row[0]
143 src_filename = row[1]
144 dst_dir = row[2]
145 dst_filename = row[3]
146 if row[4] != "":
147 xs = int(row[4])
148 ys = int(row[5])
149 xl = int(row[6])
150 yl = int(row[7])
151 xt = int(row[8])
152 yt = int(row[9])
153 else:
154 xs = None
155 blacklisted = row[10]
157 if blacklisted == "y":
158 # Skip blacklisted files
159 continue
161 if make_texture_pack == False and dst_dir == "":
162 # If destination dir is empty, this texture is not supposed to be used in MCL2
163 # (but maybe an external mod). It should only be used in texture packs.
164 # Otherwise, it must be ignored.
165 # Example: textures for mcl_supplemental
166 continue
168 src_file = base_dir + src_dir + "/" + src_filename # source file
169 src_file_exists = os.path.isfile(src_file)
170 dst_file = target_dir(dst_dir) + "/" + dst_filename # destination file
172 if src_file_exists == False:
173 print("WARNING: Source file does not exist: "+src_file)
174 failed_conversions = failed_conversions + 1
175 continue
177 if xs != None:
178 # Crop and copy images
179 if not dry_run:
180 os.system("convert "+src_file+" -crop "+xl+"x"+yl+"+"+xs+"+"+ys+" "+dst_file)
181 if verbose:
182 print(src_file + " → " + dst_file)
183 else:
184 # Copy image verbatim
185 if not dry_run:
186 shutil.copy2(src_file, dst_file)
187 if verbose:
188 print(src_file + " → " + dst_file)
190 # Convert chest textures (requires ImageMagick)
191 chest_files = [
192 [ 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" ],
193 [ 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" ],
194 [ 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" ]
197 for c in chest_files:
198 chest_file = c[0]
199 if os.path.isfile(chest_file):
200 PPX = (PXSIZE/16)
201 CHPX = (PPX * 14) # Chest width
202 LIDPX = (PPX * 5) # Lid height
203 LIDLOW = (PPX * 10) # Lower lid section height
204 LOCKW = (PPX * 6) # Lock width
205 LOCKH = (PPX * 5) # Lock height
207 cdir = c[1]
208 top = cdir + "/" + c[2]
209 bottom = cdir + "/" + c[3]
210 front = cdir + "/" + c[4]
211 left = cdir + "/" + c[5]
212 right = cdir + "/" + c[6]
213 back = cdir + "/" + c[7]
214 # Top
215 os.system("convert " + chest_file + " \
216 \( -clone 0 -crop "+str(CHPX)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX)+"x"+str(CHPX)+" "+top)
217 # Bottom
218 os.system("convert " + chest_file + " \
219 \( -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)
220 # Front
221 os.system("convert " + chest_file + " \
222 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
223 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
224 -extent "+str(CHPX)+"x"+str(CHPX)+" "+front)
225 # TODO: Add lock
227 # Left, right back (use same texture, we're lazy
228 files = [ left, right, back ]
229 for f in files:
230 os.system("convert " + chest_file + " \
231 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
232 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
233 -extent "+str(CHPX)+"x"+str(CHPX)+" "+f)
235 # Double chests
237 chest_files = [
238 [ 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" ],
239 [ 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" ]
241 for c in chest_files:
242 chest_file = c[0]
243 if os.path.isfile(chest_file):
244 PPX = (PXSIZE/16)
245 CHPX = (PPX * 14) # Chest width (short side)
246 CHPX2 = (PPX * 15) # Chest width (long side)
247 LIDPX = (PPX * 5) # Lid height
248 LIDLOW = (PPX * 10) # Lower lid section height
249 LOCKW = (PPX * 6) # Lock width
250 LOCKH = (PPX * 5) # Lock height
252 cdir = c[1]
253 front = cdir + "/" + c[2]
254 top = cdir + "/" + c[3]
255 side = cdir + "/" + c[4]
256 # Top
257 os.system("convert " + chest_file + " \
258 \( -clone 0 -crop "+str(CHPX2)+"x"+str(CHPX)+"+"+str(CHPX)+"+0 \) -geometry +0+0 -composite -extent "+str(CHPX2)+"x"+str(CHPX)+" "+top)
259 # Front
260 # TODO: Add lock
261 os.system("convert " + chest_file + " \
262 \( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDPX)+"+"+str(CHPX)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
263 \( -clone 0 -crop "+str(CHPX2)+"x"+str(LIDLOW)+"+"+str(CHPX)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
264 -extent "+str(CHPX2)+"x"+str(CHPX)+" "+front)
265 # Side
266 os.system("convert " + chest_file + " \
267 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDPX)+"+"+str(0)+"+"+str(CHPX)+" \) -geometry +0+0 -composite \
268 \( -clone 0 -crop "+str(CHPX)+"x"+str(LIDLOW)+"+"+str(0)+"+"+str(CHPX*2+LIDPX)+" \) -geometry +0+"+str(LIDPX-PPX)+" -composite \
269 -extent "+str(CHPX)+"x"+str(CHPX)+" "+side)
272 # Generate railway crossings and t-junctions. Note: They may look strange.
273 # Note: these may be only a temporary solution, as crossings and t-junctions do not occour in MC.
274 # TODO: Curves
275 rails = [
276 # (Straigt src, curved src, t-junction dest, crossing dest)
277 ("rail_normal.png", "rail_normal_turned.png", "default_rail_t_junction.png", "default_rail_crossing.png"),
278 ("rail_golden.png", "rail_normal_turned.png", "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"),
279 ("rail_golden_powered.png", "rail_normal_turned.png", "mcl_minecarts_rail_golden_t_junction_powered.png", "mcl_minecarts_rail_golden_crossing_powered.png"),
280 ("rail_detector.png", "rail_normal_turned.png", "mcl_minecarts_rail_detector_t_junction.png", "mcl_minecarts_rail_detector_crossing.png"),
281 ("rail_detector_powered.png", "rail_normal_turned.png", "mcl_minecarts_rail_detector_t_junction_powered.png", "mcl_minecarts_rail_detector_crossing_powered.png"),
282 ("rail_activator.png", "rail_normal_turned.png", "mcl_minecarts_rail_activator_t_junction.png", "mcl_minecarts_rail_activator_crossing.png"),
283 ("rail_activator_powered.png", "rail_normal_turned.png", "mcl_minecarts_rail_activator_d_t_junction.png", "mcl_minecarts_rail_activator_powered_crossing.png"),
285 for r in rails:
286 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])
287 os.system("convert "+tex_dir+"/blocks/"+r[0]+" -rotate 90 "+tempfile1.name)
288 os.system("composite -compose Dst_Over "+tempfile1.name+" "+tex_dir+"/blocks/"+r[0]+" "+target_dir("/mods/ENTITIES/mcl_minecarts/textures")+"/"+r[3])
290 # Convert banner overlays
291 overlays = [
292 "base",
293 "border",
294 "bricks",
295 "circle",
296 "creeper",
297 "cross",
298 "curly_border",
299 "diagonal_left",
300 "diagonal_right",
301 "diagonal_up_left",
302 "diagonal_up_right",
303 "flower",
304 "gradient",
305 "gradient_up",
306 "half_horizontal_bottom",
307 "half_horizontal",
308 "half_vertical",
309 "half_vertical_right",
310 "rhombus",
311 "mojang",
312 "skull",
313 "small_stripes",
314 "straight_cross",
315 "stripe_bottom",
316 "stripe_center",
317 "stripe_downleft",
318 "stripe_downright",
319 "stripe_left",
320 "stripe_middle",
321 "stripe_right",
322 "stripe_top",
323 "square_bottom_left",
324 "square_bottom_right",
325 "square_top_left",
326 "square_top_right",
327 "triangle_bottom",
328 "triangles_bottom",
329 "triangle_top",
330 "triangles_top",
332 for o in overlays:
333 orig = tex_dir + "/entity/banner/" + o + ".png"
334 if os.path.isfile(orig):
335 if o == "mojang":
336 o = "thing"
337 dest = target_dir("/mods/ITEMS/mcl_banners/textures")+"/"+"mcl_banners_"+o+".png"
338 os.system("convert "+orig+" -transparent-color white -background black -alpha remove -alpha copy -channel RGB -white-threshold 0 "+dest)
340 # Convert grass
341 grass_file = tex_dir + "/blocks/grass_top.png"
342 if os.path.isfile(grass_file):
343 FOLIAG = tex_dir+"/colormap/foliage.png"
344 GRASS = tex_dir+"/colormap/grass.png"
347 # Leaves
348 colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_oak.png", "116+143", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_leaves.png")
349 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")
350 colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_acacia.png", "40+255", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_acacia_leaves.png")
351 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")
352 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")
353 colorize_alpha(FOLIAG, tex_dir+"/blocks/leaves_jungle.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_jungleleaves.png")
355 # Waterlily
356 colorize_alpha(FOLIAG, tex_dir+"/blocks/waterlily.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/flowers_waterlily.png")
358 # Vines
359 colorize_alpha(FOLIAG, tex_dir+"/blocks/vine.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_vine.png")
361 # Tall grass, fern (inventory images)
362 pcol = "49+172" # Plains grass color
363 colorize_alpha(GRASS, tex_dir+"/blocks/tallgrass.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_tallgrass_inv.png")
364 colorize_alpha(GRASS, tex_dir+"/blocks/fern.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_fern_inv.png")
365 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")
366 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")
368 # TODO: Convert grass palette
370 offset = [
371 [ pcol, "", "grass" ], # Default grass: Plains
372 [ "40+255", "_dry", "dry_grass" ], # Dry grass: Savanna, Mesa Plateau F, Nether, …
374 for o in offset:
375 colorize(GRASS, tex_dir+"/blocks/grass_top.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+".png")
376 colorize_alpha(GRASS, tex_dir+"/blocks/grass_side_overlay.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+"_side.png")
379 print("Textures conversion COMPLETE!")
380 if failed_conversions > 0:
381 print("WARNING: Number of missing files in original resource pack: "+str(failed_conversions))
382 print("NOTE: Please keep in mind this script does not reliably convert all the textures yet.")
383 if make_texture_pack:
384 print("You can now retrieve the texture pack in "+output_dir+"/"+output_dir_name+"/")
386 # ENTRY POINT
387 if make_texture_pack and not os.path.isdir(output_dir+"/"+output_dir_name):
388 os.mkdir(output_dir+"/"+output_dir_name)
390 tempfile1 = tempfile.NamedTemporaryFile()
391 tempfile2 = tempfile.NamedTemporaryFile()
393 convert_textures()
395 tempfile1.close()
396 tempfile2.close()