diff --git a/overviewer_core/aux_files/genPOI.py b/overviewer_core/aux_files/genPOI.py index 0e88ea839..eb271e604 100644 --- a/overviewer_core/aux_files/genPOI.py +++ b/overviewer_core/aux_files/genPOI.py @@ -31,7 +31,7 @@ from multiprocessing import Pool from argparse import ArgumentParser -from overviewer_core import config_parser, logger, nbt, world +from overviewer_core import config_parser, logger, nbt, world, textures from overviewer_core.files import FileReplacer, get_fs_caps UUID_LOOKUP_URL = 'https://sessionserver.mojang.com/session/minecraft/profile/' @@ -157,7 +157,7 @@ def signWrangler(poi): return poi -def handleEntities(rset, config, config_path, filters, markers): +def handleEntities(rset, config, config_path, filters, markers, tex): """ Add markers for Entities or TileEntities. @@ -216,6 +216,14 @@ def handleEntities(rset, config, config_path, filters, markers): for name, marker_list in marker_dict.items(): markers[name]['raw'].extend(marker_list) + # Banner Rendering requires a Textures object. Because I don't know if that object is completely thread save + # I've placed the code here. This means rendering of banners only happens on a single thread. + for name, value in markers.items(): + for m in value['raw']: + print(name, m) + #if poi['id'] == "Banner" or poi['id'] == "minecraft:banner": + # # For Banners: render the banner and set as default for this poi + # poi['icon'] = render_and_store_dynamic_icon(render_banner, 0, poi) logging.info("Done.") @@ -555,6 +563,14 @@ def main(): checked=f.get('checked', False)) marker_groups[rname].append(group) + # Initialize a Textures object. This is required for the rendering of banners + # TODO: Is this the correct way to instantiate Textures? + tex = textures.Textures(config['texturepath']) + f = tex.find_file("assets/minecraft/textures/block/sandstone_top.png", verbose=True) + f = tex.find_file("assets/minecraft/textures/block/grass_block_top.png", verbose=True) + f = tex.find_file("assets/minecraft/textures/block/diamond_ore.png", verbose=True) + f = tex.find_file("assets/minecraft/textures/block/oak_planks.png", verbose=True) + # initialize the structure for the markers markers = dict((name, dict(created=False, raw=[], name=filter_name)) for name, filter_name, __, __, __, __ in filters) @@ -566,7 +582,7 @@ def keyfunc(x): return x[3] sfilters = sorted(filters, key=keyfunc) for rset, rset_filters in itertools.groupby(sfilters, keyfunc): - handleEntities(rset, config, args.config, list(rset_filters), markers) + handleEntities(rset, config, args.config, list(rset_filters), markers, tex) # apply filters to players if not args.skipplayers: diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 8c701dab1..fc309277d 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -804,6 +804,107 @@ def generate_texture_tuple(self, img): return None return (img, self.generate_opaque_mask(img)) + +## +## Banner Rendering +## +# Source: https://minecraft.gamepedia.com/Formatting_codes +banner_color_palette = [ + (0xd0, 0xd6, 0xd7), + (0xe1, 0x61, 0x00), + (0xaa, 0x2d, 0xa0), + (0x1f, 0x8a, 0xc8), + (0xf2, 0xb1, 0x0e), + (0x5e, 0xaa, 0x11), + (0xd7, 0x65, 0x90), + (0x34, 0x38, 0x3c), + (0x7e, 0x7e, 0x74), + (0x0e, 0x78, 0x89), + (0x64, 0x1a, 0x9d), + (0x29, 0x2b, 0x90), + (0x60, 0x3a, 0x1a), + (0x48, 0x5b, 0x20), + (0x8f, 0x1b, 0x1b), + (0x02, 0x03, 0x06), +] +banner_patterns = { + # Important: The left half is the front of the banner + "bs": "stripe_bottom", + "ts": "stripe_top", + "ls": "stripe_left", + "rs": "stripe_right", + "cs": "stripe_center", + "ms": "stripe_middle", + "drs": "stripe_downright", + "dls": "stripe_downleft", + "ss": "small_stripes", + "cr": "cross", + "sc": "straight_cross", + "ld": "diagonal_left", + "rud": "diagonal_right", + "lud": "diagonal_up_left", + "rd": "diagonal_up_right", + "vh": "half_vertical", + "vhr": "half_vertical_right", + "hh": "half_horizontal", + "hhb": "half_horizontal_bottom", + "bl": "square_bottom_left", + "br": "square_bottom_right", + "tl": "square_top_left", + "tr": "square_top_right", + "bt": "triangle_bottom", + "tt": "triangle_top", + "bts": "triangles_bottom", + "tts": "triangles_top", + "mc": "circle", + "mr": "rhombus", + "bo": "border", + "cbo": "curly_border", + "bri": "bricks", + "gra": "gradient", + "gru": "gradient_up", + "cre": "creeper", + "sku": "skull", + "flo": "flower", + "moj": "mojang", + "glb": "globe", +} + + +def apply_color(img: Image, color: tuple) -> Image: + rf, gf, bf = (c / 256 for c in color) + r, g, b, alpha = img.split() + r = r.point(lambda i: i * rf) + g = g.point(lambda i: i * gf) + b = b.point(lambda i: i * bf) + return Image.merge("RGBA", (r, g, b, alpha)) + + +def render_banner(self: Textures, base_color_id: int, entity_nbt, backside=False): + """ + 'keepPacked': 0, + 'x': 186, + 'y': 68, + 'z': -250, + 'id': 'minecraft:banner', + 'Patterns': [ + {'Pattern': 'cre', 'Color': 14}, + {'Pattern': 'gra', 'Color': 0} + ] + """ + texture_crop = (21, 1, 41, 41) if backside else (1, 1, 21, 41) + + img = Image.new("RGB", (20, 40)) + # Apply base pattern and color + base_pattern = self.load_image('assets/minecraft/textures/entity/banner/base.png') + img.paste(apply_color(base_pattern.crop(texture_crop), banner_color_palette[base_color_id])) + for p in entity_nbt["Patterns"]: + pattern = self.load_image('assets/minecraft/textures/entity/banner/%s.png' % banner_patterns[p["Pattern"]]) + colored_pattern = apply_color(pattern.crop(texture_crop), banner_color_palette[p["Color"]]) + img.paste(colored_pattern, (0, 0), colored_pattern) + + return img + ## ## The other big one: @material and associated framework ##