From 3a6208ead09fd095d6a9924bf32282a6f3e4b249 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 22 Sep 2011 20:44:56 -0400 Subject: [PATCH 01/47] Added support for Nether Brick Also reformatted the texture list (sorry for the noisy diff) --- overviewer_core/textures.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 7c8b708..f055649 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -437,9 +437,11 @@ def _build_blockimages(): # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102, -1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 137, + 66, 69, 72, 73, 75, -1, 102,103,104,105,-1, 102, -1, -1, -1, -1, + # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 + -1, -1, -1, -1, -1, -1, -1, 137,-1, -1, -1, -1, -1, -1, -1, -1, + # 112 + 224 ] # NOTE: For non-block textures, the sideid is ignored, but can't be -1 @@ -456,9 +458,11 @@ def _build_blockimages(): # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 136, + 66, 70, 72, 73, 74, -1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, + # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 + -1, -1, -1, -1, -1 ,-1 ,-1, 136,-1, -1, -1, -1, -1, -1, -1, -1, + # 112 + 224 ] # This maps block id to the texture that goes on the side of the block From 415578e1b67fe68cebc1a36dd094d042691e3f94 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 10:50:53 +0200 Subject: [PATCH 02/47] Added nether fences. --- overviewer_core/chunk.py | 2 +- overviewer_core/src/iterate.c | 5 +++-- overviewer_core/textures.py | 27 +++++++++++++++------------ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 3fdaf4a..943cd90 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -130,7 +130,7 @@ transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109]) + 106, 107, 108, 109, 113]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 74c7904..450815b 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -291,7 +291,7 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { * This is a TODO! */ } else if ((state->block == 90) || (state->block == 101) || - (state->block == 102)) { + (state->block == 102) || (state->block == 113)) { return check_adjacent_blocks(state, x, y, z, state->block); } @@ -426,7 +426,8 @@ chunk_render(PyObject *self, PyObject *args) { (state.block == 20) || (state.block == 54) || (state.block == 55) || (state.block == 79) || (state.block == 85) || (state.block == 90) || - (state.block == 101) || (state.block == 102)) { + (state.block == 101) || (state.block == 102) || + (state.block == 113)) { ancilData = generate_pseudo_data(&state, ancilData); state.block_pdata = ancilData; } else { diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index f055649..7b438d0 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1434,12 +1434,17 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) - if blockID == 85: # fences + if blockID == 85 or blockID == 113: # normal fences and nether fences # create needed images for Big stick fence + if blockID == 85: # normal fence + fence_top = terrain_images[4].copy() + fence_side = terrain_images[4].copy() + fence_small_side = terrain_images[4].copy() + else: # netherbrick fence + fence_top = terrain_images[224].copy() + fence_side = terrain_images[224].copy() + fence_small_side = terrain_images[224].copy() - fence_top = terrain_images[4].copy() - fence_side = terrain_images[4].copy() - # generate the textures of the fence ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -1472,9 +1477,6 @@ def generate_special_texture(blockID, data): # Now render the small sticks. # Create needed images - fence_small_side = terrain_images[4].copy() - - # Generate mask ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -2336,7 +2338,7 @@ special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109]) + 104, 105, 106, 107, 108, 109, 113]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2396,14 +2398,15 @@ special_map[96] = range(8) # trapdoor, open, closed, orientation special_map[98] = range(3) # stone brick, normal, mossy and cracked special_map[99] = range(11) # huge brown mushroom, side, corner, etc, piece special_map[100] = range(11) # huge red mushroom, side, corner, etc, piece -special_map[101]= range(16) # iron bars, all the possible combination, uses pseudo data -special_map[102]= range(16) # glass panes, all the possible combination, uses pseudo data +special_map[101] = range(16) # iron bars, all the possible combination, uses pseudo data +special_map[102] = range(16) # glass panes, all the possible combination, uses pseudo data special_map[104] = range(8) # pumpkin stem, size of the stem special_map[105] = range(8) # melon stem, size of the stem special_map[106] = (1,2,4,8) # vine, orientation special_map[107] = range(8) # fence gates, orientation + open bit -special_map[108]= range(4) # red stairs, orientation -special_map[109]= range(4) # stonebrick stairs, orientation +special_map[108] = range(4) # red stairs, orientation +special_map[109] = range(4) # stonebrick stairs, orientation +special_map[113] = range(16) # netherbrick fence, uses pseudo data # placeholders that are generated in generate() bgcolor = None From 9aa2c2252ad6d801e2401d2e7b2a7a9ab0a2a6a1 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 11:08:46 +0200 Subject: [PATCH 03/47] Added netherbrick stairs. --- overviewer_core/chunk.py | 2 +- overviewer_core/textures.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 943cd90..6cf552c 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -130,7 +130,7 @@ transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109, 113]) + 106, 107, 108, 109, 113, 114]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 7b438d0..8e18cd4 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -940,7 +940,7 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) - if blockID in (53,67, 108, 109): # wooden, stone brick, and cobblestone stairs. + if blockID in (53,67, 108, 109, 114): # wooden, stone brick, cobblestone and netherbrick stairs. if blockID == 53: # wooden texture = terrain_images[4] @@ -950,6 +950,8 @@ def generate_special_texture(blockID, data): texture = terrain_images[7] elif blockID == 109: # stone brick stairs texture = terrain_images[54] + elif blockID == 114: # netherbrick stairs + texture = terrain_images[224] side = texture.copy() half_block_u = texture.copy() # up, down, left, right @@ -2338,7 +2340,7 @@ special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109, 113]) + 104, 105, 106, 107, 108, 109, 113, 114]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2407,6 +2409,7 @@ special_map[107] = range(8) # fence gates, orientation + open bit special_map[108] = range(4) # red stairs, orientation special_map[109] = range(4) # stonebrick stairs, orientation special_map[113] = range(16) # netherbrick fence, uses pseudo data +special_map[114] = range(4) # netherbrick stairs, orientation # placeholders that are generated in generate() bgcolor = None From 692afc35457f618b38946cdbbc8e75b9b3bb3c21 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 11:18:49 +0200 Subject: [PATCH 04/47] Add nether wart. --- overviewer_core/textures.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 8e18cd4..d331ada 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -272,8 +272,9 @@ def _build_block(top, side, blockID=None): otherside = ImageEnhance.Brightness(otherside).enhance(0.8) otherside.putalpha(othersidealpha) - ## special case for tall-grass, fern, dead shrub, and pumpkin/melon stem - if blockID in (31,32,104,105): + ## special case for tall-grass, fern, dead shrub, pumpkin/melon stem + ## and nether wart + if blockID in (31,32,104,105,115): front = original_texture.resize((14,11), Image.ANTIALIAS) composite.alpha_over(img, front, (5,9)) return img @@ -1926,6 +1927,20 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) + if blockID == 115: # nether wart + if data == 0: # just come up + t = terrain_images[226] + elif data in (1, 2): + t = terrain_images[227] + elif data >= 3: # fully grown + t = terrain_images[228] + + # use the same technic as tall grass + img = _build_block(t, t, blockID) + + return generate_texture_tuple(img, blockID) + + return None def convert_data(blockID, data): @@ -2340,7 +2355,7 @@ special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109, 113, 114]) + 104, 105, 106, 107, 108, 109, 113, 114, 115]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2410,6 +2425,7 @@ special_map[108] = range(4) # red stairs, orientation special_map[109] = range(4) # stonebrick stairs, orientation special_map[113] = range(16) # netherbrick fence, uses pseudo data special_map[114] = range(4) # netherbrick stairs, orientation +special_map[115] = range(4) # nether wart, size of the plant # placeholders that are generated in generate() bgcolor = None From add7ddc4c5fc214413271148d0e6027b78198019 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 11:23:29 +0200 Subject: [PATCH 05/47] Added mycelium, it looks it hasn't got any data nor tinting. --- overviewer_core/textures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index d331ada..273d27f 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -440,7 +440,7 @@ def _build_blockimages(): # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 66, 69, 72, 73, 75, -1, 102,103,104,105,-1, 102, -1, -1, -1, -1, # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 - -1, -1, -1, -1, -1, -1, -1, 137,-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 137,-1, -1, -1, -1, -1, -1, 78, -1, # 112 224 ] @@ -461,7 +461,7 @@ def _build_blockimages(): # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 66, 70, 72, 73, 74, -1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 - -1, -1, -1, -1, -1 ,-1 ,-1, 136,-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1 ,-1 ,-1, 136,-1, -1, -1, -1, -1, -1, 77, -1, # 112 224 ] From c99b54fb90512cfc7f416de4f8a72808f89ed77a Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 11:37:11 +0200 Subject: [PATCH 06/47] Rotate the ancildata of the netherbrick staris... --- overviewer_core/textures.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 273d27f..4d364a3 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2033,7 +2033,8 @@ def convert_data(blockID, data): elif data == 2: data = 3 elif data == 3: data = 1 elif data == 4: data = 2 - if blockID in (53,67,108,109): # wooden and cobblestone stairs. + if blockID in (53,67,108,109,114): # wooden, cobblestone, brick + # stonebrick and netherbrick stairs. if _north == 'upper-left': if data == 0: data = 2 elif data == 1: data = 3 From 41beb13c39f1587e8a6fcbef2ab99660c6244912 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Fri, 23 Sep 2011 14:10:47 +0200 Subject: [PATCH 07/47] Add nether wart to the list of transparent blocks and update the solid_blocks list --- overviewer_core/chunk.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 6cf552c..cf432fc 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -130,12 +130,13 @@ transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109, 113, 114]) + 106, 107, 108, 109, 111, 113, 114, 115]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, - 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) + 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91, 95, 97, 98, + 99, 100, 103, 108, 109, 110, 112, 114]) # This set holds block ids that are fluid blocks fluid_blocks = set([8,9,10,11]) From 9ee463e4b5aedc6dd3e2d1101c679be0a5d3eb92 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 26 Sep 2011 14:00:41 +0200 Subject: [PATCH 08/47] Add silverfish in block block. --- overviewer_core/textures.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 4d364a3..791a6b0 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1733,6 +1733,18 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) + if blockID == 97: # silverfish in stone block + if data == 0: # stone + t = terrain_images[1] + elif data == 1: # cobblestone + t = terrain_images[16] + elif data == 2: # stone brick + t = terrain_images[54] + + img = _build_block(t, t, blockID) + + return generate_texture_tuple(img, blockID) + if blockID == 98: # normal, mossy and cracked stone brick if data == 0: # normal t = terrain_images[54] @@ -2355,8 +2367,8 @@ def loadLightColor(): special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, - 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109, 113, 114, 115]) + 86, 90, 91, 92, 93, 94, 96, 97, 98, 99, 100, 101, + 102, 104, 105, 106, 107, 108, 109, 113, 114, 115]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2413,6 +2425,7 @@ special_map[92] = range(6) # cake, eaten amount, (not implemented) special_map[93] = range(16) # OFF redstone repeater, orientation and delay special_map[94] = range(16) # ON redstone repeater, orientation and delay special_map[96] = range(8) # trapdoor, open, closed, orientation +special_map[97] = range(3) # silverfish in stony block, type of block special_map[98] = range(3) # stone brick, normal, mossy and cracked special_map[99] = range(11) # huge brown mushroom, side, corner, etc, piece special_map[100] = range(11) # huge red mushroom, side, corner, etc, piece From 6fec99915535dff93ea4bcff521f1ca72ce346a6 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 27 Sep 2011 10:31:26 +0200 Subject: [PATCH 09/47] Add lilypads with biome tinting. NOTE: no info about the ancildata in minepedia, using the stairs ancildata at the moment! --- overviewer_core/src/rendermode-normal.c | 11 +++++++++-- overviewer_core/textures.py | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/overviewer_core/src/rendermode-normal.c b/overviewer_core/src/rendermode-normal.c index e37a47a..24bf665 100644 --- a/overviewer_core/src/rendermode-normal.c +++ b/overviewer_core/src/rendermode-normal.c @@ -173,7 +173,9 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * * get constant brown color (see textures.py) */ (((state->block == 104) || (state->block == 105)) && (state->block_data != 7)) || /* vines */ - state->block == 106) + state->block == 106 || + /* lily pads */ + state->block == 111) { /* do the biome stuff! */ PyObject *facemask = mask; @@ -240,6 +242,10 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * /* vines */ color = PySequence_GetItem(self->grasscolor, index); break; + case 111: + /* lily padas */ + color = PySequence_GetItem(self->grasscolor, index); + break; default: break; }; @@ -270,7 +276,8 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * facemask = NULL; } - if (state->block == 18 || state->block == 106) /* leaves and vines */ + if (state->block == 18 || state->block == 106 || state->block == 111) + /* leaves, vines and lyli pads */ { r = 37; g = 118; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 791a6b0..1e19e3e 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1939,6 +1939,20 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) + if blockID == 111: # lily pad + t = terrain_images[76] # NOTE: using same data as stairs, no + # info in minepedia at the moment. + if data == 0: # pointing south + img = _build_full_block(None, None, None, None, None, t) + elif data == 1: # pointing north + img = _build_full_block(None, None, None, None, None, t.rotate(180)) + elif data == 2: # pointing west + img = _build_full_block(None, None, None, None, None, t.rotate(270)) + elif data == 3: # pointing east + img = _build_full_block(None, None, None, None, None, t.rotate(90)) + + return generate_texture_tuple(img, blockID) + if blockID == 115: # nether wart if data == 0: # just come up t = terrain_images[226] @@ -2045,8 +2059,9 @@ def convert_data(blockID, data): elif data == 2: data = 3 elif data == 3: data = 1 elif data == 4: data = 2 - if blockID in (53,67,108,109,114): # wooden, cobblestone, brick + if blockID in (53,67,108,109,111,114): # wooden, cobblestone, brick # stonebrick and netherbrick stairs. + # AND lily pad. if _north == 'upper-left': if data == 0: data = 2 elif data == 1: data = 3 @@ -2368,7 +2383,8 @@ special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, 86, 90, 91, 92, 93, 94, 96, 97, 98, 99, 100, 101, - 102, 104, 105, 106, 107, 108, 109, 113, 114, 115]) + 102, 104, 105, 106, 107, 108, 109, 111, 113, 114, + 115]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2437,6 +2453,7 @@ special_map[106] = (1,2,4,8) # vine, orientation special_map[107] = range(8) # fence gates, orientation + open bit special_map[108] = range(4) # red stairs, orientation special_map[109] = range(4) # stonebrick stairs, orientation +special_map[111] = range(4) # lily pad, orientation special_map[113] = range(16) # netherbrick fence, uses pseudo data special_map[114] = range(4) # netherbrick stairs, orientation special_map[115] = range(4) # nether wart, size of the plant From 1c5b324767683fd73729d95eab8559ebafc2098f Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 27 Sep 2011 11:06:57 +0200 Subject: [PATCH 10/47] Make iron bars and glass panes stick to everything but air. --- overviewer_core/src/iterate.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 450815b..041f0b4 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -284,14 +284,15 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { return final_data; - /* portal, iron bars and glass panes - * Note: iron bars and glass panes "stick" to other blocks, but - * at the moment of writing this is not clear which ones stick and - * which others no, so for the moment stick only with himself. - * This is a TODO! - */ - } else if ((state->block == 90) || (state->block == 101) || - (state->block == 102) || (state->block == 113)) { + } else if ((state->block == 101) || (state->block == 102)) { + /* iron bars and glass panes: + * they seem to stick to almost everything but air, but + * not sure yet! Still a TODO! */ + /* return check adjacent blocks with air, bit inverted */ + return check_adjacent_blocks(state, x, y, z, 0) ^ 0x0f; + + } else if ((state->block == 90) || (state->block == 113)) { + /* portal and nether brick fences */ return check_adjacent_blocks(state, x, y, z, state->block); } From f758f876d6a8d140ecf78a66deee9fd47dc513f0 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Sat, 8 Oct 2011 17:38:40 +0200 Subject: [PATCH 11/47] Added all the new 1.9-pre3 blocks: - The enchantment table has not book yet! - The brewing stand is the 2d texture pasted. TODO. - The cauldron works perfect. - The air portal draw som random dots resembling stars, is not marevellous, but does the job. - Air portal frame is rendered without problem. --- overviewer_core/chunk.py | 3 +- overviewer_core/textures.py | 76 ++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index cf432fc..300cada 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -130,7 +130,8 @@ transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109, 111, 113, 114, 115]) + 106, 107, 108, 109, 111, 113, 114, 115, 116, 117, 118, + 119, 120]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 1e19e3e..a74859e 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -280,7 +280,7 @@ def _build_block(top, side, blockID=None): return img ## special case for non-block things - if blockID in (37,38,6,39,40,83,30): ## flowers, sapling, mushrooms, reeds, web + if blockID in (37,38,6,39,40,83,30,120): ## flowers, sapling, mushrooms, reeds, web, broken air portal frame(data = 1) # # instead of pasting these blocks at the cube edges, place them in the middle: # and omit the top @@ -1963,6 +1963,73 @@ def generate_special_texture(blockID, data): # use the same technic as tall grass img = _build_block(t, t, blockID) + return generate_texture_tuple(img, blockID) + + if blockID == 116: # enchantment table + # no book at the moment + top = terrain_images[166] + side = terrain_images[182] + img = _build_full_block((top, 4), None, None, side, side) + return generate_texture_tuple(img, blockID) + + if blockID == 117: # brewing stand + # place holder. just paste it in 2d! + t = terrain_images[157] + img = Image.new("RGBA", (24,24), bgcolor) + + composite.alpha_over(img, t, (4, 4), t) + return generate_texture_tuple(img, blockID) + + if blockID == 118: # cauldron + side = terrain_images[154] + top = terrain_images[138] + bottom = terrain_images[139] + water = transform_image(_load_image("water.png")) + if data == 0: # empty + img = _build_full_block(top, side, side, side, side) + + if data == 1: # 1/3 filled + img = _build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,8), water) + img2 = _build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + if data == 2: # 2/3 filled + img = _build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,4), water) + img2 = _build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + if data == 3: # 3/3 filled + img = _build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,0), water) + img2 = _build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + return generate_texture_tuple(img, blockID) + + if blockID == 119: # air portal + img = Image.new("RGBA", (24,24), bgcolor) + # generate a black texure with white, blue and grey dots resembling stars + t = Image.new("RGBA", (16,16), (0,0,0,255)) + for color in [(155,155,155,255), (100,255,100,255), (255,255,255,255)]: + for i in range(6): + x = randint(0,15) + y = randint(0,15) + t.putpixel((x,y),color) + + t = transform_image(t, blockID) + composite.alpha_over(img, t, (0,0), t) + + return generate_texture_tuple(img, blockID) + + if blockID == 120: # air portal frame + if data == 0: # fixed block + t = terrain_images[159] + img = _build_full_block(t, None, None, t, t) + if data == 1: # broken block + t = terrain_images[158] + img = _build_block(t, t, blockID) return generate_texture_tuple(img, blockID) @@ -2384,7 +2451,7 @@ special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, 86, 90, 91, 92, 93, 94, 96, 97, 98, 99, 100, 101, 102, 104, 105, 106, 107, 108, 109, 111, 113, 114, - 115]) + 115, 116, 117, 118, 119, 120]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -2457,6 +2524,11 @@ special_map[111] = range(4) # lily pad, orientation special_map[113] = range(16) # netherbrick fence, uses pseudo data special_map[114] = range(4) # netherbrick stairs, orientation special_map[115] = range(4) # nether wart, size of the plant +special_map[116] = (0,) # enchantment table, nothings special. book not implemented +special_map[117] = range(8) # brewing stand, number of bottles. (not implemented) +special_map[118] = range(4) # cauldron, amount of water in it. +special_map[119] = (0,) # air portal, generated texture +special_map[120] = range(2) # air portal frame, fixed or broken block # placeholders that are generated in generate() bgcolor = None From e692dfebdc8d44c06fd0e0338c7454a260433e7b Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Thu, 27 Oct 2011 12:09:50 +0200 Subject: [PATCH 12/47] Updated End Frame Portal block. --- overviewer_core/textures.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 039746c..92f448b 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2024,12 +2024,26 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) if blockID == 120: # air portal frame - if data == 0: # fixed block - t = terrain_images[159] - img = _build_full_block(t, None, None, t, t) - if data == 1: # broken block - t = terrain_images[158] - img = _build_block(t, t, blockID) + # The bottom 2 bits are oritation info but seems there is no + # graphical difference between orientations + top = terrain_images[158] + eye_t = terrain_images[174] + side = terrain_images[159] + img = _build_full_block((top, 4), None, None, side, side) + if data & 0x4 == 0x4: # ender eye on it + # generate the eye + eye_t = terrain_images[174].copy() + eye_t_s = terrain_images[174].copy() + # cut out from the texture the side and the top of the eye + ImageDraw.Draw(eye_t).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(eye_t_s).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + # trnasform images and paste + eye = transform_image(eye_t) + eye_s = transform_image_side(eye_t_s) + eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, eye_s, (5,5), eye_s) + composite.alpha_over(img, eye_os, (9,5), eye_os) + composite.alpha_over(img, eye, (0,0), eye) return generate_texture_tuple(img, blockID) @@ -2528,7 +2542,7 @@ special_map[116] = (0,) # enchantment table, nothings special. book not implemen special_map[117] = range(8) # brewing stand, number of bottles. (not implemented) special_map[118] = range(4) # cauldron, amount of water in it. special_map[119] = (0,) # air portal, generated texture -special_map[120] = range(2) # air portal frame, fixed or broken block +special_map[120] = range(5) # air portal frame orientation, with or without eye of ender # placeholders that are generated in generate() bgcolor = None From 1e66e6eecbd4fde7198d36d23552fae780efa146 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Thu, 27 Oct 2011 12:22:06 +0200 Subject: [PATCH 13/47] Add white stone block. --- overviewer_core/chunk.py | 2 +- overviewer_core/textures.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 2df3fdb..e1b454b 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -137,7 +137,7 @@ transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33 solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91, 95, 97, 98, - 99, 100, 103, 108, 109, 110, 112, 114]) + 99, 100, 103, 108, 109, 110, 112, 114, 121]) # This set holds block ids that are fluid blocks fluid_blocks = set([8,9,10,11]) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 92f448b..12840cb 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -441,8 +441,8 @@ def _build_blockimages(): 66, 69, 72, 73, 75, -1, 102,103,104,105,-1, 102, -1, -1, -1, -1, # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 -1, -1, -1, -1, -1, -1, -1, 137,-1, -1, -1, -1, -1, -1, 78, -1, - # 112 - 224 + # 112 113 114 115 116 117 118 119 120 121 + 224, -1, -1, -1, -1, -1, -1, -1, -1,175 ] # NOTE: For non-block textures, the sideid is ignored, but can't be -1 @@ -462,8 +462,8 @@ def _build_blockimages(): 66, 70, 72, 73, 74, -1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, # 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 -1, -1, -1, -1, -1 ,-1 ,-1, 136,-1, -1, -1, -1, -1, -1, 77, -1, - # 112 - 224 + # 112 113 114 115 116 117 118 119 120 121 + 224, -1, -1, -1, -1, -1, -1, -1, -1,175 ] # This maps block id to the texture that goes on the side of the block From 8a925ecc524f0ccd26feafe28e549791dfcb3d26 Mon Sep 17 00:00:00 2001 From: Jason Scheirer Date: Mon, 31 Oct 2011 01:34:48 -0700 Subject: [PATCH 14/47] Handle minecraft.jar if it's a directory (if you've installed mods etc.) --- overviewer_core/textures.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index c7e58d8..c404fc8 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -89,7 +89,7 @@ def _find_file(filename, mode="rb", verbose=False): jarpaths.append(os.path.join(_find_file_local_path, "minecraft.jar")) for jarpath in jarpaths: - if os.path.exists(jarpath): + if os.path.isfile(jarpath): jar = zipfile.ZipFile(jarpath) for jarfilename in [filename, 'misc/' + filename, 'environment/' + filename]: try: @@ -97,6 +97,12 @@ def _find_file(filename, mode="rb", verbose=False): return jar.open(jarfilename) except (KeyError, IOError), e: pass + elif os.path.isdir(jarpath): + for jarfilename in [filename, 'misc/' + filename, 'environment/' + filename]: + ondiskfilename = os.path.join(jarpath, jarfilename) + if os.path.isfile(ondiskfilename): + if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) + return open(ondiskfilename, 'rb') raise IOError("Could not find the file `{0}'. You can either place it in the same place as overviewer.py, use --textures-path, or install the Minecraft client.".format(filename)) From 69c109fc05ff1cd2da02b817ec8244d1c9c591c0 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 31 Oct 2011 13:28:28 -0400 Subject: [PATCH 15/47] moved trasparent_blocks, etc. into textures.py (Issue #516) --- overviewer_core/chunk.py | 19 ------------------- overviewer_core/src/iterate.c | 2 +- overviewer_core/src/rendermode-overlay.c | 4 ++-- overviewer_core/src/rendermode-spawn.c | 2 +- overviewer_core/textures.py | 19 +++++++++++++++++++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index b8af17a..e638350 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -125,25 +125,6 @@ def get_tileentity_data(level): data = level['TileEntities'] return data -# This set holds blocks ids that can be seen through, for occlusion calculations -transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, - 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109]) - -# This set holds block ids that are solid blocks -solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, - 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) - -# This set holds block ids that are fluid blocks -fluid_blocks = set([8,9,10,11]) - -# This set holds block ids that are not candidates for spawning mobs on -# (glass, slabs, stairs, fluids, ice, pistons, webs,TNT, wheat, cactus, iron bars, glass planes, fences, fence gate, cake, bed, repeaters, trapdoor) -nospawn_blocks = set([20,26, 29, 30, 33, 34, 44, 46, 53, 59, 67, 79, 81, 85, 92, 93, 94, 96, 107, 109, 101, 102]).union(fluid_blocks) - class ChunkCorrupt(Exception): pass diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 44f30f8..618c3aa 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -53,7 +53,7 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); if (!specialblockmap) return NULL; - transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks"); + transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; diff --git a/overviewer_core/src/rendermode-overlay.c b/overviewer_core/src/rendermode-overlay.c index 17b760b..72e58ff 100644 --- a/overviewer_core/src/rendermode-overlay.c +++ b/overviewer_core/src/rendermode-overlay.c @@ -38,8 +38,8 @@ rendermode_overlay_start(void *data, RenderState *state, PyObject *options) { self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); - self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); + self->solid_blocks = PyObject_GetAttrString(state->textures, "solid_blocks"); + self->fluid_blocks = PyObject_GetAttrString(state->textures, "fluid_blocks"); self->get_color = get_color; diff --git a/overviewer_core/src/rendermode-spawn.c b/overviewer_core/src/rendermode-spawn.c index a5c0a27..c303f23 100644 --- a/overviewer_core/src/rendermode-spawn.c +++ b/overviewer_core/src/rendermode-spawn.c @@ -72,7 +72,7 @@ rendermode_spawn_start(void *data, RenderState *state, PyObject *options) { /* now do custom initializations */ self = (RenderModeSpawn *)data; - self->nospawn_blocks = PyObject_GetAttrString(state->chunk, "nospawn_blocks"); + self->nospawn_blocks = PyObject_GetAttrString(state->textures, "nospawn_blocks"); self->blocklight = PyObject_GetAttrString(state->self, "blocklight"); self->skylight = PyObject_GetAttrString(state->self, "skylight"); diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index c7e58d8..f4416db 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2321,6 +2321,25 @@ def loadLightColor(): lightcolor = None return lightcolor +# This set holds blocks ids that can be seen through, for occlusion calculations +transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, + 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, + 106, 107, 108, 109]) + +# This set holds block ids that are solid blocks +solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, + 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) + +# This set holds block ids that are fluid blocks +fluid_blocks = set([8,9,10,11]) + +# This set holds block ids that are not candidates for spawning mobs on +# (glass, slabs, stairs, fluids, ice, pistons, webs,TNT, wheat, cactus, iron bars, glass planes, fences, fence gate, cake, bed, repeaters, trapdoor) +nospawn_blocks = set([20,26, 29, 30, 33, 34, 44, 46, 53, 59, 67, 79, 81, 85, 92, 93, 94, 96, 107, 109, 101, 102]).union(fluid_blocks) + # This set holds block ids that require special pre-computing. These are typically # things that require ancillary data to render properly (i.e. ladder plus orientation) # A good source of information is: From 75858f2df80fae3022fd48de73f7e23f7f2e6016 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 31 Oct 2011 13:40:38 -0400 Subject: [PATCH 16/47] removed historical, seperate mask from texture tuples (Issue #516) --- overviewer_core/src/iterate.c | 4 ++-- overviewer_core/textures.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 618c3aa..d0ab6a2 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -450,8 +450,8 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *src, *mask, *mask_light; int randx = 0, randy = 0; src = PyTuple_GetItem(t, 0); - mask = PyTuple_GetItem(t, 1); - mask_light = PyTuple_GetItem(t, 2); + mask = PyTuple_GetItem(t, 0); + mask_light = PyTuple_GetItem(t, 1); if (mask == Py_None) mask = src; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index f4416db..73cb5da 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -517,7 +517,7 @@ def generate_opaque_mask(img): def generate_texture_tuple(img, blockid): """ This takes an image and returns the needed tuple for the blockmap list and specialblockmap dictionary.""" - return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) + return (img, generate_opaque_mask(img)) def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" From 8e0a82ba62fc025fe74c107d6db759c17c473f0e Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 31 Oct 2011 13:58:25 -0400 Subject: [PATCH 17/47] unified blockmap and specialblockmap (Issue #516) --- overviewer_core/src/iterate.c | 65 +++++++++++++++-------------------- overviewer_core/textures.py | 52 +++++++++++++--------------- 2 files changed, 52 insertions(+), 65 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index d0ab6a2..dac8587 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -21,7 +21,6 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; static PyObject *special_blocks = NULL; -static PyObject *specialblockmap = NULL; static PyObject *transparent_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { @@ -50,9 +49,6 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { special_blocks = PyObject_GetAttrString(textures, "special_blocks"); if (!special_blocks) return NULL; - specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); - if (!specialblockmap) - return NULL; transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; @@ -380,6 +376,9 @@ chunk_render(PyObject *self, PyObject *args) { state.imgy = yoff - state.x*6 + state.y*6 + 128*12 + 15*6; for (state.z = 0; state.z < 128; state.z++) { + PyObject *tmp; + unsigned char ancilData; + state.imgy -= 12; /* get blockid */ @@ -403,47 +402,39 @@ chunk_render(PyObject *self, PyObject *args) { } blockid = PyInt_FromLong(state.block); - // check for occlusion + /* check for occlusion */ if (render_mode_occluded(rendermode, state.x, state.y, state.z)) { continue; } - // everything stored here will be a borrowed ref + /* everything stored here will be a borrowed ref */ - /* get the texture and mask from block type / ancil. data */ - if (!PySequence_Contains(special_blocks, blockid)) { - /* t = textures.blockmap[blockid] */ - t = PyList_GetItem(blockmap, state.block); + ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); + state.block_data = ancilData; + /* block that need pseudo ancildata: + * grass, water, glass, chest, restone wire, + * ice, fence, portal, iron bars, glass panes */ + if ((state.block == 2) || (state.block == 9) || + (state.block == 20) || (state.block == 54) || + (state.block == 55) || (state.block == 79) || + (state.block == 85) || (state.block == 90) || + (state.block == 101) || (state.block == 102)) { + ancilData = generate_pseudo_data(&state, ancilData); + state.block_pdata = ancilData; } else { - PyObject *tmp; - - unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); - state.block_data = ancilData; - /* block that need pseudo ancildata: - * grass, water, glass, chest, restone wire, - * ice, fence, portal, iron bars, glass panes */ - if ((state.block == 2) || (state.block == 9) || - (state.block == 20) || (state.block == 54) || - (state.block == 55) || (state.block == 79) || - (state.block == 85) || (state.block == 90) || - (state.block == 101) || (state.block == 102)) { - ancilData = generate_pseudo_data(&state, ancilData); - state.block_pdata = ancilData; - } else { - state.block_pdata = 0; - } - - tmp = PyTuple_New(2); - - Py_INCREF(blockid); /* because SetItem steals */ - PyTuple_SetItem(tmp, 0, blockid); - PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); - - /* this is a borrowed reference. no need to decref */ - t = PyDict_GetItem(specialblockmap, tmp); - Py_DECREF(tmp); + state.block_pdata = 0; } + tmp = PyTuple_New(2); + + Py_INCREF(blockid); /* because SetItem steals */ + PyTuple_SetItem(tmp, 0, blockid); + PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); + + /* this is a borrowed reference. no need to decref */ + t = PyDict_GetItem(blockmap, tmp); + Py_DECREF(tmp); + /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 73cb5da..36af92a 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -496,15 +496,22 @@ def load_water(): Block 8, flowing water, is given a full 3 sided cube.""" watertexture = _load_image("water.png") - w1 = _build_block(watertexture, None) - blockmap[9] = generate_texture_tuple(w1,9) - w2 = _build_block(watertexture, watertexture) - blockmap[8] = generate_texture_tuple(w2,8) - lavatexture = _load_image("lava.png") + + w1 = _build_block(watertexture, None) + w1 = generate_texture_tuple(w1,9) + w2 = _build_block(watertexture, watertexture) + w2 = generate_texture_tuple(w2,8) + lavablock = _build_block(lavatexture, lavatexture) - blockmap[10] = generate_texture_tuple(lavablock,10) - blockmap[11] = blockmap[10] + lava = generate_texture_tuple(lavablock,10) + + for data in range(16): + blockmap[(9, data)] = w1 + blockmap[(8, data)] = w2 + + blockmap[(10, data)] = lava + blockmap[(11, data)] = lava def generate_opaque_mask(img): """ Takes the alpha channel of the image and generates a mask @@ -516,7 +523,7 @@ def generate_opaque_mask(img): def generate_texture_tuple(img, blockid): """ This takes an image and returns the needed tuple for the - blockmap list and specialblockmap dictionary.""" + blockmap dictionary.""" return (img, generate_opaque_mask(img)) def generate_special_texture(blockID, data): @@ -2425,7 +2432,6 @@ bgcolor = None terrain_images = None blockmap = None biome_grass_texture = None -specialblockmap = None def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): global _north @@ -2443,7 +2449,10 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower # generate the normal blocks global blockmap - blockmap = _build_blockimages() + blockmap = {} + for blockID, t in enumerate(_build_blockimages()): + blockmap[(blockID, 0)] = t + load_water() # generate biome grass mask @@ -2451,32 +2460,19 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) # generate the special blocks - global specialblockmap, special_blocks - specialblockmap = {} + global special_blocks for blockID in special_blocks: for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) + blockmap[(blockID, data)] = generate_special_texture(blockID, data) if texture_size != 24: # rescale biome textures. biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS) - # rescale the normal block images - for i in range(len(blockmap)): - if blockmap[i] != None: - block = blockmap[i] - alpha = block[1] - block = block[0] - block.putalpha(alpha) - scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - blockmap[i] = generate_texture_tuple(scaled_block, i) - # rescale the special block images - for blockid, data in iter(specialblockmap): - block = specialblockmap[(blockid,data)] + for blockid, data in iter(blockmap): + block = blockmap[(blockid,data)] if block != None: - alpha = block[1] block = block[0] - block.putalpha(alpha) scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + blockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) From 4b905685ebc8b3bd6d18d1ae8ef64ac35ca537fe Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 31 Oct 2011 20:57:05 -0400 Subject: [PATCH 18/47] major textures.py restructuring, not all textures implemented (Issue #516) Be careful -- the build_* and transform_* functions no longer have a blockID argument, because that made no sense. --- overviewer_core/chunk.py | 2 +- overviewer_core/src/iterate.c | 4 - overviewer_core/textures.py | 2157 +++------------------------------ 3 files changed, 175 insertions(+), 1988 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index e638350..06a2048 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -393,7 +393,7 @@ def generate_facemasks(): left = Image.new("L", (24,24), 0) whole = Image.new("L", (24,24), 0) - toppart = textures.transform_image(white) + toppart = textures.transform_image_top(white) leftpart = textures.transform_image_side(white) # using the real PIL paste here (not alpha_over) because there is diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index dac8587..7d8dc23 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -20,7 +20,6 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; -static PyObject *special_blocks = NULL; static PyObject *transparent_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { @@ -46,9 +45,6 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { blockmap = PyObject_GetAttrString(textures, "blockmap"); if (!blockmap) return NULL; - special_blocks = PyObject_GetAttrString(textures, "special_blocks"); - if (!special_blocks) - return NULL; transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 36af92a..586f481 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -24,11 +24,26 @@ from random import randint import numpy from PIL import Image, ImageEnhance, ImageOps, ImageDraw import logging +import functools import util import composite +## +## useful global variables +## + +# user-provided path given by --texture-path _find_file_local_path = None +# image background color to use +bgcolor = None +# an array of the textures in terrain.png, split up +terrain_images = None + +## +## Helpers for opening textures +## + def _find_file(filename, mode="rb", verbose=False): """Searches for the given file and returns an open handle to it. This searches the following locations in this order: @@ -106,9 +121,6 @@ def _load_image(filename): buffer = StringIO(fileobj.read()) return Image.open(buffer).convert("RGBA") -def _get_terrain_image(): - return _load_image("terrain.png") - def _split_terrain(terrain): """Builds and returns a length 256 array of each 16x16 chunk of texture""" textures = [] @@ -129,7 +141,11 @@ def _split_terrain(terrain): return textures -def transform_image(img, blockID=None): +## +## Image Transformation Functions +## + +def transform_image_top(img): """Takes a PIL image and rotates it left 45 degrees and shrinks the y axis by a factor of 2. Returns the resulting image, which will be 24x12 pixels @@ -157,25 +173,10 @@ def transform_image(img, blockID=None): newimg = img.transform((24,12), Image.AFFINE, transform) return newimg -def transform_image_side(img, blockID=None): +def transform_image_side(img): """Takes an image and shears it for the left side of the cube (reflect for the right side)""" - if blockID in (44,): # step block - # make the top half transparent - # (don't just crop img, since we want the size of - # img to be unchanged - mask = img.crop((0,8,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,0,16,8), mask) - img = n - if blockID in (78,): # snow - # make the top three quarters transparent - mask = img.crop((0,12,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,12,16,16), mask) - img = n - # Size of the cube side before shear img = img.resize((12,12), Image.ANTIALIAS) @@ -188,7 +189,7 @@ def transform_image_side(img, blockID=None): newimg = img.transform((12,18), Image.AFFINE, transform) return newimg -def transform_image_slope(img, blockID=None): +def transform_image_slope(img): """Takes an image and shears it in the shape of a slope going up in the -y direction (reflect for +x direction). Used for minetracks""" @@ -205,7 +206,7 @@ def transform_image_slope(img, blockID=None): return newimg -def transform_image_angle(img, angle, blockID=None): +def transform_image_angle(img, angle): """Takes an image an shears it in arbitrary angle with the axis of rotation being vertical. @@ -245,7 +246,7 @@ def transform_image_angle(img, angle, blockID=None): return newimg -def _build_block(top, side, blockID=None): +def build_block(top, side): """From a top texture and a side texture, build a block image. top and side should be 16x16 image objects. Returns a 24x24 image @@ -253,13 +254,13 @@ def _build_block(top, side, blockID=None): img = Image.new("RGBA", (24,24), bgcolor) original_texture = top.copy() - top = transform_image(top, blockID) + top = transform_image_top(top) if not side: composite.alpha_over(img, top, (0,0), top) return img - side = transform_image_side(side, blockID) + side = transform_image_side(side) otherside = side.transpose(Image.FLIP_LEFT_RIGHT) # Darken the sides slightly. These methods also affect the alpha layer, @@ -272,39 +273,9 @@ def _build_block(top, side, blockID=None): otherside = ImageEnhance.Brightness(otherside).enhance(0.8) otherside.putalpha(othersidealpha) - ## special case for tall-grass, fern, dead shrub, and pumpkin/melon stem - if blockID in (31,32,104,105): - front = original_texture.resize((14,11), Image.ANTIALIAS) - composite.alpha_over(img, front, (5,9)) - return img - - ## special case for non-block things - if blockID in (37,38,6,39,40,83,30): ## flowers, sapling, mushrooms, reeds, web - # - # instead of pasting these blocks at the cube edges, place them in the middle: - # and omit the top - composite.alpha_over(img, side, (6,3), side) - composite.alpha_over(img, otherside, (6,3), otherside) - return img - - if blockID in (81,): # cacti! - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,6), otherside) - composite.alpha_over(img, top, (0,0), top) - elif blockID in (44,): # half step - # shift each texture down 6 pixels - composite.alpha_over(img, side, (0,12), side) - composite.alpha_over(img, otherside, (12,12), otherside) - composite.alpha_over(img, top, (0,6), top) - elif blockID in (78,): # snow - # shift each texture down 9 pixels - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,9), top) - else: - composite.alpha_over(img, top, (0,0), top) - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,0), top) + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) # Manually touch up 6 pixels that leave a gap because of how the # shearing works out. This makes the blocks perfectly tessellate-able @@ -317,8 +288,7 @@ def _build_block(top, side, blockID=None): return img - -def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None): +def build_full_block(top, side1, side2, side3, side4, bottom=None): """From a top texture, a bottom texture and 4 different side textures, build a full block with four differnts faces. All images should be 16x16 image objects. Returns a 24x24 image. Can be used to render any block. @@ -363,7 +333,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None # first back sides if side1 != None : - side1 = transform_image_side(side1, blockID) + side1 = transform_image_side(side1) side1 = side1.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side. @@ -375,7 +345,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None if side2 != None : - side2 = transform_image_side(side2, blockID) + side2 = transform_image_side(side2) # Darken this side. sidealpha2 = side2.split()[3] @@ -385,12 +355,12 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side2, (12,0), side2) if bottom != None : - bottom = transform_image(bottom, blockID) + bottom = transform_image_top(bottom) composite.alpha_over(img, bottom, (0,12), bottom) # front sides if side3 != None : - side3 = transform_image_side(side3, blockID) + side3 = transform_image_side(side3) # Darken this side sidealpha = side3.split()[3] @@ -400,7 +370,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side3, (0,6), side3) if side4 != None : - side4 = transform_image_side(side4, blockID) + side4 = transform_image_side(side4) side4 = side4.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side @@ -411,107 +381,22 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side4, (12,6), side4) if top != None : - top = transform_image(top, blockID) + top = transform_image_top(top) composite.alpha_over(img, top, (0, increment), top) return img - -def _build_blockimages(): - """Returns a mapping from blockid to an image of that block in perspective - The values of the mapping are actually (image in RGB mode, alpha channel). - This is not appropriate for all block types, only block types that are - proper cubes""" - - # Top textures of all block types. The number here is the index in the - # texture array (terrain_images), which comes from terrain.png's cells, left to right top to - # bottom. - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102, -1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 137, - ] - - # NOTE: For non-block textures, the sideid is ignored, but can't be -1 - - # And side textures of all block types - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 136, - ] - - # This maps block id to the texture that goes on the side of the block - if len(topids) != len(sideids): - raise Exception("mismatched lengths") - - allimages = [] - for toptextureid, sidetextureid,blockID in zip(topids, sideids,range(len(topids))): - if toptextureid == -1 or sidetextureid == -1: - allimages.append(None) - continue - - toptexture = terrain_images[toptextureid] - sidetexture = terrain_images[sidetextureid] - - ## _build_block needs to know about the block ID, not just the textures - ## of the block or the texture ID - img = _build_block(toptexture, sidetexture, blockID) - - allimages.append(generate_texture_tuple(img, blockID)) - - # Future block types: - while len(allimages) < 256: - allimages.append(None) - return allimages - -def load_water(): - """Evidentially, the water and lava textures are not loaded from any files - in the jar (that I can tell). They must be generated on the fly. While - terrain.png does have some water and lava cells, not all texture packs - include them. So I load them here from a couple pngs included. - - This mutates the blockmap global list with the new water and lava blocks. - Block 9, standing water, is given a block with only the top face showing. - Block 8, flowing water, is given a full 3 sided cube.""" - - watertexture = _load_image("water.png") - lavatexture = _load_image("lava.png") +def build_sprite(side): + """From a side texture, create a sprite-like texture such as those used + for spiderwebs or flowers.""" + img = Image.new("RGBA", (24,24), bgcolor) - w1 = _build_block(watertexture, None) - w1 = generate_texture_tuple(w1,9) - w2 = _build_block(watertexture, watertexture) - w2 = generate_texture_tuple(w2,8) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) - lavablock = _build_block(lavatexture, lavatexture) - lava = generate_texture_tuple(lavablock,10) - - for data in range(16): - blockmap[(9, data)] = w1 - blockmap[(8, data)] = w2 - - blockmap[(10, data)] = lava - blockmap[(11, data)] = lava + composite.alpha_over(img, side, (6,3), side) + composite.alpha_over(img, otherside, (6,3), otherside) + return img def generate_opaque_mask(img): """ Takes the alpha channel of the image and generates a mask @@ -521,1720 +406,27 @@ def generate_opaque_mask(img): alpha = img.split()[3] return alpha.point(lambda a: int(min(a, 25.5) * 10)) -def generate_texture_tuple(img, blockid): - """ This takes an image and returns the needed tuple for the - blockmap dictionary.""" - return (img, generate_opaque_mask(img)) - -def generate_special_texture(blockID, data): - """Generates a special texture, such as a correctly facing minecraft track""" - - data = convert_data(blockID, data) - - # blocks need to be handled here (and in chunk.py) - - if blockID == 2: # grass - # data & 0x10 means SNOW sides - side_img = terrain_images[3] - if data & 0x10: - side_img = terrain_images[68] - img = _build_block(terrain_images[0], side_img, 2) - if not data & 0x10: - global biome_grass_texture - composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) - return generate_texture_tuple(img, blockID) - - - if blockID == 6: # saplings - # The bottom two bits are used fo the sapling type, the top two - # bits are used as a grow-counter for the tree. - - if data & 0x3 == 0: # usual saplings - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - if data & 0x3 == 1: # spruce sapling - toptexture = terrain_images[63] - sidetexture = terrain_images[63] - - if data & 0x3 == 2: # birch sapling - toptexture = terrain_images[79] - sidetexture = terrain_images[79] - - if data & 0x3 == 3: # unused usual sapling - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - img = _build_block(toptexture, sidetexture, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice - # water,glass and ice share the way to be rendered - if blockID == 9: - texture = _load_image("water.png") - elif blockID == 20: - texture = terrain_images[49] - else: - texture = terrain_images[67] - - if (data & 0b10000) == 16: - top = texture - - else: top = None - - if (data & 0b0001) == 1: - side1 = texture # top left - else: side1 = None - - if (data & 0b1000) == 8: - side2 = texture # top right - else: side2 = None - - if (data & 0b0010) == 2: - side3 = texture # bottom left - else: side3 = None - - if (data & 0b0100) == 4: - side4 = texture # bottom right - else: side4 = None - - # if nothing shown do not draw at all - if top == side3 == side4 == None: - return None - - img = _build_full_block(top,None,None,side3,side4) - return generate_texture_tuple(img, blockID) - - - if blockID == 17: # wood: normal, birch and pines - top = terrain_images[21] - if data == 0: - side = terrain_images[20] - img = _build_block(top, side, 17) - if data == 1: - side = terrain_images[116] - img = _build_block(top, side, 17) - if data == 2: - side = terrain_images[117] - img = _build_block(top, side, 17) - - return generate_texture_tuple(img, blockID) - - - if blockID == 18: # leaves - t = terrain_images[52] - if data == 1: - # pine! - t = terrain_images[132] - img = _build_block(t, t, 18) - return generate_texture_tuple(img, blockID) - - - if blockID == 26: # bed - increment = 8 - left_face = None - right_face = None - if data & 0x8 == 0x8: # head of the bed - top = terrain_images[135] - if data & 0x00 == 0x00: # head pointing to West - top = top.copy().rotate(270) - left_face = terrain_images[151] - right_face = terrain_images[152] - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = terrain_images[152] - right_face = terrain_images[151] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - right_face = None - if data & 0x03 == 0x03: # South - right_face = None - right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - - else: # foot of the bed - top = terrain_images[134] - if data & 0x00 == 0x00: # head pointing to West - top = top.rotate(270) - left_face = terrain_images[150] - right_face = None - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = None - right_face = terrain_images[150] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) - if data & 0x03 == 0x03: # South - left_face = terrain_images[149] - right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - - top = (top, increment) - img = _build_full_block(top, None, None, left_face, right_face) - - return generate_texture_tuple(img, blockID) - - - if blockID == 31: # tall grass - if data == 0: # dead shrub - texture = terrain_images[55] - elif data == 1: # tall grass - texture = terrain_images[39] - elif data == 2: # fern - texture = terrain_images[56] - - img = _build_block(texture, texture, blockID) - return generate_texture_tuple(img,31) - - - if blockID in (29,33): # sticky and normal body piston. - if blockID == 29: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[109].copy() - interior_t = terrain_images[110].copy() - - if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff - # remove piston texture from piston body - ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x1: # up - img = _build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t , None, None ,side_t.rotate(90), back_t) - - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) - temp = transform_image_side(interior_t, blockID) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (9,5), temp) - - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) - temp = transform_image_side(interior_t, blockID) - composite.alpha_over(img, temp, (3,5), temp) - - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) - - else: # pushed in, normal full blocks, easy stuff - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x1: # up - img = _build_full_block(piston_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) - - - return generate_texture_tuple(img, blockID) - - - if blockID == 34: # piston extension (sticky and normal) - if (data & 0x8) == 0x8: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[107].copy() - # crop piston body - ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the horizontal piston extension stick - h_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t, blockID) - composite.alpha_over(h_stick, temp, (1,7), temp) - temp = transform_image(side_t.rotate(90)) - composite.alpha_over(h_stick, temp, (1,1), temp) - # Darken it - sidealpha = h_stick.split()[3] - h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) - h_stick.putalpha(sidealpha) - - # generate the vertical piston extension stick - v_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t.rotate(90), blockID) - composite.alpha_over(v_stick, temp, (12,6), temp) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(v_stick, temp, (1,6), temp) - # Darken it - sidealpha = v_stick.split()[3] - v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) - v_stick.putalpha(sidealpha) - - # Piston orientation is stored in the 3 first bits - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block((back_t, 12) ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,-3), v_stick) - elif data & 0x07 == 0x1: # up - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(piston_t ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,4), v_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), None) - temp = transform_image_side(back_t, blockID).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (2,2), temp) - composite.alpha_over(img, h_stick, (6,3), h_stick) - elif data & 0x07 == 0x3: # west - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - composite.alpha_over(img, h_stick, (0,0), h_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - elif data & 0x07 == 0x5: # south - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) - temp = transform_image_side(back_t, blockID) - composite.alpha_over(img2, temp, (10,2), temp) - composite.alpha_over(img, img2, (0,0), img2) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 35: # wool - if data == 0: # white - top = side = terrain_images[64] - elif data == 1: # orange - top = side = terrain_images[210] - elif data == 2: # magenta - top = side = terrain_images[194] - elif data == 3: # light blue - top = side = terrain_images[178] - elif data == 4: # yellow - top = side = terrain_images[162] - elif data == 5: # light green - top = side = terrain_images[146] - elif data == 6: # pink - top = side = terrain_images[130] - elif data == 7: # grey - top = side = terrain_images[114] - elif data == 8: # light grey - top = side = terrain_images[225] - elif data == 9: # cyan - top = side = terrain_images[209] - elif data == 10: # purple - top = side = terrain_images[193] - elif data == 11: # blue - top = side = terrain_images[177] - elif data == 12: # brown - top = side = terrain_images[161] - elif data == 13: # dark green - top = side = terrain_images[145] - elif data == 14: # red - top = side = terrain_images[129] - elif data == 15: # black - top = side = terrain_images[113] - - img = _build_block(top, side, 35) - return generate_texture_tuple(img, blockID) - - - if blockID in (43,44): # slab and double-slab - - if data == 0: # stone slab - top = terrain_images[6] - side = terrain_images[5] - elif data == 1: # stone slab - top = terrain_images[176] - side = terrain_images[192] - elif data == 2: # wooden slab - top = side = terrain_images[4] - elif data == 3: # cobblestone slab - top = side = terrain_images[16] - elif data == 4: # brick? - top = side = terrain_images[7] - elif data == 5: # stone brick? - top = side = terrain_images[54] - - img = _build_block(top, side, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID in (50,75,76): # torch, off redstone torch, on redstone torch - - # choose the proper texture - if blockID == 50: # torch - small = terrain_images[80] - elif blockID == 75: # off redstone torch - small = terrain_images[115] - else: # on redstone torch - small = terrain_images[99] - - # compose a torch bigger than the normal - # (better for doing transformations) - torch = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(torch,small,(-4,-3)) - composite.alpha_over(torch,small,(-5,-2)) - composite.alpha_over(torch,small,(-3,-2)) - - # angle of inclination of the texture - rotation = 15 - - if data == 1: # pointing south - torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. - img = _build_full_block(None, None, None, torch, None, None, blockID) - - elif data == 2: # pointing north - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, None, torch, None, None, None, blockID) - - elif data == 3: # pointing west - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, torch, None, None, None, None, blockID) - - elif data == 4: # pointing east - torch = torch.rotate(-rotation, Image.NEAREST) - img = _build_full_block(None, None, None, None, torch, None, blockID) - - elif data == 5: # standing on the floor - # compose a "3d torch". - img = Image.new("RGBA", (24,24), bgcolor) - - small_crop = small.crop((2,2,14,14)) - slice = small_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(img, slice, (7,5)) - composite.alpha_over(img, small_crop, (6,6)) - composite.alpha_over(img, small_crop, (7,6)) - composite.alpha_over(img, slice, (7,7)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 51: # fire - firetexture = _load_image("fire.png") - side1 = transform_image_side(firetexture) - side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, side1, (12,0), side1) - composite.alpha_over(img, side2, (0,0), side2) - - composite.alpha_over(img, side1, (0,6), side1) - composite.alpha_over(img, side2, (12,6), side2) - - return generate_texture_tuple(img, blockID) - - - if blockID in (53,67, 108, 109): # wooden, stone brick, and cobblestone stairs. - - if blockID == 53: # wooden - texture = terrain_images[4] - elif blockID == 67: # cobblestone - texture = terrain_images[16] - elif blockID == 108: # red brick stairs - texture = terrain_images[7] - elif blockID == 109: # stone brick stairs - texture = terrain_images[54] - - side = texture.copy() - half_block_u = texture.copy() # up, down, left, right - half_block_d = texture.copy() - half_block_l = texture.copy() - half_block_r = texture.copy() - - # generate needed geometries - ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data == 0: # ascending south - img = _build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) - tmp1 = transform_image_side(half_block_u) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_l) - composite.alpha_over(img, tmp2, (0,6)) - - elif data == 1: # ascending north - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_r) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_l, None, None, texture, side) - composite.alpha_over(img, tmp2) - - elif data == 2: # ascending west - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_u) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_d, None, None, side, texture) - composite.alpha_over(img, tmp2) - - elif data == 3: # ascending east - img = _build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) - tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_d) - composite.alpha_over(img, tmp2, (0,6)) - - # touch up a (horrible) pixel - img.putpixel((18,3),(0,0,0,0)) - - return generate_texture_tuple(img, blockID) - - if blockID == 54: # chests - # First to bits of the pseudo data store if it's a single chest - # or it's a double chest, first half or second half. - # The to last bits store the orientation. - - top = terrain_images[25] - side = terrain_images[26] - - if data & 12 == 0: # single chest - front = terrain_images[27] - back = terrain_images[26] - - elif data & 12 == 4: # double, first half - front = terrain_images[41] - back = terrain_images[57] - - elif data & 12 == 8: # double, second half - front = terrain_images[42] - back = terrain_images[58] - - else: # just in case - front = terrain_images[25] - side = terrain_images[25] - back = terrain_images[25] - - if data & 3 == 0: # facing west - img = _build_full_block(top, None, None, side, front) - - elif data & 3 == 1: # north - img = _build_full_block(top, None, None, front, side) - - elif data & 3 == 2: # east - img = _build_full_block(top, None, None, side, back) - - elif data & 3 == 3: # south - img = _build_full_block(top, None, None, back, side) - - else: - img = _build_full_block(top, None, None, back, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 55: # redstone wire - - if data & 0b1000000 == 64: # powered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) - - - else: # unpowered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) - - # generate an image per redstone direction - branch_top_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_top_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the bottom texture - if data & 0b111111 == 0: - bottom = redstone_cross_t.copy() - - elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction - bottom = redstone_wire_t.copy() - - elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction - bottom = redstone_wire_t.copy().rotate(90) - - else: - bottom = Image.new("RGBA", (16,16), bgcolor) - if (data & 0b0001) == 1: - composite.alpha_over(bottom,branch_top_left) - - if (data & 0b1000) == 8: - composite.alpha_over(bottom,branch_top_right) - - if (data & 0b0010) == 2: - composite.alpha_over(bottom,branch_bottom_left) - - if (data & 0b0100) == 4: - composite.alpha_over(bottom,branch_bottom_right) - - # check for going up redstone wire - if data & 0b100000 == 32: - side1 = redstone_wire_t.rotate(90) - else: - side1 = None - - if data & 0b010000 == 16: - side2 = redstone_wire_t.rotate(90) - else: - side2 = None - - img = _build_full_block(None,side1,side2,None,None,bottom) - - return generate_texture_tuple(img, blockID) - - - if blockID == 58: # crafting table - top = terrain_images[43] - side3 = terrain_images[43+16] - side4 = terrain_images[43+16+1] - - img = _build_full_block(top, None, None, side3, side4, None, 58) - return generate_texture_tuple(img, blockID) - - - if blockID == 59: # crops - raw_crop = terrain_images[88+data] - crop1 = transform_image(raw_crop, blockID) - crop2 = transform_image_side(raw_crop, blockID) - crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(img, crop1, (0,12), crop1) - composite.alpha_over(img, crop2, (6,3), crop2) - composite.alpha_over(img, crop3, (6,3), crop3) - return generate_texture_tuple(img, blockID) - - - if blockID in (61, 62, 23): #furnace and burning furnace - top = terrain_images[62] - side = terrain_images[45] - - if blockID == 61: - front = terrain_images[44] - - elif blockID == 62: - front = terrain_images[45+16] - - elif blockID == 23: - front = terrain_images[46] - - if data == 3: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 4: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 63: # singposts - - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # If the signpost is looking directly to the image, draw some - # random dots, they will look as text. - if data in (0,1,2,3,4,5,15): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - - # Minecraft uses wood texture for the signpost stick - texture_stick = terrain_images[20] - texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) - ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - img = Image.new("RGBA", (24,24), bgcolor) - - # W N ~90 E S ~270 - angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) - angle = math.radians(angles[data]) - post = transform_image_angle(texture, angle) - - # choose the position of the "3D effect" - incrementx = 0 - if data in (1,6,7,8,9,14): - incrementx = -1 - elif data in (3,4,5,11,12,13): - incrementx = +1 - - composite.alpha_over(img, texture_stick,(11, 8),texture_stick) - # post2 is a brighter signpost pasted with a small sift, - # gives to the signpost some 3D effect. - post2 = ImageEnhance.Brightness(post).enhance(1.2) - composite.alpha_over(img, post2,(incrementx, -3),post2) - composite.alpha_over(img, post, (0,-2), post) - - return generate_texture_tuple(img, blockID) - - - if blockID in (64,71): #wooden door, or iron door - if data & 0x8 == 0x8: # top of the door - raw_door = terrain_images[81 if blockID == 64 else 82] - else: # bottom of the door - raw_door = terrain_images[97 if blockID == 64 else 98] - - # if you want to render all doors as closed, then force - # force swung to be False - if data & 0x4 == 0x4: - swung=True - else: - swung=False - - # mask out the high bits to figure out the orientation - img = Image.new("RGBA", (24,24), bgcolor) - if (data & 0x03) == 0: # northeast corner - if not swung: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (0,6), tex) - else: - # flip first to set the doornob on the correct side - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - tex = tex.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - - if (data & 0x03) == 1: # southeast corner - if not swung: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - else: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (12,0), tex) - - if (data & 0x03) == 2: # southwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (12,0), tex) - else: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - - if (data & 0x03) == 3: # northwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - else: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (0,6), tex) - - return generate_texture_tuple(img, blockID) - - - if blockID == 65: # ladder - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[83] - #print "ladder is facing: %d" % data - if data == 5: - # normally this ladder would be obsured by the block it's attached to - # but since ladders can apparently be placed on transparent blocks, we - # have to render this thing anyway. same for data == 2 - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 2: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 3: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 4: - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - - if blockID in (27, 28, 66): # minetrack: - img = Image.new("RGBA", (24,24), bgcolor) - - if blockID == 27: # powered rail - if data & 0x8 == 0: # unpowered - raw_straight = terrain_images[163] - raw_corner = terrain_images[112] # they don't exist but make the code - # much simplier - elif data & 0x8 == 0x8: # powered - raw_straight = terrain_images[179] - raw_corner = terrain_images[112] # leave corners for code simplicity - # filter the 'powered' bit - data = data & 0x7 - - elif blockID == 28: # detector rail - raw_straight = terrain_images[195] - raw_corner = terrain_images[112] # leave corners for code simplicity - - elif blockID == 66: # normal rail - raw_straight = terrain_images[128] - raw_corner = terrain_images[112] - - ## use transform_image to scale and shear - if data == 0: - track = transform_image(raw_straight, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 6: - track = transform_image(raw_corner, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 7: - track = transform_image(raw_corner.rotate(270), blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 8: - # flip - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 9: - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 1: - track = transform_image(raw_straight.rotate(90), blockID) - composite.alpha_over(img, track, (0,12), track) - - #slopes - elif data == 2: # slope going up in +x direction - track = transform_image_slope(raw_straight,blockID) - track = track.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, track, (2,0), track) - # the 2 pixels move is needed to fit with the adjacent tracks - - elif data == 3: # slope going up in -x direction - # tracks are sprites, in this case we are seeing the "side" of - # the sprite, so draw a line to make it beautiful. - ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) - # grey from track texture (exterior grey). - # the track doesn't start from image corners, be carefull drawing the line! - elif data == 4: # slope going up in -y direction - track = transform_image_slope(raw_straight,blockID) - composite.alpha_over(img, track, (0,0), track) - - elif data == 5: # slope going up in +y direction - # same as "data == 3" - ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 68: # wall sign - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # draw some random black dots, they will look as text - """ don't draw text at the moment, they are used in blank for decoration - - if data in (3,4): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - """ - - img = Image.new("RGBA", (24,24), bgcolor) - - incrementx = 0 - if data == 2: # east - incrementx = +1 - sign = _build_full_block(None, None, None, None, texture) - elif data == 3: # west - incrementx = -1 - sign = _build_full_block(None, texture, None, None, None) - elif data == 4: # north - incrementx = +1 - sign = _build_full_block(None, None, texture, None, None) - elif data == 5: # south - incrementx = -1 - sign = _build_full_block(None, None, None, texture, None) - - sign2 = ImageEnhance.Brightness(sign).enhance(1.2) - composite.alpha_over(img, sign2,(incrementx, 2),sign2) - composite.alpha_over(img, sign, (0,3), sign) - - return generate_texture_tuple(img, blockID) - - if blockID == 70 or blockID == 72: # wooden and stone pressure plates - if blockID == 70: # stone - t = terrain_images[1].copy() - else: # wooden - t = terrain_images[4].copy() - - # cut out the outside border, pressure plates are smaller - # than a normal block - ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) - - # create the textures and a darker version to make a 3d by - # pasting them with an offstet of 1 pixel - img = Image.new("RGBA", (24,24), bgcolor) - - top = transform_image(t, blockID) - - alpha = top.split()[3] - topd = ImageEnhance.Brightness(top).enhance(0.8) - topd.putalpha(alpha) - - #show it 3d or 2d if unpressed or pressed - if data == 0: - composite.alpha_over(img,topd, (0,12),topd) - composite.alpha_over(img,top, (0,11),top) - elif data == 1: - composite.alpha_over(img,top, (0,12),top) - - return generate_texture_tuple(img, blockID) - - if blockID == 85: # fences - # create needed images for Big stick fence - - fence_top = terrain_images[4].copy() - fence_side = terrain_images[4].copy() - - # generate the textures of the fence - ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the big stick - fence_side = transform_image_side(fence_side,85) - fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) - fence_top = transform_image(fence_top,85) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_side.split()[3] - fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) - fence_side.putalpha(sidealpha) - othersidealpha = fence_other_side.split()[3] - fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) - fence_other_side.putalpha(othersidealpha) - - # Compose the fence big stick - fence_big = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(fence_big,fence_side, (5,4),fence_side) - composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) - composite.alpha_over(fence_big,fence_top, (0,0),fence_top) - - # Now render the small sticks. - # Create needed images - fence_small_side = terrain_images[4].copy() - - # Generate mask - ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the small sticks - fence_small_side = transform_image_side(fence_small_side,85) - fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_small_other_side.split()[3] - fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) - fence_small_other_side.putalpha(sidealpha) - sidealpha = fence_small_side.split()[3] - fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) - fence_small_side.putalpha(sidealpha) - - # Create img to compose the fence - img = Image.new("RGBA", (24,24), bgcolor) - - # Position of fence small sticks in img. - # These postitions are strange because the small sticks of the - # fence are at the very left and at the very right of the 16x16 images - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose small sticks in the back of the image, - # then big stick and thecn small sticks in the front. - - if (data & 0b0001) == 1: - composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left - if (data & 0b1000) == 8: - composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right - - composite.alpha_over(img,fence_big,(0,0),fence_big) - - if (data & 0b0010) == 2: - composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left - if (data & 0b0100) == 4: - composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right - - return generate_texture_tuple(img, blockID) - - - if blockID in (86,91): # pumpkins, jack-o-lantern - top = terrain_images[102] - frontID = 119 if blockID == 86 else 120 - front = terrain_images[frontID] - side = terrain_images[118] - - if data == 0: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 1: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 90: # portal - portaltexture = _load_image("portal.png") - img = Image.new("RGBA", (24,24), bgcolor) - - side = transform_image_side(portaltexture) - otherside = side.transpose(Image.FLIP_TOP_BOTTOM) - - if data in (1,4): - composite.alpha_over(img, side, (5,4), side) - - if data in (2,8): - composite.alpha_over(img, otherside, (5,4), otherside) - - return generate_texture_tuple(img, blockID) - - - if blockID == 92: # cake! (without bites, at the moment) - - top = terrain_images[121] - side = terrain_images[122] - top = transform_image(top, blockID) - side = transform_image_side(side, blockID) - otherside = side.transpose(Image.FLIP_LEFT_RIGHT) - - sidealpha = side.split()[3] - side = ImageEnhance.Brightness(side).enhance(0.9) - side.putalpha(sidealpha) - othersidealpha = otherside.split()[3] - otherside = ImageEnhance.Brightness(otherside).enhance(0.8) - otherside.putalpha(othersidealpha) - - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,6), top) - - return generate_texture_tuple(img, blockID) - - - if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF - # generate the diode - top = terrain_images[131] if blockID == 93 else terrain_images[147] - side = terrain_images[5] - increment = 13 - - if (data & 0x3) == 0: # pointing east - pass - - if (data & 0x3) == 1: # pointing south - top = top.rotate(270) - - if (data & 0x3) == 2: # pointing west - top = top.rotate(180) - - if (data & 0x3) == 3: # pointing north - top = top.rotate(90) - - img = _build_full_block( (top, increment), None, None, side, side) - - # compose a "3d" redstone torch - t = terrain_images[115].copy() if blockID == 93 else terrain_images[99].copy() - torch = Image.new("RGBA", (24,24), bgcolor) - - t_crop = t.crop((2,2,14,14)) - slice = t_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(torch, slice, (6,4)) - composite.alpha_over(torch, t_crop, (5,5)) - composite.alpha_over(torch, t_crop, (6,5)) - composite.alpha_over(torch, slice, (6,6)) - - # paste redstone torches everywhere! - # the torch is too tall for the repeater, crop the bottom. - ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # touch up the 3d effect with big rectangles, just in case, for other texture packs - ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # torch positions for every redstone torch orientation. - # - # This is a horrible list of torch orientations. I tried to - # obtain these orientations by rotating the positions for one - # orientation, but pixel rounding is horrible and messes the - # torches. - - if (data & 0x3) == 0: # pointing east - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,3) - static_torch = (-3,-1) - - elif (data & 0x3) == 1: # pointing south - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,2) - static_torch = (5,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,2) - static_torch = (5,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,3) - static_torch = (5,-1) - - elif (data & 0x3) == 2: # pointing west - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,0) - static_torch = (5,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,0) - static_torch = (5,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,-1) - static_torch = (5,3) - - elif (data & 0x3) == 3: # pointing north - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,0) - static_torch = (-3,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,0) - static_torch = (-3,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,-1) - static_torch = (-3,3) - - # this paste order it's ok for east and south orientation - # but it's wrong for north and west orientations. But using the - # default texture pack the torches are small enough to no overlap. - composite.alpha_over(img, torch, static_torch, torch) - composite.alpha_over(img, torch, moving_torch, torch) - - return generate_texture_tuple(img, blockID) - - - if blockID == 96: # trapdoor - texture = terrain_images[84] - if data & 0x4 == 0x4: # opened trapdoor - if data & 0x3 == 0: # west - img = _build_full_block(None, None, None, None, texture) - if data & 0x3 == 1: # east - img = _build_full_block(None, texture, None, None, None) - if data & 0x3 == 2: # south - img = _build_full_block(None, None, texture, None, None) - if data & 0x3 == 3: # north - img = _build_full_block(None, None, None, texture, None) - - elif data & 0x4 == 0: # closed trapdoor - img = _build_full_block((texture, 12), None, None, texture, texture) - - return generate_texture_tuple(img, blockID) - - if blockID == 98: # normal, mossy and cracked stone brick - if data == 0: # normal - t = terrain_images[54] - elif data == 1: # mossy - t = terrain_images[100] - else: # cracked - t = terrain_images[101] - - img = _build_full_block(t, None, None, t, t) - - return generate_texture_tuple(img, blockID) - - if blockID == 99 or blockID == 100: # huge brown and red mushroom - if blockID == 99: # brown - cap = terrain_images[126] - else: # red - cap = terrain_images[125] - stem = terrain_images[141] - porous = terrain_images[142] - - if data == 0: # fleshy piece - img = _build_full_block(porous, None, None, porous, porous) - - if data == 1: # north-east corner - img = _build_full_block(cap, None, None, cap, porous) - - if data == 2: # east side - img = _build_full_block(cap, None, None, porous, porous) - - if data == 3: # south-east corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 4: # north side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 5: # top piece - img = _build_full_block(cap, None, None, porous, porous) - - if data == 6: # south side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 7: # north-west corner - img = _build_full_block(cap, None, None, cap, cap) - - if data == 8: # west side - img = _build_full_block(cap, None, None, porous, cap) - - if data == 9: # south-west corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 10: # stem - img = _build_full_block(porous, None, None, stem, stem) - - return generate_texture_tuple(img, blockID) - - if blockID == 101 or blockID == 102: # iron bars and glass panes - if blockID == 101: - # iron bars - t = terrain_images[85] - else: - # glass panes - t = terrain_images[49] - left = t.copy() - right = t.copy() - - # generate the four small pieces of the glass pane - ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - up_left = transform_image_side(left) - up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) - dw_right = transform_image_side(right) - dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) - - # Create img to compose the texture - img = Image.new("RGBA", (24,24), bgcolor) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose things in the back of the image, - # then things in the front. - - if (data & 0b0001) == 1 or data == 0: - composite.alpha_over(img,up_left, (6,3),up_left) # top left - if (data & 0b1000) == 8 or data == 0: - composite.alpha_over(img,up_right, (6,3),up_right) # top right - if (data & 0b0010) == 2 or data == 0: - composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left - if (data & 0b0100) == 4 or data == 0: - composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right - - return generate_texture_tuple(img, blockID) - - if blockID == 104 or blockID == 105: # pumpkin and melon stems. - # the ancildata value indicates how much of the texture - # is shown. - if data & 48 == 0: - # not fully grown stem or no pumpkin/melon touching it, - # straight up stem - t = terrain_images[111].copy() - img = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) - img = _build_block(img, img, blockID) - if data & 7 == 7: - # fully grown stem gets brown color! - # there is a conditional in rendermode-normal to not - # tint the data value 7 - img = tintTexture(img, (211,169,116)) - return generate_texture_tuple(img, blockID) - - else: # fully grown, and a pumpking/melon touching it, - # corner stem - pass - - - if blockID == 106: # vine - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[143] - # print "vine is facing: %d" % data - if data == 2: # south - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 1: # east - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 4: # west - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 8: # north - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - if blockID == 107: - # create the closed gate side - gate_side = terrain_images[4].copy() - gate_side_draw = ImageDraw.Draw(gate_side) - gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # darken the sides slightly, as with the fences - sidealpha = gate_side.split()[3] - gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) - gate_side.putalpha(sidealpha) - - # create the other sides - mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT), blockID) - gate_side = transform_image_side(gate_side, blockID) - gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) - mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Create img to compose the fence gate - img = Image.new("RGBA", (24,24), bgcolor) - - if data & 0x4: - # opened - data = data & 0x3 - if data == 0: - composite.alpha_over(img, gate_side, (2,8), gate_side) - composite.alpha_over(img, gate_side, (13,3), gate_side) - elif data == 1: - composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) - composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) - elif data == 2: - composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) - composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) - elif data == 3: - composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) - else: - # closed - - # positions for pasting the fence sides, as with fences - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - if data == 0 or data == 2: - composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) - elif data == 1 or data == 3: - composite.alpha_over(img, gate_side, pos_top_left, gate_side) - composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) - - return generate_texture_tuple(img, blockID) - - return None - -def convert_data(blockID, data): - if blockID == 26: # bed - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID in (29, 33, 34): # sticky piston, piston, piston extension - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID in (27, 28, 66): # minetrack: - #Masked to not clobber powered rail on/off info - #Ascending and flat straight - if _north == 'upper-left': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID == 66: # normal minetrack only - #Corners - if _north == 'upper-left': - if data == 6: data = 7 - elif data == 7: data = 8 - elif data == 8: data = 6 - elif data == 9: data = 9 - elif _north == 'upper-right': - if data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 7 - elif _north == 'lower-right': - if data == 6: data = 9 - elif data == 7: data = 6 - elif data == 8: data = 8 - elif data == 9: data = 7 - if blockID in (50, 75, 76): # torch, off/on redstone torch - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 4 - elif data == 3: data = 2 - elif data == 4: data = 1 - elif _north == 'upper-right': - if data == 1: data = 2 - elif data == 2: data = 1 - elif data == 3: data = 4 - elif data == 4: data = 3 - elif _north == 'lower-right': - if data == 1: data = 4 - elif data == 2: data = 3 - elif data == 3: data = 1 - elif data == 4: data = 2 - if blockID in (53,67,108,109): # wooden and cobblestone stairs. - if _north == 'upper-left': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 1 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 1 - elif data == 1: data = 0 - elif data == 2: data = 3 - elif data == 3: data = 2 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 2 - elif data == 2: data = 0 - elif data == 3: data = 1 - if blockID in (61, 62, 23): # furnace and burning furnace - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 63: # signposts - if _north == 'upper-left': - data = (data + 4) % 16 - elif _north == 'upper-right': - data = (data + 8) % 16 - elif _north == 'lower-right': - data = (data + 12) % 16 - if blockID in (64,71): # wooden/iron door - #Masked to not clobber block top/bottom & swung info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 65: # ladder - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 68: # wall sign - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID in (86,91): # pumpkins, jack-o-lantern - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if blockID in (93, 94): # redstone repeaters, ON and OFF - #Masked to not clobber delay info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 96: # trapdoor - #Masked to not clobber opened/closed info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - if blockID == 99 or blockID == 100: # huge red and brown mushroom - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 6 - elif data == 3: data = 9 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 1 - elif data == 8: data = 4 - elif data == 9: data = 7 - elif _north == 'upper-right': - if data == 1: data = 9 - elif data == 2: data = 8 - elif data == 3: data = 7 - elif data == 4: data = 6 - elif data == 6: data = 4 - elif data == 7: data = 3 - elif data == 8: data = 2 - elif data == 9: data = 1 - elif _north == 'lower-right': - if data == 1: data = 7 - elif data == 2: data = 4 - elif data == 3: data = 1 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 3 - if blockID == 106: # vine - if _north == 'upper-left': - if data == 1: data = 2 - elif data == 4: data = 8 - elif data == 8: data = 1 - elif data == 2: data = 4 - elif _north == 'upper-right': - if data == 1: data = 4 - elif data == 4: data = 1 - elif data == 8: data = 2 - elif data == 2: data = 8 - elif _north == 'lower-right': - if data == 1: data = 8 - elif data == 4: data = 2 - elif data == 8: data = 4 - elif data == 2: data = 1 - if blockID == 107: # fence gates - opened = False - if data & 0x4: - data = data & 0x3 - opened = True - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if opened: - data = data | 0x4 - - return data - def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA return i +def generate_texture_tuple(img): + """ This takes an image and returns the needed tuple for the + blockmap dictionary.""" + return (img, generate_opaque_mask(img)) + +## +## Biomes +## + currentBiomeFile = None currentBiomeData = None grasscolor = None foliagecolor = None watercolor = None +_north = None def prepareBiomeData(worlddir): global grasscolor, foliagecolor, watercolor @@ -2275,6 +467,7 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + global _north biomeX = chunkX // 32 biomeY = chunkY // 32 rots = 0 @@ -2314,6 +507,10 @@ def getBiomeData(worlddir, chunkX, chunkY): currentBiomeData = data return data +## +## Color Light +## + lightcolor = None lightcolor_checked = False def loadLightColor(): @@ -2328,143 +525,99 @@ def loadLightColor(): lightcolor = None return lightcolor -# This set holds blocks ids that can be seen through, for occlusion calculations -transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, - 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109]) - -# This set holds block ids that are solid blocks -solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, - 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) - -# This set holds block ids that are fluid blocks -fluid_blocks = set([8,9,10,11]) - -# This set holds block ids that are not candidates for spawning mobs on -# (glass, slabs, stairs, fluids, ice, pistons, webs,TNT, wheat, cactus, iron bars, glass planes, fences, fence gate, cake, bed, repeaters, trapdoor) -nospawn_blocks = set([20,26, 29, 30, 33, 34, 44, 46, 53, 59, 67, 79, 81, 85, 92, 93, 94, 96, 107, 109, 101, 102]).union(fluid_blocks) - -# This set holds block ids that require special pre-computing. These are typically -# things that require ancillary data to render properly (i.e. ladder plus orientation) -# A good source of information is: -# http://www.minecraftwiki.net/wiki/Data_values -# (when adding new blocks here and in generate_special_textures, -# please, if possible, keep the ascending order of blockid value) - -special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, - 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, - 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, - 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109]) - -# this is a map of special blockIDs to a list of all -# possible values for ancillary data that it might have. - -special_map = {} - -# 0x10 means SNOW sides -special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is - # used in the mod WildGrass, and this - # small fix shows the map as expected, - # and is harmless for normal maps -special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings) -special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data -special_map[17] = range(3) # wood: normal, birch and pine -special_map[18] = range(16) # leaves, birch, normal or pine leaves -special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data -special_map[26] = range(12) # bed, orientation -special_map[23] = range(6) # dispensers, orientation -special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered -special_map[28] = range(6) # detector rail, orientation/slope -special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out -special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself -special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out -special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal -special_map[35] = range(16) # wool, colored and white -special_map[43] = range(6) # stone, sandstone, wooden and cobblestone double-slab -special_map[44] = range(6) # stone, sandstone, wooden and cobblestone slab -special_map[50] = (1,2,3,4,5) # torch, position in the block -special_map[51] = range(16) # fire, position in the block (not implemented) -special_map[53] = range(4) # wooden stairs, orientation -special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data -special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data -special_map[58] = (0,) # crafting table, it has 2 different sides -special_map[59] = range(8) # crops, grow from 0 to 7 -special_map[61] = range(6) # furnace, orientation -special_map[62] = range(6) # burning furnace, orientation -special_map[63] = range(16) # signpost, orientation -special_map[64] = range(16) # wooden door, open/close and orientation -special_map[65] = (2,3,4,5) # ladder, orientation -special_map[66] = range(10) # minecrart tracks, orientation, slope -special_map[67] = range(4) # cobblestone stairs, orientation -special_map[68] = (2,3,4,5) # wall sing, orientation -special_map[70] = (0,1) # stone pressure plate, non pressed and pressed -special_map[71] = range(16) # iron door, open/close and orientation -special_map[72] = (0,1) # wooden pressure plate, non pressed and pressed -special_map[75] = (1,2,3,4,5) # off redstone torch, orientation -special_map[76] = (1,2,3,4,5) # on redstone torch, orientation -special_map[79] = range(32) # ice, used to only render the exterior surface, uses pseudo data -special_map[85] = range(17) # fences, all the possible combination, uses pseudo data -special_map[86] = range(5) # pumpkin, orientation -special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data -special_map[91] = range(5) # jack-o-lantern, orientation -special_map[92] = range(6) # cake, eaten amount, (not implemented) -special_map[93] = range(16) # OFF redstone repeater, orientation and delay -special_map[94] = range(16) # ON redstone repeater, orientation and delay -special_map[96] = range(8) # trapdoor, open, closed, orientation -special_map[98] = range(3) # stone brick, normal, mossy and cracked -special_map[99] = range(11) # huge brown mushroom, side, corner, etc, piece -special_map[100] = range(11) # huge red mushroom, side, corner, etc, piece -special_map[101]= range(16) # iron bars, all the possible combination, uses pseudo data -special_map[102]= range(16) # glass panes, all the possible combination, uses pseudo data -special_map[104] = range(8) # pumpkin stem, size of the stem -special_map[105] = range(8) # melon stem, size of the stem -special_map[106] = (1,2,4,8) # vine, orientation -special_map[107] = range(8) # fence gates, orientation + open bit -special_map[108]= range(4) # red stairs, orientation -special_map[109]= range(4) # stonebrick stairs, orientation +## +## The big one: generate() and associated framework +## # placeholders that are generated in generate() -bgcolor = None -terrain_images = None -blockmap = None +texture_dimensions = None +blockmap_generators = {} +blockmap = {} biome_grass_texture = None +transparent_blocks = set([0,]) +solid_blocks = set() +fluid_blocks = set() +nospawn_blocks = set() + +# the material registration decorator +def material(blockIds, data=[0], **kwargs): + # mapping from property name to the set to store them in + properties = {"transparent" : transparent_blocks, "solid" : solid_blocks, "fluid" : fluid_blocks, "nospawn" : nospawn_blocks} + + # make sure blockIds and data are iterable + try: + blockIds = iter(blockIds) + except: + blockIds = [blockIds,] + try: + data = iter(data) + except: + data = [data,] + + def inner_material(func): + global blockmap_generators + + # create a wrapper function with a known signature + @functools.wraps(func) + def func_wrapper(blockId, data, north): + try: + return func(blockId, data, north) + except TypeError: + return func(blockId, data) + + for block in blockIds: + # set the property sets appropriately + for prop in properties: + if kwargs.get(prop, False): + properties[prop].update([block]) + + # populate blockmap_generators with our function + for d in data: + blockmap_generators[(block, d)] = func_wrapper + + return func_wrapper + return inner_material + +# shortcut function for pure blocks, default to solid +def block(blockid, top_index, side_index=None, **kwargs): + new_kwargs = {'solid' : True} + new_kwargs.update(kwargs) + + if side_index is None: + side_index = top_index + + @material(blockid, **new_kwargs) + def inner_block(unused_id, unused_data): + return build_block(terrain_images[top_index], terrain_images[side_index]) + return inner_block + def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): - global _north - _north = north_direction global _find_file_local_path global bgcolor + global texture_dimensions + global _north bgcolor = bgc - global _find_file_local_path, texture_dimensions _find_file_local_path = path + _north = north_direction texture_dimensions = (texture_size, texture_size) # This maps terainids to 16x16 images global terrain_images - terrain_images = _split_terrain(_get_terrain_image()) - - # generate the normal blocks - global blockmap - blockmap = {} - for blockID, t in enumerate(_build_blockimages()): - blockmap[(blockID, 0)] = t - - load_water() + terrain_images = _split_terrain(_load_image("terrain.png")) # generate biome grass mask global biome_grass_texture - biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) - - # generate the special blocks - global special_blocks - for blockID in special_blocks: - for data in special_map[blockID]: - blockmap[(blockID, data)] = generate_special_texture(blockID, data) + biome_grass_texture = build_block(terrain_images[0], terrain_images[38]) + # generate the blocks + global blockmap, blockmap_generators + blockmap = {} + for blockid, data in blockmap_generators: + texgen = blockmap_generators[(blockid, data)] + tex = texgen(blockid, data, north_direction) + blockmap[(blockid, data)] = generate_texture_tuple(tex) + if texture_size != 24: # rescale biome textures. biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS) @@ -2476,3 +629,41 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower block = block[0] scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) blockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + +## +## and finally: actual texture definitions +## + +# stone +block(1, 1) + +@material(2, range(11) + [0x10,], solid=True) +def grass(blockid, data): + # 0x10 bit means SNOW + side_img = terrain_images[3] + if data & 0x10: + side_img = terrain_images[68] + img = build_block(terrain_images[0], side_img) + if not data & 0x10: + global biome_grass_texture + composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) + return img + +# dirt +block(3, 2) +# cobblestone +block(4, 16) +# wooden plank +block(5, 4) + +@material(6, range(16), transparent=True) +def saplings(blockid, data): + # usual saplings + tex = terrain_images[15] + + if data & 0x3 == 1: # spruce sapling + tex = terrain_images[63] + if data & 0x3 == 2: # birch sapling + tex = terrain_images[79] + + return build_sprite(tex) From 7a92343bb8d7ec5d37b488011b1a376fc5a405b2 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 1 Nov 2011 15:51:10 -0400 Subject: [PATCH 19/47] @material and block have been made more verbose (Issue #516) --- overviewer_core/textures.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 586f481..0609789 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -541,15 +541,15 @@ fluid_blocks = set() nospawn_blocks = set() # the material registration decorator -def material(blockIds, data=[0], **kwargs): +def material(blockid=[], data=[0], **kwargs): # mapping from property name to the set to store them in properties = {"transparent" : transparent_blocks, "solid" : solid_blocks, "fluid" : fluid_blocks, "nospawn" : nospawn_blocks} - # make sure blockIds and data are iterable + # make sure blockid and data are iterable try: - blockIds = iter(blockIds) + blockid = iter(blockid) except: - blockIds = [blockIds,] + blockid = [blockid,] try: data = iter(data) except: @@ -560,13 +560,13 @@ def material(blockIds, data=[0], **kwargs): # create a wrapper function with a known signature @functools.wraps(func) - def func_wrapper(blockId, data, north): + def func_wrapper(blockid, data, north): try: - return func(blockId, data, north) + return func(blockid, data, north) except TypeError: - return func(blockId, data) + return func(blockid, data) - for block in blockIds: + for block in blockid: # set the property sets appropriately for prop in properties: if kwargs.get(prop, False): @@ -580,14 +580,17 @@ def material(blockIds, data=[0], **kwargs): return inner_material # shortcut function for pure blocks, default to solid -def block(blockid, top_index, side_index=None, **kwargs): +def block(blockid=[], top_index=None, side_index=None, **kwargs): new_kwargs = {'solid' : True} new_kwargs.update(kwargs) + if top_index is None: + raise ValueError("top_index was not provided") + if side_index is None: side_index = top_index - @material(blockid, **new_kwargs) + @material(blockid=blockid, **new_kwargs) def inner_block(unused_id, unused_data): return build_block(terrain_images[top_index], terrain_images[side_index]) return inner_block @@ -635,9 +638,9 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower ## # stone -block(1, 1) +block(blockid=1, top_index=1) -@material(2, range(11) + [0x10,], solid=True) +@material(blockid=2, data=range(11)+[0x10,], solid=True) def grass(blockid, data): # 0x10 bit means SNOW side_img = terrain_images[3] @@ -650,13 +653,13 @@ def grass(blockid, data): return img # dirt -block(3, 2) +block(blockid=3, top_index=2) # cobblestone -block(4, 16) +block(blockid=4, top_index=16) # wooden plank -block(5, 4) +block(blockid=5, top_index=4) -@material(6, range(16), transparent=True) +@material(blockid=6, data=range(16), transparent=True) def saplings(blockid, data): # usual saplings tex = terrain_images[15] From da142dfb7a720f73b652701f616d6680ba884c79 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 1 Nov 2011 18:18:29 -0400 Subject: [PATCH 20/47] textures re-implemented for blocks up to id 25 --- overviewer_core/textures.py | 140 +++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 2 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 0609789..004cf42 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -415,6 +415,8 @@ def tintTexture(im, c): def generate_texture_tuple(img): """ This takes an image and returns the needed tuple for the blockmap dictionary.""" + if img is None: + return None return (img, generate_opaque_mask(img)) ## @@ -547,11 +549,11 @@ def material(blockid=[], data=[0], **kwargs): # make sure blockid and data are iterable try: - blockid = iter(blockid) + iter(blockid) except: blockid = [blockid,] try: - data = iter(data) + iter(data) except: data = [data,] @@ -670,3 +672,137 @@ def saplings(blockid, data): tex = terrain_images[79] return build_sprite(tex) + +# bedrock +block(blockid=7, top_index=17) + +@material(blockid=8, data=range(16), fluid=True, transparent=True) +def water(blockid, data): + watertex = _load_image("water.png") + return build_block(watertex, watertex) + +# other water, glass, and ice (no inner surfaces) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[9, 20, 79], data=range(32), transparent=True, nospawn=True) +def no_inner_surfaces(blockid, data): + if blockid == 9: + texture = _load_image("water.png") + elif blockid == 20: + texture = terrain_images[49] + else: + texture = terrain_images[67] + + if (data & 0b10000) == 16: + top = texture + else: + top = None + + if (data & 0b0001) == 1: + side1 = texture # top left + else: + side1 = None + + if (data & 0b1000) == 8: + side2 = texture # top right + else: + side2 = None + + if (data & 0b0010) == 2: + side3 = texture # bottom left + else: + side3 = None + + if (data & 0b0100) == 4: + side4 = texture # bottom right + else: + side4 = None + + # if nothing shown do not draw at all + if top is None and side3 is None and side4 is None: + return None + + img = build_full_block(top,None,None,side3,side4) + return img + +@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False) +def lava(blockid, data): + lavatex = _load_image("lava.png") + return build_block(lavatex, lavatex) + +# sand +block(blockid=12, top_index=18) +# gravel +block(blockid=13, top_index=19) +# gold ore +block(blockid=14, top_index=32) +# iron ore +block(blockid=15, top_index=33) +# coal ore +block(blockid=16, top_index=34) + +@material(blockid=17, data=range(3), solid=True) +def wood(blockid, data): + top = terrain_images[21] + if data == 0: # normal + return build_block(top, terrain_images[20]) + if data == 1: # birch + return build_block(top, terrain_images[116]) + if data == 2: # pine + return build_block(top, terrain_images[117]) + +@material(blockid=18, data=range(16), transparent=True, solid=True) +def leaves(blockid, data): + t = terrain_images[52] + if data == 1: + # pine! + t = terrain_images[132] + return build_block(t, t) + +# sponge +block(blockid=19, top_index=48) +# lapis lazuli ore +block(blockid=21, top_index=160) +# lapis lazuli block +block(blockid=22, top_index=144) + +# dispensers, furnaces, and burning furnaces +@material(blockid=[23, 61, 62], data=range(6), solid=True) +def furnaces(blockid, data, north): + # first, do the north rotation if needed + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + top = terrain_images[62] + side = terrain_images[45] + + if blockid == 61: + front = terrain_images[44] + elif blockid == 62: + front = terrain_images[61] + elif blockid == 23: + front = terrain_images[46] + + if data == 3: # pointing west + return build_full_block(top, None, None, side, front) + elif data == 4: # pointing north + return build_full_block(top, None, None, front, side) + else: # in any other direction the front can't be seen + return build_full_block(top, None, None, side, side) + +# sandstone +block(blockid=24, top_index=176, side_index=192) +# note block +block(blockid=25, top_index=74) From c9fc7eba622824b7d02bdf482fe7fd38ac06da9d Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 1 Nov 2011 21:10:11 -0400 Subject: [PATCH 21/47] textures added back for blocks up to id 50 --- overviewer_core/textures.py | 549 +++++++++++++++++++++++++++++++++++- 1 file changed, 546 insertions(+), 3 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 004cf42..3b1396c 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -398,6 +398,15 @@ def build_sprite(side): composite.alpha_over(img, otherside, (6,3), otherside) return img +def build_billboard(tex): + """From a texture, create a billboard-like texture such as those used for + tall grass or melon stems.""" + img = Image.new("RGBA", (24,24), bgcolor) + + front = tex.resize((14, 11), Image.ANTIALIAS) + composite.alpha_over(img, front, (5,9)) + return img + def generate_opaque_mask(img): """ Takes the alpha channel of the image and generates a mask (used for lighting the block) that deprecates values of alpha @@ -571,8 +580,12 @@ def material(blockid=[], data=[0], **kwargs): for block in blockid: # set the property sets appropriately for prop in properties: - if kwargs.get(prop, False): - properties[prop].update([block]) + try: + if block in kwargs.get(prop, []): + properties[prop].update([block]) + except TypeError: + if kwargs.get(prop, False): + properties[prop].update([block]) # populate blockmap_generators with our function for d in data: @@ -597,6 +610,32 @@ def block(blockid=[], top_index=None, side_index=None, **kwargs): return build_block(terrain_images[top_index], terrain_images[side_index]) return inner_block +# shortcut function for sprite blocks, defaults to transparent +def sprite(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_sprite(unused_id, unused_data): + return build_sprite(terrain_images[index]) + return inner_sprite + +# shortcut function for billboard blocks, defaults to transparent +def billboard(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_billboard(unused_id, unused_data): + return build_billboard(terrain_images[index]) + return inner_billboard + def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): global _find_file_local_path global bgcolor @@ -683,7 +722,7 @@ def water(blockid, data): # other water, glass, and ice (no inner surfaces) # uses pseudo-ancildata found in iterate.c -@material(blockid=[9, 20, 79], data=range(32), transparent=True, nospawn=True) +@material(blockid=[9, 20, 79], data=range(32), fluid=(9,), transparent=True, nospawn=True) def no_inner_surfaces(blockid, data): if blockid == 9: texture = _load_image("water.png") @@ -806,3 +845,507 @@ def furnaces(blockid, data, north): block(blockid=24, top_index=176, side_index=192) # note block block(blockid=25, top_index=74) + +@material(blockid=26, data=range(12), transparent=True) +def bed(blockid, data, north): + # first get north rotation done + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + increment = 8 + left_face = None + right_face = None + if data & 0x8 == 0x8: # head of the bed + top = terrain_images[135] + if data & 0x00 == 0x00: # head pointing to West + top = top.copy().rotate(270) + left_face = terrain_images[151] + right_face = terrain_images[152] + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = terrain_images[152] + right_face = terrain_images[151] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + right_face = None + if data & 0x03 == 0x03: # South + right_face = None + right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + + else: # foot of the bed + top = terrain_images[134] + if data & 0x00 == 0x00: # head pointing to West + top = top.rotate(270) + left_face = terrain_images[150] + right_face = None + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = None + right_face = terrain_images[150] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) + if data & 0x03 == 0x03: # South + left_face = terrain_images[149] + right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + + top = (top, increment) + return build_full_block(top, None, None, left_face, right_face) + +# powered, detector, and normal rails +@material(blockid=[27, 28, 66], data=range(14), transparent=True) +def rails(blockid, data, north): + # first, do north rotation + # Masked to not clobber powered rail on/off info + # Ascending and flat straight + if north == 'upper-left': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + img = Image.new("RGBA", (24,24), bgcolor) + + if blockid == 27: # powered rail + if data & 0x8 == 0: # unpowered + raw_straight = terrain_images[163] + raw_corner = terrain_images[112] # they don't exist but make the code + # much simplier + elif data & 0x8 == 0x8: # powered + raw_straight = terrain_images[179] + raw_corner = terrain_images[112] # leave corners for code simplicity + # filter the 'powered' bit + data = data & 0x7 + + elif blockid == 28: # detector rail + raw_straight = terrain_images[195] + raw_corner = terrain_images[112] # leave corners for code simplicity + + elif blockid == 66: # normal rail + raw_straight = terrain_images[128] + raw_corner = terrain_images[112] + + ## use transform_image to scale and shear + if data == 0: + track = transform_image_top(raw_straight) + composite.alpha_over(img, track, (0,12), track) + elif data == 6: + track = transform_image_top(raw_corner) + composite.alpha_over(img, track, (0,12), track) + elif data == 7: + track = transform_image_top(raw_corner.rotate(270)) + composite.alpha_over(img, track, (0,12), track) + elif data == 8: + # flip + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90)) + composite.alpha_over(img, track, (0,12), track) + elif data == 9: + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM)) + composite.alpha_over(img, track, (0,12), track) + elif data == 1: + track = transform_image_top(raw_straight.rotate(90)) + composite.alpha_over(img, track, (0,12), track) + + #slopes + elif data == 2: # slope going up in +x direction + track = transform_image_slope(raw_straight) + track = track.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, track, (2,0), track) + # the 2 pixels move is needed to fit with the adjacent tracks + + elif data == 3: # slope going up in -x direction + # tracks are sprites, in this case we are seeing the "side" of + # the sprite, so draw a line to make it beautiful. + ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) + # grey from track texture (exterior grey). + # the track doesn't start from image corners, be carefull drawing the line! + elif data == 4: # slope going up in -y direction + track = transform_image_slope(raw_straight) + composite.alpha_over(img, track, (0,0), track) + + elif data == 5: # slope going up in +y direction + # same as "data == 3" + ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) + + return img + +# sticky and normal piston body +@material(blockid=[29, 33], data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, solid=True) +def piston(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if blockid == 29: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[109].copy() + interior_t = terrain_images[110].copy() + + if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff + # remove piston texture from piston body + ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x1: # up + img = build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t , None, None ,side_t.rotate(90), back_t) + + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) + temp = transform_image_side(interior_t) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (9,5), temp) + + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) + temp = transform_image_side(interior_t) + composite.alpha_over(img, temp, (3,5), temp) + + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) + + else: # pushed in, normal full blocks, easy stuff + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x1: # up + img = build_full_block(piston_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) + + return img + +# sticky and normal piston shaft +@material(blockid=34, data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True) +def piston_extension(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if (data & 0x8) == 0x8: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[107].copy() + # crop piston body + ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the horizontal piston extension stick + h_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t) + composite.alpha_over(h_stick, temp, (1,7), temp) + temp = transform_image_top(side_t.rotate(90)) + composite.alpha_over(h_stick, temp, (1,1), temp) + # Darken it + sidealpha = h_stick.split()[3] + h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) + h_stick.putalpha(sidealpha) + + # generate the vertical piston extension stick + v_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t.rotate(90)) + composite.alpha_over(v_stick, temp, (12,6), temp) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(v_stick, temp, (1,6), temp) + # Darken it + sidealpha = v_stick.split()[3] + v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) + v_stick.putalpha(sidealpha) + + # Piston orientation is stored in the 3 first bits + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block((back_t, 12) ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,-3), v_stick) + elif data & 0x07 == 0x1: # up + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(piston_t ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,4), v_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), None) + temp = transform_image_side(back_t).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (2,2), temp) + composite.alpha_over(img, h_stick, (6,3), h_stick) + elif data & 0x07 == 0x3: # west + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + composite.alpha_over(img, h_stick, (0,0), h_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + elif data & 0x07 == 0x5: # south + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) + temp = transform_image_side(back_t) + composite.alpha_over(img2, temp, (10,2), temp) + composite.alpha_over(img, img2, (0,0), img2) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + + return img + +# cobweb +sprite(blockid=30, index=11) + +@material(blockid=31, data=range(3), transparent=True) +def tall_grass(blockid, data): + if data == 0: # dead shrub + texture = terrain_images[55] + elif data == 1: # tall grass + texture = terrain_images[39] + elif data == 2: # fern + texture = terrain_images[56] + + return build_billboard(texture) + +# dead bush +billboard(blockid=32, index=55) + +@material(blockid=35, data=range(16), solid=True) +def wool(blockid, data): + if data == 0: # white + texture = terrain_images[64] + elif data == 1: # orange + texture = terrain_images[210] + elif data == 2: # magenta + texture = terrain_images[194] + elif data == 3: # light blue + texture = terrain_images[178] + elif data == 4: # yellow + texture = terrain_images[162] + elif data == 5: # light green + texture = terrain_images[146] + elif data == 6: # pink + texture = terrain_images[130] + elif data == 7: # grey + texture = terrain_images[114] + elif data == 8: # light grey + texture = terrain_images[225] + elif data == 9: # cyan + texture = terrain_images[209] + elif data == 10: # purple + texture = terrain_images[193] + elif data == 11: # blue + texture = terrain_images[177] + elif data == 12: # brown + texture = terrain_images[161] + elif data == 13: # dark green + texture = terrain_images[145] + elif data == 14: # red + texture = terrain_images[129] + elif data == 15: # black + texture = terrain_images[113] + + return build_block(texture, texture) + +# dandelion +sprite(blockid=37, index=13) +# rose +sprite(blockid=38, index=12) +# brown mushroom +sprite(blockid=39, index=29) +# red mushroom +sprite(blockid=40, index=28) +# block of gold +block(blockid=41, top_index=23) +# block of iron +block(blockid=42, top_index=22) + +# double slabs and slabs +@material(blockid=[43, 44], data=range(6), transparent=(44,), solid=(43,)) +def slabs(blockid, data): + if data == 0: # stone slab + top = terrain_images[6] + side = terrain_images[5] + elif data == 1: # stone slab + top = terrain_images[176] + side = terrain_images[192] + elif data == 2: # wooden slab + top = side = terrain_images[4] + elif data == 3: # cobblestone slab + top = side = terrain_images[16] + elif data == 4: # brick? + top = side = terrain_images[7] + elif data == 5: # stone brick? + top = side = terrain_images[54] + + if blockid == 43: # double slab + return build_block(top, side) + + # plain slab + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + img = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(img, side, (0,12), side) + composite.alpha_over(img, otherside, (12,12), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# brick block +block(blockid=45, top_index=7) +# TNT +block(blockid=46, top_index=9, side_index=8) +# bookshelf +block(blockid=47, top_index=4, side_index=35) +# moss stone +block(blockid=48, top_index=36) +# obsidian +block(blockid=49, top_index=37) + +# torch, redstone torch (off), redstone torch(on) +@material(blockid=[50, 75, 76], data=[1, 2, 3, 4, 5], transparent=True) +def torches(blockid, data, north): + # first, north rotations + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + elif north == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 + + # choose the proper texture + if blockid == 50: # torch + small = terrain_images[80] + elif blockid == 75: # off redstone torch + small = terrain_images[115] + else: # on redstone torch + small = terrain_images[99] + + # compose a torch bigger than the normal + # (better for doing transformations) + torch = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(torch,small,(-4,-3)) + composite.alpha_over(torch,small,(-5,-2)) + composite.alpha_over(torch,small,(-3,-2)) + + # angle of inclination of the texture + rotation = 15 + + if data == 1: # pointing south + torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. + img = build_full_block(None, None, None, torch, None, None) + + elif data == 2: # pointing north + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, None, torch, None, None, None) + + elif data == 3: # pointing west + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, torch, None, None, None, None) + + elif data == 4: # pointing east + torch = torch.rotate(-rotation, Image.NEAREST) + img = build_full_block(None, None, None, None, torch, None) + + elif data == 5: # standing on the floor + # compose a "3d torch". + img = Image.new("RGBA", (24,24), bgcolor) + + small_crop = small.crop((2,2,14,14)) + slice = small_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(img, slice, (7,5)) + composite.alpha_over(img, small_crop, (6,6)) + composite.alpha_over(img, small_crop, (7,6)) + composite.alpha_over(img, slice, (7,7)) + + return img From 2cc160bcaa9e769df9f3426d19108685e5c24235 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Thu, 3 Nov 2011 13:52:02 +0100 Subject: [PATCH 22/47] Add up to blockid 85. --- overviewer_core/textures.py | 619 ++++++++++++++++++++++++++++++++++++ 1 file changed, 619 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 3b1396c..9a9794d 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1349,3 +1349,622 @@ def torches(blockid, data, north): composite.alpha_over(img, slice, (7,7)) return img + +# fire +@material(blockid=51, data=range(16), transparent=True) +def fire(blockid, data): + firetexture = _load_image("fire.png") + side1 = transform_image_side(firetexture) + side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + + composite.alpha_over(img, side1, (12,0), side1) + composite.alpha_over(img, side2, (0,0), side2) + + composite.alpha_over(img, side1, (0,6), side1) + composite.alpha_over(img, side2, (12,6), side2) + + return img + +# monster spawner +block(blockid=52, top_index=34) + +# wooden, cobblestone, red brick, stone brick and netherbrick stairs. +@material(blockid=[53,67,108,109,114], data=range(4), transparent=True) +def stairs(blockid, data, north): + + # first, north rotations + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + if blockid == 53: # wooden + texture = terrain_images[4] + elif blockid == 67: # cobblestone + texture = terrain_images[16] + elif blockid == 108: # red brick stairs + texture = terrain_images[7] + elif blockid == 109: # stone brick stairs + texture = terrain_images[54] + elif blockid == 114: # netherbrick stairs + texture = terrain_images[224] + + side = texture.copy() + half_block_u = texture.copy() # up, down, left, right + half_block_d = texture.copy() + half_block_l = texture.copy() + half_block_r = texture.copy() + + # generate needed geometries + ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data == 0: # ascending south + img = build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) + tmp1 = transform_image_side(half_block_u) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_l) + composite.alpha_over(img, tmp2, (0,6)) + + elif data == 1: # ascending north + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_r) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_l, None, None, texture, side) + composite.alpha_over(img, tmp2) + + elif data == 2: # ascending west + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_u) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_d, None, None, side, texture) + composite.alpha_over(img, tmp2) + + elif data == 3: # ascending east + img = build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) + tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_d) + composite.alpha_over(img, tmp2, (0,6)) + + # touch up a (horrible) pixel + img.putpixel((18,3),(0,0,0,0)) + + return img + +# normal and locked chest (locked was the one used in april fools' day) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[54,95], data=range(12), transparent=True) +def chests(blockid, data): + # First two bits of the pseudo data store if it's a single chest + # or it's a double chest, first half or second half (left to right). + # The last two bits store the orientation. + + # No need for north stuff, uses pseudo data and rotates with the map + + top = terrain_images[25] + side = terrain_images[26] + + if data & 12 == 0: # single chest + front = terrain_images[27] + back = terrain_images[26] + + elif data & 12 == 4: # double, first half + front = terrain_images[41] + back = terrain_images[57] + + elif data & 12 == 8: # double, second half + front = terrain_images[42] + back = terrain_images[58] + + else: # just in case + front = terrain_images[25] + side = terrain_images[25] + back = terrain_images[25] + + if data & 3 == 0: # facing west + img = build_full_block(top, None, None, side, front) + + elif data & 3 == 1: # north + img = build_full_block(top, None, None, front, side) + + elif data & 3 == 2: # east + img = build_full_block(top, None, None, side, back) + + elif data & 3 == 3: # south + img = build_full_block(top, None, None, back, side) + + else: + img = build_full_block(top, None, None, back, side) + + return img + +# redstone wire +# uses pseudo-ancildata found in iterate.c +@material(blockid=55, data=range(128), transparent=True) +def wire(blockid, data): + + if data & 0b1000000 == 64: # powered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) + + + else: # unpowered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) + + # generate an image per redstone direction + branch_top_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_top_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the bottom texture + if data & 0b111111 == 0: + bottom = redstone_cross_t.copy() + + elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction + bottom = redstone_wire_t.copy() + + elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction + bottom = redstone_wire_t.copy().rotate(90) + + else: + bottom = Image.new("RGBA", (16,16), bgcolor) + if (data & 0b0001) == 1: + composite.alpha_over(bottom,branch_top_left) + + if (data & 0b1000) == 8: + composite.alpha_over(bottom,branch_top_right) + + if (data & 0b0010) == 2: + composite.alpha_over(bottom,branch_bottom_left) + + if (data & 0b0100) == 4: + composite.alpha_over(bottom,branch_bottom_right) + + # check for going up redstone wire + if data & 0b100000 == 32: + side1 = redstone_wire_t.rotate(90) + else: + side1 = None + + if data & 0b010000 == 16: + side2 = redstone_wire_t.rotate(90) + else: + side2 = None + + img = build_full_block(None,side1,side2,None,None,bottom) + + return img + +# crafting table +# needs two different sides +@material(blockid=58, solid=True) +def crafting_table(blockid, data): + top = terrain_images[43] + side3 = terrain_images[43+16] + side4 = terrain_images[43+16+1] + + img = build_full_block(top, None, None, side3, side4, None) + return img + +# crops +@material(blockid=59, data=range(8), transparent=True) +def crops(blockid, data): + raw_crop = terrain_images[88+data] + crop1 = transform_image_top(raw_crop) + crop2 = transform_image_side(raw_crop) + crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(img, crop1, (0,12), crop1) + composite.alpha_over(img, crop2, (6,3), crop2) + composite.alpha_over(img, crop3, (6,3), crop3) + return img + +# signposts +@material(blockid=63, data=range(16), transparent=True) +def signpost(blockid, data, north): + + # first north rotations + if north == 'upper-left': + data = (data + 4) % 16 + elif north == 'upper-right': + data = (data + 8) % 16 + elif north == 'lower-right': + data = (data + 12) % 16 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # If the signpost is looking directly to the image, draw some + # random dots, they will look as text. + if data in (0,1,2,3,4,5,15): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + + # Minecraft uses wood texture for the signpost stick + texture_stick = terrain_images[20] + texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) + ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + img = Image.new("RGBA", (24,24), bgcolor) + + # W N ~90 E S ~270 + angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) + angle = math.radians(angles[data]) + post = transform_image_angle(texture, angle) + + # choose the position of the "3D effect" + incrementx = 0 + if data in (1,6,7,8,9,14): + incrementx = -1 + elif data in (3,4,5,11,12,13): + incrementx = +1 + + composite.alpha_over(img, texture_stick,(11, 8),texture_stick) + # post2 is a brighter signpost pasted with a small shift, + # gives to the signpost some 3D effect. + post2 = ImageEnhance.Brightness(post).enhance(1.2) + composite.alpha_over(img, post2,(incrementx, -3),post2) + composite.alpha_over(img, post, (0,-2), post) + + return img + + +# wooden and iron door +@material(blockid=[64,71], data=range(16), transparent=True) +def door(blockid, data, north): + #Masked to not clobber block top/bottom & swung info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + if data & 0x8 == 0x8: # top of the door + raw_door = terrain_images[81 if blockid == 64 else 82] + else: # bottom of the door + raw_door = terrain_images[97 if blockid == 64 else 98] + + # if you want to render all doors as closed, then force + # force swung to be False + if data & 0x4 == 0x4: + swung=True + else: + swung=False + + # mask out the high bits to figure out the orientation + img = Image.new("RGBA", (24,24), bgcolor) + if (data & 0x03) == 0: # northeast corner + if not swung: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (0,6), tex) + else: + # flip first to set the doornob on the correct side + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + tex = tex.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if (data & 0x03) == 1: # southeast corner + if not swung: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + else: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (12,0), tex) + + if (data & 0x03) == 2: # southwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (12,0), tex) + else: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if (data & 0x03) == 3: # northwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + else: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (0,6), tex) + + return img + +# ladder +@material(blockd=65, data=[2, 3, 4, 5], transparent = True) +def ladder(blockid, data, north): + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[83] + + if data == 5: + # normally this ladder would be obsured by the block it's attached to + # but since ladders can apparently be placed on transparent blocks, we + # have to render this thing anyway. same for data == 2 + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + return generate_texture_tuple(img, blockID) + if data == 2: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + return generate_texture_tuple(img, blockID) + if data == 3: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + return generate_texture_tuple(img, blockID) + if data == 4: + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + return generate_texture_tuple(img, blockID) + + +# wall signs +@material(blockid=68, data=[2, 3, 4, 5], trasnparent=True) +def wall_sign(blockid, data, north): # wall sign + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # draw some random black dots, they will look as text + """ don't draw text at the moment, they are used in blank for decoration + + if data in (3,4): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + """ + + img = Image.new("RGBA", (24,24), bgcolor) + + incrementx = 0 + if data == 2: # east + incrementx = +1 + sign = build_full_block(None, None, None, None, texture) + elif data == 3: # west + incrementx = -1 + sign = build_full_block(None, texture, None, None, None) + elif data == 4: # north + incrementx = +1 + sign = build_full_block(None, None, texture, None, None) + elif data == 5: # south + incrementx = -1 + sign = build_full_block(None, None, None, texture, None) + + sign2 = ImageEnhance.Brightness(sign).enhance(1.2) + composite.alpha_over(img, sign2,(incrementx, 2),sign2) + composite.alpha_over(img, sign, (0,3), sign) + + return img + + +# wooden and stone pressure plates +@material(blockid=[70, 72], data=[0,1], transparent=True) +def pressure_plate(blockid, data): + if blockid == 70: # stone + t = terrain_images[1].copy() + else: # wooden + t = terrain_images[4].copy() + + # cut out the outside border, pressure plates are smaller + # than a normal block + ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) + + # create the textures and a darker version to make a 3d by + # pasting them with an offstet of 1 pixel + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(t) + + alpha = top.split()[3] + topd = ImageEnhance.Brightness(top).enhance(0.8) + topd.putalpha(alpha) + + #show it 3d or 2d if unpressed or pressed + if data == 0: + composite.alpha_over(img,topd, (0,12),topd) + composite.alpha_over(img,top, (0,11),top) + elif data == 1: + composite.alpha_over(img,top, (0,12),top) + + return img + + +# nether and normal fences +# uses pseudo-ancildata found in iterate.c +@material(blockid=[85, 113], data=range(16), transparent=True) +def fence(blockid, data): + # no need for north rotations, it uses pseudo data. + # create needed images for Big stick fence + if blockid == 85: # normal fence + fence_top = terrain_images[4].copy() + fence_side = terrain_images[4].copy() + fence_small_side = terrain_images[4].copy() + else: # netherbrick fence + fence_top = terrain_images[224].copy() + fence_side = terrain_images[224].copy() + fence_small_side = terrain_images[224].copy() + + # generate the textures of the fence + ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the big stick + fence_side = transform_image_side(fence_side) + fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) + fence_top = transform_image_top(fence_top) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_side.split()[3] + fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) + fence_side.putalpha(sidealpha) + othersidealpha = fence_other_side.split()[3] + fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) + fence_other_side.putalpha(othersidealpha) + + # Compose the fence big stick + fence_big = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(fence_big,fence_side, (5,4),fence_side) + composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) + composite.alpha_over(fence_big,fence_top, (0,0),fence_top) + + # Now render the small sticks. + # Create needed images + ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the small sticks + fence_small_side = transform_image_side(fence_small_side) + fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_small_other_side.split()[3] + fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) + fence_small_other_side.putalpha(sidealpha) + sidealpha = fence_small_side.split()[3] + fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) + fence_small_side.putalpha(sidealpha) + + # Create img to compose the fence + img = Image.new("RGBA", (24,24), bgcolor) + + # Position of fence small sticks in img. + # These postitions are strange because the small sticks of the + # fence are at the very left and at the very right of the 16x16 images + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose small sticks in the back of the image, + # then big stick and thecn small sticks in the front. + + if (data & 0b0001) == 1: + composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left + if (data & 0b1000) == 8: + composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right + + composite.alpha_over(img,fence_big,(0,0),fence_big) + + if (data & 0b0010) == 2: + composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left + if (data & 0b0100) == 4: + composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right + + return img From eb1f79ddf122d05e245aa978d43aac9280dd06cc Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 3 Nov 2011 11:44:36 -0300 Subject: [PATCH 23/47] Fixed typo --- overviewer_core/textures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 9a9794d..a7b93e2 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1783,7 +1783,7 @@ def ladder(blockid, data, north): # wall signs -@material(blockid=68, data=[2, 3, 4, 5], trasnparent=True) +@material(blockid=68, data=[2, 3, 4, 5], transparent=True) def wall_sign(blockid, data, north): # wall sign # first north rotations From acb2d159f5552792ea8b935ea1989bdddd808751 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 3 Nov 2011 15:56:01 -0400 Subject: [PATCH 24/47] filled in simpler blocks between id 50 and 85 --- overviewer_core/textures.py | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index a7b93e2..ceb16c6 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1589,6 +1589,11 @@ def wire(blockid, data): return img +# diamond ore +block(blockid=56, top_index=50) +# diamond block +block(blockid=57, top_index=24) + # crafting table # needs two different sides @material(blockid=58, solid=True) @@ -1614,6 +1619,14 @@ def crops(blockid, data): composite.alpha_over(img, crop3, (6,3), crop3) return img +# farmland +@material(blockid=60, data=range(9), solid=True) +def farmland(blockid, data): + top = terrain_images[86] + if data == 0: + top = terrain_images[87] + return build_block(top, terrain_images[2]) + # signposts @material(blockid=63, data=range(16), transparent=True) def signpost(blockid, data, north): @@ -1839,6 +1852,9 @@ def wall_sign(blockid, data, north): # wall sign return img +## +## not rendered: levers +## # wooden and stone pressure plates @material(blockid=[70, 72], data=[0,1], transparent=True) @@ -1871,6 +1887,72 @@ def pressure_plate(blockid, data): return img +# normal and glowing redstone ore +block(blockid=[73, 74], top_index=51) + +## +## not rendered: buttons +## + +# snow +@material(blockid=78, data=range(8), transparent=True) +def snow(blockid, data): + # still not rendered correctly: data other than 0 + tex = terrain_images[66] + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(tex) + side = transform_image_side(tex) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,9), top) + + return img + +# snow block +block(blockid=80, top_index=66) + +# cactus +@material(blockid=81, data=range(15), transparent=True, solid=True, nospawn=True) +def cactus(blockid, data): + top = terrain_images[69] + side = terrain_images[70] + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,6), otherside) + composite.alpha_over(img, top, (0,0), top) + + return img + +# clay block +block(blockid=82, top_index=72) + +# sugar cane +@material(blockid=83, data=range(16), transparent=True) +def sugar_cane(blockid, data): + tex = terrain_images[73] + return build_sprite(tex) + +# jukebox +@material(blockid=84, data=range(16), solid=True) +def jukebox(blockid, data): + return build_block(terrain_images[75], terrain_images[74]) # nether and normal fences # uses pseudo-ancildata found in iterate.c From c1fdcffad7932be804664414b033b5542326f995 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 3 Nov 2011 16:07:56 -0400 Subject: [PATCH 25/47] fixed rendering error in snow and half-steps --- overviewer_core/textures.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index ceb16c6..ed0c21a 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1250,6 +1250,11 @@ def slabs(blockid, data): if blockid == 43: # double slab return build_block(top, side) + # cut the side texture in half + mask = side.crop((0,8,16,16)) + side = Image.new(side.mode, side.size, bgcolor) + composite.alpha_over(side, mask,(0,0,16,8), mask) + # plain slab top = transform_image_top(top) side = transform_image_side(side) @@ -1898,12 +1903,18 @@ block(blockid=[73, 74], top_index=51) @material(blockid=78, data=range(8), transparent=True) def snow(blockid, data): # still not rendered correctly: data other than 0 + tex = terrain_images[66] + # make the side image, top 3/4 transparent + mask = tex.crop((0,12,16,16)) + sidetex = Image.new(tex.mode, tex.size, bgcolor) + composite.alpha_over(sidetex, mask, (0,12,16,16), mask) + img = Image.new("RGBA", (24,24), bgcolor) top = transform_image_top(tex) - side = transform_image_side(tex) + side = transform_image_side(sidetex) otherside = side.transpose(Image.FLIP_LEFT_RIGHT) composite.alpha_over(img, side, (0,6), side) From 1cfe21153ac7ceecbba135ebc66dbe0aee07ff2f Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 11:56:35 +0100 Subject: [PATCH 26/47] All blocks before 1.9pre added. --- overviewer_core/textures.py | 279 ++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index ed0c21a..71581f8 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2061,3 +2061,282 @@ def fence(blockid, data): composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right return img + +# pumpkin +@material(blockid=[86, 91], data=range(4), solid=True) +def pumpkin(blockid, data, north): # pumpkins, jack-o-lantern + # north rotation + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + + # texture generation + top = terrain_images[102] + frontID = 119 if blockid == 86 else 120 + front = terrain_images[frontID] + side = terrain_images[118] + + if data == 0: # pointing west + img = build_full_block(top, None, None, side, front) + + elif data == 1: # pointing north + img = build_full_block(top, None, None, front, side) + + else: # in any other direction the front can't be seen + img = build_full_block(top, None, None, side, side) + + return img + +# netherrack +block(blockid=87, top_index=103) + +# soul sand +block(blockid=88, top_index=104) + +# glowstone +block(blockid=89, top_index=105) + +# portal +@material(blockid=90, data=[1, 2, 4, 8], transparent=True) +def portal(blockid, data): + # no north orientation uses pseudo data + portaltexture = _load_image("portal.png") + img = Image.new("RGBA", (24,24), bgcolor) + + side = transform_image_side(portaltexture) + otherside = side.transpose(Image.FLIP_TOP_BOTTOM) + + if data in (1,4): + composite.alpha_over(img, side, (5,4), side) + + if data in (2,8): + composite.alpha_over(img, otherside, (5,4), otherside) + + return img + +# cake! +# TODO is rendered un-bitten +@material(blockid=92, data=range(6), transparent=True) +def cake(blockid, data): + + # choose textures for cake + top = terrain_images[121] + side = terrain_images[122] + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + # darken sides slightly + sidealpha = side.split()[3] + side = ImageEnhance.Brightness(side).enhance(0.9) + side.putalpha(sidealpha) + othersidealpha = otherside.split()[3] + otherside = ImageEnhance.Brightness(otherside).enhance(0.8) + otherside.putalpha(othersidealpha) + + img = Image.new("RGBA", (24,24), bgcolor) + + # composite the cake + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# redstone repeaters ON and OFF +@material(blockid=[93,94], data=range(16), transparent=True) +def repeater(blockid, data, north): + # north rotation + # Masked to not clobber delay info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + # generate the diode + top = terrain_images[131] if blockid == 93 else terrain_images[147] + side = terrain_images[5] + increment = 13 + + if (data & 0x3) == 0: # pointing east + pass + + if (data & 0x3) == 1: # pointing south + top = top.rotate(270) + + if (data & 0x3) == 2: # pointing west + top = top.rotate(180) + + if (data & 0x3) == 3: # pointing north + top = top.rotate(90) + + img = build_full_block( (top, increment), None, None, side, side) + + # compose a "3d" redstone torch + t = terrain_images[115].copy() if blockid == 93 else terrain_images[99].copy() + torch = Image.new("RGBA", (24,24), bgcolor) + + t_crop = t.crop((2,2,14,14)) + slice = t_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(torch, slice, (6,4)) + composite.alpha_over(torch, t_crop, (5,5)) + composite.alpha_over(torch, t_crop, (6,5)) + composite.alpha_over(torch, slice, (6,6)) + + # paste redstone torches everywhere! + # the torch is too tall for the repeater, crop the bottom. + ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # touch up the 3d effect with big rectangles, just in case, for other texture packs + ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # torch positions for every redstone torch orientation. + # + # This is a horrible list of torch orientations. I tried to + # obtain these orientations by rotating the positions for one + # orientation, but pixel rounding is horrible and messes the + # torches. + + if (data & 0x3) == 0: # pointing east + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,3) + static_torch = (-3,-1) + + elif (data & 0x3) == 1: # pointing south + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,2) + static_torch = (5,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,2) + static_torch = (5,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,3) + static_torch = (5,-1) + + elif (data & 0x3) == 2: # pointing west + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,0) + static_torch = (5,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,0) + static_torch = (5,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,-1) + static_torch = (5,3) + + elif (data & 0x3) == 3: # pointing north + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,0) + static_torch = (-3,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,0) + static_torch = (-3,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,-1) + static_torch = (-3,3) + + # this paste order it's ok for east and south orientation + # but it's wrong for north and west orientations. But using the + # default texture pack the torches are small enough to no overlap. + composite.alpha_over(img, torch, static_torch, torch) + composite.alpha_over(img, torch, moving_torch, torch) + + return img + +# trapdoor +# TODO the trapdoor is looks like a sprite when opened, that's not good +@material(blockid=96, data=range(8), transparent=True) +def trapdoor(blockid, data, north): + + # north rotation + # Masked to not clobber opened/closed info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + + # texture generation + texture = terrain_images[84] + if data & 0x4 == 0x4: # opened trapdoor + if data & 0x3 == 0: # west + img = build_full_block(None, None, None, None, texture) + if data & 0x3 == 1: # east + img = build_full_block(None, texture, None, None, None) + if data & 0x3 == 2: # south + img = build_full_block(None, None, texture, None, None) + if data & 0x3 == 3: # north + img = build_full_block(None, None, None, texture, None) + + elif data & 0x4 == 0: # closed trapdoor + img = build_full_block((texture, 12), None, None, texture, texture) + + return img From 3f628dcc1b159e04ef9613de7a921f6018a4688a Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 13:16:35 +0100 Subject: [PATCH 27/47] Add all the 1.9 pre-release blocks. --- overviewer_core/textures.py | 451 ++++++++++++++++++++++++++++++++++++ 1 file changed, 451 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 71581f8..77a00d0 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2340,3 +2340,454 @@ def trapdoor(blockid, data, north): img = build_full_block((texture, 12), None, None, texture, texture) return img + +# block with hidden silverfish (stone, cobblestone and stone brick) +@material(blockid=97, data=range(3), solid=True) +def hidden_silverfish(blockid, data): + if data == 0: # stone + t = terrain_images[1] + elif data == 1: # cobblestone + t = terrain_images[16] + elif data == 2: # stone brick + t = terrain_images[54] + + img = build_block(t, t) + + return img + +# stone brick +@material(blockid=98, data=range(3), solid=True) +def stone_brick(blockid, data): + if data == 0: # normal + t = terrain_images[54] + elif data == 1: # mossy + t = terrain_images[100] + else: # cracked + t = terrain_images[101] + + img = build_full_block(t, None, None, t, t) + + return img + +# huge brown and red mushroom +@material(blockid=[99,100], data=range(11), solid=True) +def huge_mushroom(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 6 + elif data == 3: data = 9 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 1 + elif data == 8: data = 4 + elif data == 9: data = 7 + elif north == 'upper-right': + if data == 1: data = 9 + elif data == 2: data = 8 + elif data == 3: data = 7 + elif data == 4: data = 6 + elif data == 6: data = 4 + elif data == 7: data = 3 + elif data == 8: data = 2 + elif data == 9: data = 1 + elif north == 'lower-right': + if data == 1: data = 7 + elif data == 2: data = 4 + elif data == 3: data = 1 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 3 + + # texture generation + if blockid == 99: # brown + cap = terrain_images[126] + else: # red + cap = terrain_images[125] + + stem = terrain_images[141] + porous = terrain_images[142] + + if data == 0: # fleshy piece + img = build_full_block(porous, None, None, porous, porous) + + if data == 1: # north-east corner + img = build_full_block(cap, None, None, cap, porous) + + if data == 2: # east side + img = build_full_block(cap, None, None, porous, porous) + + if data == 3: # south-east corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 4: # north side + img = build_full_block(cap, None, None, cap, porous) + + if data == 5: # top piece + img = build_full_block(cap, None, None, porous, porous) + + if data == 6: # south side + img = build_full_block(cap, None, None, cap, porous) + + if data == 7: # north-west corner + img = build_full_block(cap, None, None, cap, cap) + + if data == 8: # west side + img = build_full_block(cap, None, None, porous, cap) + + if data == 9: # south-west corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 10: # stem + img = build_full_block(porous, None, None, stem, stem) + + return img + +# iron bars and glass pane +# TODO glass pane is not a sprite, it has a texture for the side, +# at the moment is not used +@material(blockid=[101,102], data=range(16), transparent=True) +def panes(blockid, data): + # no north rotation, uses pseudo data + if blockid == 101: + # iron bars + t = terrain_images[85] + else: + # glass panes + t = terrain_images[49] + left = t.copy() + right = t.copy() + + # generate the four small pieces of the glass pane + ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + up_left = transform_image_side(left) + up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) + dw_right = transform_image_side(right) + dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) + + # Create img to compose the texture + img = Image.new("RGBA", (24,24), bgcolor) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose things in the back of the image, + # then things in the front. + + if (data & 0b0001) == 1 or data == 0: + composite.alpha_over(img,up_left, (6,3),up_left) # top left + if (data & 0b1000) == 8 or data == 0: + composite.alpha_over(img,up_right, (6,3),up_right) # top right + if (data & 0b0010) == 2 or data == 0: + composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left + if (data & 0b0100) == 4 or data == 0: + composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right + + return img + +# melon +block(blockid=103, top_index=137, side_index=136, solid=True) + +# pumpkin and melon stem +# TODO To render it as in game needs from pseudo data and ancil data: +# once fully grown the stem bends to the melon/pumpkin block, +# at the moment only render the growing stem +@material(blockid=[104,105], data=range(8), transparent=True) +def stem(blockid, data, north): + # the ancildata value indicates how much of the texture + # is shown. + if data & 7 == 0: + # not fully grown stem or no pumpkin/melon touching it, + # straight up stem + t = terrain_images[111].copy() + img = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) + img = build_sprite(t) + if data & 7 == 7: + # fully grown stem gets brown color! + # there is a conditional in rendermode-normal to not + # tint the data value 7 + img = tintTexture(img, (211,169,116)) + return img + + else: # fully grown, and a pumpking/melon touching it, + # corner stem + return None + +# vines +# TODO multiple sides of a block can contain vines! At the moment +# only pure directions are rendered +# (source http://www.minecraftwiki.net/wiki/Data_values#Vines) +@material(blockid=106, data=range(8), transparent=True) +def vines(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 2 + elif data == 4: data = 8 + elif data == 8: data = 1 + elif data == 2: data = 4 + elif north == 'upper-right': + if data == 1: data = 4 + elif data == 4: data = 1 + elif data == 8: data = 2 + elif data == 2: data = 8 + elif north == 'lower-right': + if data == 1: data = 8 + elif data == 4: data = 2 + elif data == 8: data = 4 + elif data == 2: data = 1 + + # texture generation + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[143] + + if data == 2: # south + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + + if data == 1: # east + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if data == 4: # west + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if data == 8: # north + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + + return img + +# fence gates +@material(blockid=107, data=range(8), transparent=True) +def fence_gate(blockid, data, north): + + # north rotation + opened = False + if data & 0x4: + data = data & 0x3 + opened = True + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + if opened: + data = data | 0x4 + + # create the closed gate side + gate_side = terrain_images[4].copy() + gate_side_draw = ImageDraw.Draw(gate_side) + gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # darken the sides slightly, as with the fences + sidealpha = gate_side.split()[3] + gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) + gate_side.putalpha(sidealpha) + + # create the other sides + mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT)) + gate_side = transform_image_side(gate_side) + gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) + mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Create img to compose the fence gate + img = Image.new("RGBA", (24,24), bgcolor) + + if data & 0x4: + # opened + data = data & 0x3 + if data == 0: + composite.alpha_over(img, gate_side, (2,8), gate_side) + composite.alpha_over(img, gate_side, (13,3), gate_side) + elif data == 1: + composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) + composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) + elif data == 2: + composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) + composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) + elif data == 3: + composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) + else: + # closed + + # positions for pasting the fence sides, as with fences + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + if data == 0 or data == 2: + composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) + elif data == 1 or data == 3: + composite.alpha_over(img, gate_side, pos_top_left, gate_side) + composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) + + return img + +# mycelium +block(blockid=110, top_index=78, side_index=77) + +# lilypad +# TODO the data-block orientation relation is not clear +@material(blockid=111, data=range(4), transparent=True) +def lilypad(blockid, data, north): + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + t = terrain_images[76] # NOTE: using same data as stairs, no + # info in minepedia at the moment. + if data == 0: # pointing south + img = build_full_block(None, None, None, None, None, t) + elif data == 1: # pointing north + img = build_full_block(None, None, None, None, None, t.rotate(180)) + elif data == 2: # pointing west + img = build_full_block(None, None, None, None, None, t.rotate(270)) + elif data == 3: # pointing east + img = build_full_block(None, None, None, None, None, t.rotate(90)) + + return img + +# nether brick +block(blockid=112, top_index=224, side_index=224) + +# nether wart +@material(blockid=115, data=range(4), transparent=True) +def nether_wart(blockid, data): + if data == 0: # just come up + t = terrain_images[226] + elif data in (1, 2): + t = terrain_images[227] + else: # fully grown + t = terrain_images[228] + + # use the same technic as tall grass + img = build_billboard(t) + + return img + +# enchantment table +# TODO there's no book at the moment +@material(blockid=116, transparent=True) +def enchantment_table(blockid, data): + # no book at the moment + top = terrain_images[166] + side = terrain_images[182] + img = build_full_block((top, 4), None, None, side, side) + + return img + +# brewing stand +# TODO this is a place holder, is a 2d image pasted +@material(blockid=117, data=range(5), transparent=True) +def brewing_stand(blockid, data, north): + t = terrain_images[157] + img = build_billboard(t) + return img + +# cauldron +@material(blockid=118, data=range(4), transparent=True) +def cauldron(blockid, data): + side = terrain_images[154] + top = terrain_images[138] + bottom = terrain_images[139] + water = transform_image_top(_load_image("water.png")) + if data == 0: # empty + img = build_full_block(top, side, side, side, side) + if data == 1: # 1/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,8), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 2: # 2/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,4), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 3: # 3/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,0), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + return img + +# end portal +@material(blockid=119, transparent=True) +def end_portal(blockid, data): + img = Image.new("RGBA", (24,24), bgcolor) + # generate a black texure with white, blue and grey dots resembling stars + t = Image.new("RGBA", (16,16), (0,0,0,255)) + for color in [(155,155,155,255), (100,255,100,255), (255,255,255,255)]: + for i in range(6): + x = randint(0,15) + y = randint(0,15) + t.putpixel((x,y),color) + + t = transform_image_top(t) + composite.alpha_over(img, t, (0,0), t) + + return img + +# end portal frame +@material(blockid=120, data=range(5), transparent=True) +def end_porta_frame(blockid, data): + # The bottom 2 bits are oritation info but seems there is no + # graphical difference between orientations + top = terrain_images[158] + eye_t = terrain_images[174] + side = terrain_images[159] + img = build_full_block((top, 4), None, None, side, side) + if data & 0x4 == 0x4: # ender eye on it + # generate the eye + eye_t = terrain_images[174].copy() + eye_t_s = terrain_images[174].copy() + # cut out from the texture the side and the top of the eye + ImageDraw.Draw(eye_t).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(eye_t_s).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + # trnasform images and paste + eye = transform_image_top(eye_t) + eye_s = transform_image_side(eye_t_s) + eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, eye_s, (5,5), eye_s) + composite.alpha_over(img, eye_os, (9,5), eye_os) + composite.alpha_over(img, eye, (0,0), eye) + + return img + +# end stone +block(blockid=121, top_index=175) From 6dbac888d2e28583148411d592e6f160fb6aa60b Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 13:26:22 +0100 Subject: [PATCH 28/47] Fix holes in stairs. --- overviewer_core/textures.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 77a00d0..6c3ab5e 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1430,6 +1430,7 @@ def stairs(blockid, data, north): tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) tmp1.putalpha(sidealpha) + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole composite.alpha_over(img, tmp1, (6,3)) tmp2 = transform_image_top(half_block_l) composite.alpha_over(img, tmp2, (0,6)) @@ -1458,6 +1459,7 @@ def stairs(blockid, data, north): tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) tmp1.putalpha(sidealpha) + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole composite.alpha_over(img, tmp1, (6,3)) tmp2 = transform_image_top(half_block_d) composite.alpha_over(img, tmp2, (0,6)) From 25493c071f206c0a3c53e7f8fd9532ffb8512582 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 15:20:43 +0100 Subject: [PATCH 29/47] Add place holders for levers and buttons so the block is transparent. --- overviewer_core/textures.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 6c3ab5e..b984f69 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1862,6 +1862,10 @@ def wall_sign(blockid, data, north): # wall sign ## ## not rendered: levers ## +@material(blockid=69, data=range(16), transparent=True) +def levers(blockid, data, north): + # place holder, used to mae the block transparent + return None # wooden and stone pressure plates @material(blockid=[70, 72], data=[0,1], transparent=True) @@ -1900,6 +1904,10 @@ block(blockid=[73, 74], top_index=51) ## ## not rendered: buttons ## +@material(blockid=77, data=range(16), transparent=True) +def buttons(blockid, data, north): + # place holder, used to make the block transparent + return None # snow @material(blockid=78, data=range(8), transparent=True) From 29bc7fa0ba3b1d9c025a33198f153a6ad37d0ca8 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 15:41:55 +0100 Subject: [PATCH 30/47] Update iterate.c for nether brick fences and update the way glass panes and iron bars stick. --- overviewer_core/src/iterate.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 7d8dc23..c6c46a1 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -276,14 +276,15 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { return final_data; - /* portal, iron bars and glass panes - * Note: iron bars and glass panes "stick" to other blocks, but - * at the moment of writing this is not clear which ones stick and - * which others no, so for the moment stick only with himself. - * This is a TODO! - */ - } else if ((state->block == 90) || (state->block == 101) || - (state->block == 102)) { + } else if ((state->block == 101) || (state->block == 102)) { + /* iron bars and glass panes: + * they seem to stick to almost everything but air, but + * not sure yet! Still a TODO! */ + /* return check adjacent blocks with air, bit inverted */ + return check_adjacent_blocks(state, x, y, z, 0) ^ 0x0f; + + } else if ((state->block == 90) || (state->block == 113)) { + /* portal and nether brick fences */ return check_adjacent_blocks(state, x, y, z, state->block); } @@ -414,7 +415,8 @@ chunk_render(PyObject *self, PyObject *args) { (state.block == 20) || (state.block == 54) || (state.block == 55) || (state.block == 79) || (state.block == 85) || (state.block == 90) || - (state.block == 101) || (state.block == 102)) { + (state.block == 101) || (state.block == 102) || + (state.block == 113)) { ancilData = generate_pseudo_data(&state, ancilData); state.block_pdata = ancilData; } else { From e2dde5e6e0ddf2b98492b58602e5f27390869bd3 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 7 Nov 2011 16:21:03 +0100 Subject: [PATCH 31/47] Add lily pads to biome tinting. Fix pumpkin and melon stem. --- overviewer_core/src/rendermode-normal.c | 11 ++++++++-- overviewer_core/textures.py | 29 +++++++++++-------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/overviewer_core/src/rendermode-normal.c b/overviewer_core/src/rendermode-normal.c index e37a47a..24bf665 100644 --- a/overviewer_core/src/rendermode-normal.c +++ b/overviewer_core/src/rendermode-normal.c @@ -173,7 +173,9 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * * get constant brown color (see textures.py) */ (((state->block == 104) || (state->block == 105)) && (state->block_data != 7)) || /* vines */ - state->block == 106) + state->block == 106 || + /* lily pads */ + state->block == 111) { /* do the biome stuff! */ PyObject *facemask = mask; @@ -240,6 +242,10 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * /* vines */ color = PySequence_GetItem(self->grasscolor, index); break; + case 111: + /* lily padas */ + color = PySequence_GetItem(self->grasscolor, index); + break; default: break; }; @@ -270,7 +276,8 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * facemask = NULL; } - if (state->block == 18 || state->block == 106) /* leaves and vines */ + if (state->block == 18 || state->block == 106 || state->block == 111) + /* leaves, vines and lyli pads */ { r = 37; g = 118; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index b984f69..918940a 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -2509,23 +2509,20 @@ block(blockid=103, top_index=137, side_index=136, solid=True) def stem(blockid, data, north): # the ancildata value indicates how much of the texture # is shown. - if data & 7 == 0: - # not fully grown stem or no pumpkin/melon touching it, - # straight up stem - t = terrain_images[111].copy() - img = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) - img = build_sprite(t) - if data & 7 == 7: - # fully grown stem gets brown color! - # there is a conditional in rendermode-normal to not - # tint the data value 7 - img = tintTexture(img, (211,169,116)) - return img + + # not fully grown stem or no pumpkin/melon touching it, + # straight up stem + t = terrain_images[111].copy() + img = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) + img = build_sprite(t) + if data & 7 == 7: + # fully grown stem gets brown color! + # there is a conditional in rendermode-normal.c to not + # tint the data value 7 + img = tintTexture(img, (211,169,116)) + return img - else: # fully grown, and a pumpking/melon touching it, - # corner stem - return None # vines # TODO multiple sides of a block can contain vines! At the moment From 0d656e274f2174d929545218ba12d41c6cb1b929 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 7 Nov 2011 16:56:59 -0500 Subject: [PATCH 32/47] added Jason Scheirer to CONTRIBUTORS.rst --- CONTRIBUTORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 21afa74..a1142cd 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -57,6 +57,7 @@ feature. * Ryan McCue * Morlok8k * Ryan Rector + * Jason Scheirer * Gregory Short * Sam Steele * timwolla From bca4b9b011afdda2d61b47816e01c6d1b671ac6f Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 7 Nov 2011 16:48:11 -0500 Subject: [PATCH 33/47] added support for loading textures directly out of texture packs --- docs/options.rst | 8 +++-- docs/running.rst | 10 ++++-- overviewer_core/textures.py | 64 ++++++++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/docs/options.rst b/docs/options.rst index a8f00a3..9295405 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -305,9 +305,11 @@ Less Useful Options .. cmdoption:: --textures-path - Use this option to specify an alternate terrain.png to use for textures when - rendering a world. ``path`` specifies the **containing directory** of - terrain.png. + Use this option to specify an alternate terrain.png (and other + textures) to when rendering a world. ``path`` specifies the + **containing directory** of terrain.png. Alternately, ``path`` can + specify a zip file containing the textures, such as a texture + pack. The Overviewer will look for terrain.png in the following places in this order: path specified by this option, the program's directory, the diff --git a/docs/running.rst b/docs/running.rst index adc93c7..8429ff3 100644 --- a/docs/running.rst +++ b/docs/running.rst @@ -90,9 +90,13 @@ You have several options: file in the same directory as overviewer.py or overviewer.exe. For installations, you will need to specify the path... see the next bullet. -* You can put a terrain.png file anywhere you want and point to its location - with the :option:`--textures-path` option. This should point to the directory containing - the terrain.png, not to the file itself. +* You can put a terrain.png file anywhere you want and point to its + location with the :option:`--textures-path` option. This should + point to the directory containing the terrain.png, not to the file + itself. + +* Alternately, you can download any texture pack ZIP you like and + point to this directly with :option:`--textures-path`. Note: the :option:`--check-terrain` option is useful for debugging terrain.png issues. For example:: diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index c404fc8..cb75d26 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -34,6 +34,7 @@ def _find_file(filename, mode="rb", verbose=False): This searches the following locations in this order: * the textures_path given in the config file (if present) + this can be either a directory or a zip file (texture pack) * The program dir (same dir as overviewer.py) * The overviewer_core/data/textures dir * On Darwin, in /Applications/Minecraft @@ -42,34 +43,61 @@ def _find_file(filename, mode="rb", verbose=False): * On Windows, at %APPDATA%/.minecraft/bin/minecraft.jar * On Darwin, at $HOME/Library/Application Support/minecraft/bin/minecraft.jar * at $HOME/.minecraft/bin/minecraft.jar + + In all of these, files are searched for in '.', 'misc/', and 'environment/'. """ + # a list of subdirectories to search for a given file, + # after the obvious '.' + search_dirs = ['misc', 'environment'] + search_zip_paths = [filename,] + [d + '/' + filename for d in search_dirs] + def search_dir(base): + """Search the given base dir for filename, in search_dirs.""" + for path in [os.path.join(base, d, filename) for d in ['',] + search_dirs]: + if os.path.isfile(path): + return path + return None + if _find_file_local_path: - path = os.path.join(_find_file_local_path, filename) - if os.path.exists(path): - if verbose: logging.info("Found %s in '%s'", filename, path) - return open(path, mode) + if os.path.isdir(_find_file_local_path): + path = search_dir(_find_file_local_path) + if path: + if verbose: logging.info("Found %s in '%s'", filename, path) + return open(path, mode) + elif os.path.isfile(_find_file_local_path): + try: + pack = zipfile.ZipFile(_find_file_local_path) + for packfilename in search_zip_paths: + try: + pack.getinfo(packfilename) + if verbose: logging.info("Found %s in '%s'", packfilename, _find_file_local_path) + return pack.open(packfilename) + except (KeyError, IOError): + pass + except (zipfile.BadZipfile, IOError): + pass programdir = util.get_program_path() - path = os.path.join(programdir, filename) - if os.path.exists(path): + path = search_dir(programdir) + if path: if verbose: logging.info("Found %s in '%s'", filename, path) return open(path, mode) - path = os.path.join(programdir, "overviewer_core", "data", "textures", filename) - if os.path.exists(path): + path = search_dir(os.path.join(programdir, "overviewer_core", "data", "textures")) + if path: + if verbose: logging.info("Found %s in '%s'", filename, path) return open(path, mode) elif hasattr(sys, "frozen") or imp.is_frozen("__main__"): # windows special case, when the package dir doesn't exist - path = os.path.join(programdir, "textures", filename) - if os.path.exists(path): + path = search_dir(os.path.join(programdir, "textures")) + if path: if verbose: logging.info("Found %s in '%s'", filename, path) return open(path, mode) if sys.platform == "darwin": - path = os.path.join("/Applications/Minecraft", filename) - if os.path.exists(path): + path = search_dir("/Applications/Minecraft") + if path: if verbose: logging.info("Found %s in '%s'", filename, path) return open(path, mode) @@ -91,18 +119,18 @@ def _find_file(filename, mode="rb", verbose=False): for jarpath in jarpaths: if os.path.isfile(jarpath): jar = zipfile.ZipFile(jarpath) - for jarfilename in [filename, 'misc/' + filename, 'environment/' + filename]: + for jarfilename in search_zip_paths: try: + jar.getinfo(jarfilename) if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) return jar.open(jarfilename) except (KeyError, IOError), e: pass elif os.path.isdir(jarpath): - for jarfilename in [filename, 'misc/' + filename, 'environment/' + filename]: - ondiskfilename = os.path.join(jarpath, jarfilename) - if os.path.isfile(ondiskfilename): - if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) - return open(ondiskfilename, 'rb') + path = search_dir(jarpath) + if path: + if verbose: logging.info("Found %s in '%s'", filename, path) + return open(path, 'rb') raise IOError("Could not find the file `{0}'. You can either place it in the same place as overviewer.py, use --textures-path, or install the Minecraft client.".format(filename)) From c0d37004cc631aa444634035a1b390c9c8673556 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 7 Nov 2011 16:55:36 -0500 Subject: [PATCH 34/47] now using MCPatcher animated water/lava textures, if present --- overviewer_core/textures.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index cb75d26..3bd5f0c 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -529,13 +529,23 @@ def load_water(): Block 9, standing water, is given a block with only the top face showing. Block 8, flowing water, is given a full 3 sided cube.""" - watertexture = _load_image("water.png") + try: + # try the MCPatcher water first, in case it's present + watertexture = _load_image("custom_water_still.png") + watertexture = watertexture.crop((0, 0, watertexture.size[0], watertexture.size[0])) + except IOError: + watertexture = _load_image("water.png") w1 = _build_block(watertexture, None) blockmap[9] = generate_texture_tuple(w1,9) w2 = _build_block(watertexture, watertexture) blockmap[8] = generate_texture_tuple(w2,8) - - lavatexture = _load_image("lava.png") + + try: + # try the MCPatcher lava first, in case it's present + lavatexture = _load_image("custom_lava_still.png") + lavatexture = lavatexture.crop((0, 0, lavatexture.size[0], lavatexture.size[0])) + except IOError: + lavatexture = _load_image("lava.png") lavablock = _build_block(lavatexture, lavatexture) blockmap[10] = generate_texture_tuple(lavablock,10) blockmap[11] = blockmap[10] @@ -599,7 +609,11 @@ def generate_special_texture(blockID, data): if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice # water,glass and ice share the way to be rendered if blockID == 9: - texture = _load_image("water.png") + try: + texture = _load_image("custom_water_still.png") + texture = texture.crop((0, 0, texture.size[0], texture.size[0])) + except IOError: + texture = _load_image("water.png") elif blockID == 20: texture = terrain_images[49] else: From d18df1e694a7872729271e2507bd23ea4c05bd9b Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Mon, 7 Nov 2011 21:49:00 -0500 Subject: [PATCH 35/47] Don't print scary bug warning on ctrl-c --- overviewer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/overviewer.py b/overviewer.py index 52a5f34..acf48e3 100755 --- a/overviewer.py +++ b/overviewer.py @@ -527,6 +527,9 @@ if __name__ == "__main__": try: main() except Exception, e: + if e.message == "Exiting": + logging.info("Exiting...") + sys.exit(0) logging.exception("""An error has occurred. This may be a bug. Please let us know! See http://docs.overviewer.org/en/latest/index.html#help From a822cd1d3cc0f5231f58c99cc06c9f598be4b3fa Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 10 Nov 2011 00:03:30 -0500 Subject: [PATCH 36/47] added an index of command line options with brief descriptions Also added a warning to regionlist that it may be broken --- docs/options.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/options.rst b/docs/options.rst index 7d9a078..d265517 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -24,6 +24,41 @@ files. Command line options ==================== +All Options: + +======================================== ================================== +:option:`--advanced-help` Print out help on all options +:option:`--bg-color` Change the background of the map +:option:`--changelist` Outputs a list of changed tiles +:option:`--changelist-format` Sets the format of the change list +:option:`--check-terrain` Output information on the textures +:option:`--display-config` Displays the configuration +:option:`--forcerender` Forces every tile to render +:option:`-h`, :option:`--help <-h>` Prints help on the basic options +:option:`--imgformat` Choose from png or jpg output +:option:`--imgquality` Change jpg output quality +:option:`--list-rendermodes` List the installed rendermodes +:option:`--no-signs` Do not place signs on the map +:option:`--north-direction` Choose which direction is north +:option:`--optimize-img` Run pngcrush on outputted tiles +:option:`-p`, :option:`--processes <-p>` Choose the number of worker processes +:option:`-q`, :option:`--quiet <-q>` Print less output +:option:`--regionlist` Only render specified areas of a map +:option:`--rendermodes` Choose which rendermode(s) to use +:option:`--settings` Specify an external settings file +:option:`--skip-js` Do not output generated javascript files +:option:`--stochastic-render` Re-render parts of the map randomly +:option:`--textures-path` Specify custom textures to use +:option:`-v`, :option:`--verbose <-v>` Print more output +:option:`-V`, :option:`--version <-V>` Print out the version +:option:`--web-assets-path` Specify alternate web assets +:option:`-z`, :option:`--zoom <-z>` Do not touch +======================================== ================================== + + +Help Options +------------ + .. cmdoption:: -h, --help Shows the list of options and exits @@ -280,6 +315,11 @@ Less Useful Options a certain date. Or perhaps you can incrementally update your map by passing in a subset of regions each time. It's up to you! + .. warning:: + + This option may currently be broken. Use at your own risk! Patches + welcome! + **Settings file:** Option name: ``regionlist`` From 74e0ef2ad63a1c4e5622e6829720d6da85a55bcf Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 10 Nov 2011 09:42:21 -0500 Subject: [PATCH 37/47] brought solid_blocks (etc.) sets back in line with old values --- overviewer_core/textures.py | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 918940a..2bbbbb4 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -715,14 +715,14 @@ def saplings(blockid, data): # bedrock block(blockid=7, top_index=17) -@material(blockid=8, data=range(16), fluid=True, transparent=True) +@material(blockid=8, data=range(16), fluid=True, transparent=True, nospawn=True) def water(blockid, data): watertex = _load_image("water.png") return build_block(watertex, watertex) # other water, glass, and ice (no inner surfaces) # uses pseudo-ancildata found in iterate.c -@material(blockid=[9, 20, 79], data=range(32), fluid=(9,), transparent=True, nospawn=True) +@material(blockid=[9, 20, 79], data=range(32), fluid=(9,), transparent=True, nospawn=True, solid=(79, 20)) def no_inner_surfaces(blockid, data): if blockid == 9: texture = _load_image("water.png") @@ -763,7 +763,7 @@ def no_inner_surfaces(blockid, data): img = build_full_block(top,None,None,side3,side4) return img -@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False) +@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False, nospawn=True) def lava(blockid, data): lavatex = _load_image("lava.png") return build_block(lavatex, lavatex) @@ -846,7 +846,7 @@ block(blockid=24, top_index=176, side_index=192) # note block block(blockid=25, top_index=74) -@material(blockid=26, data=range(12), transparent=True) +@material(blockid=26, data=range(12), transparent=True, nospawn=True) def bed(blockid, data, north): # first get north rotation done # Masked to not clobber block head/foot info @@ -1000,7 +1000,7 @@ def rails(blockid, data, north): return img # sticky and normal piston body -@material(blockid=[29, 33], data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, solid=True) +@material(blockid=[29, 33], data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, solid=True, nospawn=True) def piston(blockid, data, north): # first, north rotation # Masked to not clobber block head/foot info @@ -1076,7 +1076,7 @@ def piston(blockid, data, north): return img # sticky and normal piston shaft -@material(blockid=34, data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True) +@material(blockid=34, data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, nospawn=True) def piston_extension(blockid, data, north): # first, north rotation # Masked to not clobber block head/foot info @@ -1163,7 +1163,7 @@ def piston_extension(blockid, data, north): return img # cobweb -sprite(blockid=30, index=11) +sprite(blockid=30, index=11, nospawn=True) @material(blockid=31, data=range(3), transparent=True) def tall_grass(blockid, data): @@ -1230,7 +1230,7 @@ block(blockid=41, top_index=23) block(blockid=42, top_index=22) # double slabs and slabs -@material(blockid=[43, 44], data=range(6), transparent=(44,), solid=(43,)) +@material(blockid=[43, 44], data=range(6), transparent=(44,), solid=True) def slabs(blockid, data): if data == 0: # stone slab top = terrain_images[6] @@ -1277,7 +1277,7 @@ def slabs(blockid, data): # brick block block(blockid=45, top_index=7) # TNT -block(blockid=46, top_index=9, side_index=8) +block(blockid=46, top_index=9, side_index=8, nospawn=True) # bookshelf block(blockid=47, top_index=4, side_index=35) # moss stone @@ -1373,10 +1373,10 @@ def fire(blockid, data): return img # monster spawner -block(blockid=52, top_index=34) +block(blockid=52, top_index=34, transparent=True) # wooden, cobblestone, red brick, stone brick and netherbrick stairs. -@material(blockid=[53,67,108,109,114], data=range(4), transparent=True) +@material(blockid=[53,67,108,109,114], data=range(4), transparent=True, solid=True, nospawn=True) def stairs(blockid, data, north): # first, north rotations @@ -1471,7 +1471,7 @@ def stairs(blockid, data, north): # normal and locked chest (locked was the one used in april fools' day) # uses pseudo-ancildata found in iterate.c -@material(blockid=[54,95], data=range(12), transparent=True) +@material(blockid=[54,95], data=range(12), solid=True) def chests(blockid, data): # First two bits of the pseudo data store if it's a single chest # or it's a double chest, first half or second half (left to right). @@ -1613,7 +1613,7 @@ def crafting_table(blockid, data): return img # crops -@material(blockid=59, data=range(8), transparent=True) +@material(blockid=59, data=range(8), transparent=True, nospawn=True) def crops(blockid, data): raw_crop = terrain_images[88+data] crop1 = transform_image_top(raw_crop) @@ -1758,7 +1758,7 @@ def door(blockid, data, north): return img # ladder -@material(blockd=65, data=[2, 3, 4, 5], transparent = True) +@material(blockd=65, data=[2, 3, 4, 5], transparent=True) def ladder(blockid, data, north): # first north rotations @@ -1910,7 +1910,7 @@ def buttons(blockid, data, north): return None # snow -@material(blockid=78, data=range(8), transparent=True) +@material(blockid=78, data=range(8), transparent=True, solid=True) def snow(blockid, data): # still not rendered correctly: data other than 0 @@ -1977,7 +1977,7 @@ def jukebox(blockid, data): # nether and normal fences # uses pseudo-ancildata found in iterate.c -@material(blockid=[85, 113], data=range(16), transparent=True) +@material(blockid=[85, 113], data=range(16), transparent=True, nospawn=True) def fence(blockid, data): # no need for north rotations, it uses pseudo data. # create needed images for Big stick fence @@ -2138,7 +2138,7 @@ def portal(blockid, data): # cake! # TODO is rendered un-bitten -@material(blockid=92, data=range(6), transparent=True) +@material(blockid=92, data=range(6), transparent=True, nospawn=True) def cake(blockid, data): # choose textures for cake @@ -2167,7 +2167,7 @@ def cake(blockid, data): return img # redstone repeaters ON and OFF -@material(blockid=[93,94], data=range(16), transparent=True) +@material(blockid=[93,94], data=range(16), transparent=True, nospawn=True) def repeater(blockid, data, north): # north rotation # Masked to not clobber delay info @@ -2313,7 +2313,7 @@ def repeater(blockid, data, north): # trapdoor # TODO the trapdoor is looks like a sprite when opened, that's not good -@material(blockid=96, data=range(8), transparent=True) +@material(blockid=96, data=range(8), transparent=True, nospawn=True) def trapdoor(blockid, data, north): # north rotation @@ -2458,7 +2458,7 @@ def huge_mushroom(blockid, data, north): # iron bars and glass pane # TODO glass pane is not a sprite, it has a texture for the side, # at the moment is not used -@material(blockid=[101,102], data=range(16), transparent=True) +@material(blockid=[101,102], data=range(16), transparent=True, nospawn=True) def panes(blockid, data): # no north rotation, uses pseudo data if blockid == 101: @@ -2570,7 +2570,7 @@ def vines(blockid, data, north): return img # fence gates -@material(blockid=107, data=range(8), transparent=True) +@material(blockid=107, data=range(8), transparent=True, nospawn=True) def fence_gate(blockid, data, north): # north rotation From dc0110ee735a27651ab1d3f5ca33bc7bc5e35ad0 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 10 Nov 2011 09:51:25 -0500 Subject: [PATCH 38/47] unknown blocks are no longer assumed to be transparent --- overviewer_core/src/iterate.c | 8 ++++++++ overviewer_core/textures.py | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index c6c46a1..47e175b 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -20,6 +20,7 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; +static PyObject *known_blocks = NULL; static PyObject *transparent_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { @@ -45,6 +46,9 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { blockmap = PyObject_GetAttrString(textures, "blockmap"); if (!blockmap) return NULL; + known_blocks = PyObject_GetAttrString(textures, "known_blocks"); + if (!known_blocks) + return NULL; transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; @@ -56,6 +60,10 @@ int is_transparent(unsigned char b) { PyObject *block = PyInt_FromLong(b); int ret = PySequence_Contains(transparent_blocks, block); + if (!ret) + { + ret = !(PySequence_Contains(known_blocks, block)); + } Py_DECREF(block); return ret; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 2bbbbb4..39d4739 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -546,7 +546,8 @@ blockmap_generators = {} blockmap = {} biome_grass_texture = None -transparent_blocks = set([0,]) +known_blocks = set() +transparent_blocks = set() solid_blocks = set() fluid_blocks = set() nospawn_blocks = set() @@ -579,6 +580,7 @@ def material(blockid=[], data=[0], **kwargs): for block in blockid: # set the property sets appropriately + known_blocks.update([block]) for prop in properties: try: if block in kwargs.get(prop, []): From afd3ad639bc71fc4261cace5e6d56a92c6f881e2 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 11 Nov 2011 15:34:45 -0500 Subject: [PATCH 39/47] textures are now stored in a continuous array in memory, instead of a dict --- overviewer_core/src/iterate.c | 42 ++++++++++++++++------------------- overviewer_core/textures.py | 26 ++++++++++++++-------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 47e175b..4cc853c 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -22,9 +22,13 @@ static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; static PyObject *known_blocks = NULL; static PyObject *transparent_blocks = NULL; +static unsigned int max_blockid = 0; +static unsigned int max_data = 0; PyObject *init_chunk_render(PyObject *self, PyObject *args) { + PyObject *tmp = NULL; + /* this function only needs to be called once, anything more should be * ignored */ if (blockmap) { @@ -53,6 +57,15 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { if (!transparent_blocks) return NULL; + tmp = PyObject_GetAttrString(textures, "max_blockid"); + if (!tmp) + return NULL; + max_blockid = PyInt_AsLong(tmp); + tmp = PyObject_GetAttrString(textures, "max_data"); + if (!tmp) + return NULL; + max_data = PyInt_AsLong(tmp); + Py_RETURN_NONE; } @@ -373,15 +386,13 @@ chunk_render(PyObject *self, PyObject *args) { for (state.x = 15; state.x > -1; state.x--) { for (state.y = 0; state.y < 16; state.y++) { - PyObject *blockid = NULL; - + /* set up the render coordinates */ state.imgx = xoff + state.x*12 + state.y*12; /* 128*12 -- offset for z direction, 15*6 -- offset for x */ state.imgy = yoff - state.x*6 + state.y*6 + 128*12 + 15*6; for (state.z = 0; state.z < 128; state.z++) { - PyObject *tmp; unsigned char ancilData; state.imgy -= 12; @@ -399,14 +410,7 @@ chunk_render(PyObject *self, PyObject *args) { if ((state.imgy >= imgsize1 + 24) || (state.imgy <= -24)) { continue; } - - /* decref'd on replacement *and* at the end of the z for block */ - if (blockid) { - Py_DECREF(blockid); - } - blockid = PyInt_FromLong(state.block); - /* check for occlusion */ if (render_mode_occluded(rendermode, state.x, state.y, state.z)) { continue; @@ -431,15 +435,12 @@ chunk_render(PyObject *self, PyObject *args) { state.block_pdata = 0; } - tmp = PyTuple_New(2); + /* make sure our block info is in-bounds */ + if (state.block >= max_blockid || ancilData >= max_data) + continue; - Py_INCREF(blockid); /* because SetItem steals */ - PyTuple_SetItem(tmp, 0, blockid); - PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); - - /* this is a borrowed reference. no need to decref */ - t = PyDict_GetItem(blockmap, tmp); - Py_DECREF(tmp); + /* get the texture */ + t = PyList_GET_ITEM(blockmap, max_data * state.block + ancilData); /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) @@ -470,11 +471,6 @@ chunk_render(PyObject *self, PyObject *args) { } } } - - if (blockid) { - Py_DECREF(blockid); - blockid = NULL; - } } } diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 39d4739..ae9d46f 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -423,7 +423,7 @@ def tintTexture(im, c): def generate_texture_tuple(img): """ This takes an image and returns the needed tuple for the - blockmap dictionary.""" + blockmap array.""" if img is None: return None return (img, generate_opaque_mask(img)) @@ -543,10 +543,14 @@ def loadLightColor(): # placeholders that are generated in generate() texture_dimensions = None blockmap_generators = {} -blockmap = {} +blockmap = [] biome_grass_texture = None known_blocks = set() +used_datas = set() +max_blockid = 0 +max_data = 0 + transparent_blocks = set() solid_blocks = set() fluid_blocks = set() @@ -578,6 +582,7 @@ def material(blockid=[], data=[0], **kwargs): except TypeError: return func(blockid, data) + used_datas.update(data) for block in blockid: # set the property sets appropriately known_blocks.update([block]) @@ -658,23 +663,26 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower # generate the blocks global blockmap, blockmap_generators - blockmap = {} + global max_blockid, max_data + max_blockid = max(known_blocks) + 1 + max_data = max(used_datas) + 1 + blockmap = [None] * max_blockid * max_data + for blockid, data in blockmap_generators: texgen = blockmap_generators[(blockid, data)] tex = texgen(blockid, data, north_direction) - blockmap[(blockid, data)] = generate_texture_tuple(tex) + blockmap[blockid * max_data + data] = generate_texture_tuple(tex) if texture_size != 24: # rescale biome textures. biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS) # rescale the special block images - for blockid, data in iter(blockmap): - block = blockmap[(blockid,data)] - if block != None: - block = block[0] + for i, tex in enumerate(blockmap): + if tex != None: + block = tex[0] scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - blockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + blockmap[i] = generate_texture_tuple(scaled_block) ## ## and finally: actual texture definitions From 944edf4ef8beabf5f7d82a1408e1cd6c41958e52 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 11 Nov 2011 16:07:10 -0500 Subject: [PATCH 40/47] changed material property lookups to use a bit table, not python sets --- overviewer_core/src/iterate.c | 66 ++++++++++++++++-------- overviewer_core/src/overviewer.h | 26 +++++++++- overviewer_core/src/rendermode-overlay.c | 21 ++------ overviewer_core/src/rendermode-spawn.c | 7 +-- overviewer_core/src/rendermodes.h | 4 -- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 4cc853c..0fc1109 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -20,14 +20,21 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; + +unsigned int max_blockid = 0; +unsigned int max_data = 0; +unsigned char *block_properties = NULL; + static PyObject *known_blocks = NULL; static PyObject *transparent_blocks = NULL; -static unsigned int max_blockid = 0; -static unsigned int max_data = 0; +static PyObject *solid_blocks = NULL; +static PyObject *fluid_blocks = NULL; +static PyObject *nospawn_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { PyObject *tmp = NULL; + unsigned int i; /* this function only needs to be called once, anything more should be * ignored */ @@ -50,12 +57,6 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { blockmap = PyObject_GetAttrString(textures, "blockmap"); if (!blockmap) return NULL; - known_blocks = PyObject_GetAttrString(textures, "known_blocks"); - if (!known_blocks) - return NULL; - transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); - if (!transparent_blocks) - return NULL; tmp = PyObject_GetAttrString(textures, "max_blockid"); if (!tmp) @@ -66,23 +67,44 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { return NULL; max_data = PyInt_AsLong(tmp); + /* assemble the property table */ + known_blocks = PyObject_GetAttrString(textures, "known_blocks"); + if (!known_blocks) + return NULL; + transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); + if (!transparent_blocks) + return NULL; + solid_blocks = PyObject_GetAttrString(textures, "solid_blocks"); + if (!solid_blocks) + return NULL; + fluid_blocks = PyObject_GetAttrString(textures, "fluid_blocks"); + if (!fluid_blocks) + return NULL; + nospawn_blocks = PyObject_GetAttrString(textures, "nospawn_blocks"); + if (!nospawn_blocks) + return NULL; + + block_properties = calloc(max_blockid, sizeof(unsigned char)); + for (i = 0; i < max_blockid; i++) { + PyObject *block = PyInt_FromLong(i); + + if (PySequence_Contains(known_blocks, block)) + block_properties[i] |= 1 << KNOWN; + if (PySequence_Contains(transparent_blocks, block)) + block_properties[i] |= 1 << TRANSPARENT; + if (PySequence_Contains(solid_blocks, block)) + block_properties[i] |= 1 << SOLID; + if (PySequence_Contains(fluid_blocks, block)) + block_properties[i] |= 1 << FLUID; + if (PySequence_Contains(nospawn_blocks, block)) + block_properties[i] |= 1 << NOSPAWN; + + Py_DECREF(block); + } + Py_RETURN_NONE; } -int -is_transparent(unsigned char b) { - PyObject *block = PyInt_FromLong(b); - int ret = PySequence_Contains(transparent_blocks, block); - if (!ret) - { - ret = !(PySequence_Contains(known_blocks, block)); - } - Py_DECREF(block); - return ret; - -} - - unsigned char check_adjacent_blocks(RenderState *state, int x,int y,int z, unsigned char blockid) { /* diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 934dae8..780b298 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -86,8 +86,32 @@ typedef struct { PyObject *right_blocks; } RenderState; PyObject *init_chunk_render(PyObject *self, PyObject *args); -int is_transparent(unsigned char b); PyObject *chunk_render(PyObject *self, PyObject *args); +typedef enum +{ + KNOWN, + TRANSPARENT, + SOLID, + FLUID, + NOSPAWN, +} BlockProperty; +/* globals set in init_chunk_render, here because they're used + in block_has_property */ +extern unsigned int max_blockid; +extern unsigned int max_data; +extern unsigned char *block_properties; +static inline int +block_has_property(unsigned char b, BlockProperty prop) { + if (b >= max_blockid || !(block_properties[b] & (1 << KNOWN))) { + /* block is unknown, return defaults */ + if (prop == TRANSPARENT) + return 1; + return 0; + } + + return block_properties[b] & (1 << prop); +} +#define is_transparent(b) block_has_property((b), TRANSPARENT) /* pull in the rendermode info */ #include "rendermodes.h" diff --git a/overviewer_core/src/rendermode-overlay.c b/overviewer_core/src/rendermode-overlay.c index 72e58ff..47c44fc 100644 --- a/overviewer_core/src/rendermode-overlay.c +++ b/overviewer_core/src/rendermode-overlay.c @@ -37,10 +37,6 @@ rendermode_overlay_start(void *data, RenderState *state, PyObject *options) { Py_DECREF(facemasks_py); self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - - self->solid_blocks = PyObject_GetAttrString(state->textures, "solid_blocks"); - self->fluid_blocks = PyObject_GetAttrString(state->textures, "fluid_blocks"); - self->get_color = get_color; return 0; @@ -52,8 +48,6 @@ rendermode_overlay_finish(void *data, RenderState *state) { Py_DECREF(self->facemask_top); Py_DECREF(self->white_color); - Py_DECREF(self->solid_blocks); - Py_DECREF(self->fluid_blocks); } static int @@ -81,8 +75,7 @@ static void rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeOverlay *self = (RenderModeOverlay *)data; unsigned char r, g, b, a; - PyObject *top_block_py, *block_py; - + // exactly analogous to edge-line code for these special blocks int increment=0; if (state->block == 44) // half-step @@ -101,27 +94,19 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject } /* check to be sure this block is solid/fluid */ - top_block_py = PyInt_FromLong(top_block); - if (PySequence_Contains(self->solid_blocks, top_block_py) || - PySequence_Contains(self->fluid_blocks, top_block_py)) { + if (block_has_property(top_block, SOLID) || block_has_property(top_block, FLUID)) { /* top block is fluid or solid, skip drawing */ - Py_DECREF(top_block_py); return; } - Py_DECREF(top_block_py); } /* check to be sure this block is solid/fluid */ - block_py = PyInt_FromLong(state->block); - if (!PySequence_Contains(self->solid_blocks, block_py) && - !PySequence_Contains(self->fluid_blocks, block_py)) { + if (!block_has_property(state->block, SOLID) && !block_has_property(state->block, FLUID)) { /* not fluid or solid, skip drawing the overlay */ - Py_DECREF(block_py); return; } - Py_DECREF(block_py); /* get our color info */ self->get_color(data, state, &r, &g, &b, &a); diff --git a/overviewer_core/src/rendermode-spawn.c b/overviewer_core/src/rendermode-spawn.c index c303f23..275f33c 100644 --- a/overviewer_core/src/rendermode-spawn.c +++ b/overviewer_core/src/rendermode-spawn.c @@ -35,13 +35,10 @@ static void get_color(void *data, RenderState *state, /* default to no overlay, until told otherwise */ *a = 0; - block_py = PyInt_FromLong(state->block); - if (PySequence_Contains(self->nospawn_blocks, block_py)) { + if (block_has_property(state->block, NOSPAWN)) { /* nothing can spawn on this */ - Py_DECREF(block_py); return; } - Py_DECREF(block_py); blocklight = getArrayByte3D(self->blocklight, x, y, MIN(127, z_light)); @@ -72,7 +69,6 @@ rendermode_spawn_start(void *data, RenderState *state, PyObject *options) { /* now do custom initializations */ self = (RenderModeSpawn *)data; - self->nospawn_blocks = PyObject_GetAttrString(state->textures, "nospawn_blocks"); self->blocklight = PyObject_GetAttrString(state->self, "blocklight"); self->skylight = PyObject_GetAttrString(state->self, "skylight"); @@ -87,7 +83,6 @@ rendermode_spawn_finish(void *data, RenderState *state) { /* first free all *our* stuff */ RenderModeSpawn* self = (RenderModeSpawn *)data; - Py_DECREF(self->nospawn_blocks); Py_DECREF(self->blocklight); Py_DECREF(self->skylight); diff --git a/overviewer_core/src/rendermodes.h b/overviewer_core/src/rendermodes.h index 1a145cf..67bd598 100644 --- a/overviewer_core/src/rendermodes.h +++ b/overviewer_core/src/rendermodes.h @@ -143,8 +143,6 @@ extern RenderModeInterface rendermode_normal; typedef struct { /* top facemask and white color image, for drawing overlays */ PyObject *facemask_top, *white_color; - /* only show overlay on top of solid or fluid blocks */ - PyObject *solid_blocks, *fluid_blocks; /* can be overridden in derived classes to control overlay alpha and color last four vars are r, g, b, a out */ @@ -195,8 +193,6 @@ typedef struct { /* inherits from overlay */ RenderModeOverlay parent; - /* used to figure out which blocks are spawnable */ - PyObject *nospawn_blocks; PyObject *skylight, *blocklight; } RenderModeSpawn; extern RenderModeInterface rendermode_spawn; From 0a5c90ba98784192071fddbd82ec3ed7cc39bba2 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sun, 13 Nov 2011 11:23:49 -0500 Subject: [PATCH 41/47] delete a tile if the innertile routine can't open it This makes corrupt tiles self-correcting on subsequent runs. Before, the warning is printed but the tile remains. Worse, the warning won't be printed again. --- overviewer_core/quadtree.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/overviewer_core/quadtree.py b/overviewer_core/quadtree.py index 8624ba4..cd77fb8 100644 --- a/overviewer_core/quadtree.py +++ b/overviewer_core/quadtree.py @@ -338,7 +338,12 @@ class QuadtreeGen(object): quad = Image.open(path[1]).resize((192,192), Image.ANTIALIAS) img.paste(quad, path[0]) except Exception, e: - logging.warning("Couldn't open %s. It may be corrupt, you may need to delete it. %s", path[1], e) + logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'", path[1], e) + logging.warning("I'm going to try and delete it. You will need to run the render again") + try: + os.unlink(path[1]) + except Exception, e: + logging.warning("I couldn't delete it. You will need to delete it yourself. Error was '%s'", e) # Save it if self.imgformat == 'jpg': From 142166ef8e2bd725d8630a4a2a149c3bc56e1383 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sun, 13 Nov 2011 11:36:27 -0500 Subject: [PATCH 42/47] Failing to delete a corrupt tile should really be an error in the log. --- overviewer_core/quadtree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/quadtree.py b/overviewer_core/quadtree.py index cd77fb8..a8da76d 100644 --- a/overviewer_core/quadtree.py +++ b/overviewer_core/quadtree.py @@ -343,7 +343,7 @@ class QuadtreeGen(object): try: os.unlink(path[1]) except Exception, e: - logging.warning("I couldn't delete it. You will need to delete it yourself. Error was '%s'", e) + logging.error("I couldn't delete it. You will need to delete it yourself. Error was '%s'", e) # Save it if self.imgformat == 'jpg': From 6f0b8f74ce1b5f02ba18ff44e953625070380aa3 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 15 Nov 2011 09:32:24 +0100 Subject: [PATCH 43/47] Added buttons. --- overviewer_core/textures.py | 70 ++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index ae9d46f..5508f06 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1911,13 +1911,73 @@ def pressure_plate(blockid, data): # normal and glowing redstone ore block(blockid=[73, 74], top_index=51) -## -## not rendered: buttons -## @material(blockid=77, data=range(16), transparent=True) def buttons(blockid, data, north): - # place holder, used to make the block transparent - return None + + # 0x8 is set if the button is pressed mask this info and render + # it as unpressed + data = data & 0x7 + + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + elif north == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 + + t = terrain_images[1].copy() + + # generate the texture for the button + ImageDraw.Draw(t).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + img = Image.new("RGBA", (24,24), bgcolor) + + button = transform_image_side(t) + + if data == 1: # facing SOUTH + # buttons can't be placed in transparent blocks, so this + # direction can't be seen + return None + + elif data == 2: # facing NORTH + # paste it twice with different brightness to make a 3D effect + composite.alpha_over(img, button, (12,-1), button) + + alpha = button.split()[3] + button = ImageEnhance.Brightness(button).enhance(0.9) + button.putalpha(alpha) + + composite.alpha_over(img, button, (11,0), button) + + elif data == 3: # facing WEST + # paste it twice with different brightness to make a 3D effect + button = button.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, button, (0,-1), button) + + alpha = button.split()[3] + button = ImageEnhance.Brightness(button).enhance(0.9) + button.putalpha(alpha) + + composite.alpha_over(img, button, (1,0), button) + + elif data == 4: # facing EAST + # buttons can't be placed in transparent blocks, so this + # direction can't be seen + return None + + return img # snow @material(blockid=78, data=range(8), transparent=True, solid=True) From b08b15c84f947be228b8083ff02779ff295ff6aa Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 15 Nov 2011 09:48:30 +0100 Subject: [PATCH 44/47] Fix rail north rotation. --- overviewer_core/textures.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 5508f06..55859a7 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -943,7 +943,23 @@ def rails(blockid, data, north): elif (data & 0b0111) == 3: data = data & 0b1000 | 5 elif (data & 0b0111) == 4: data = data & 0b1000 | 3 elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - + if blockid == 66: # normal minetrack only + #Corners + if north == 'upper-left': + if data == 6: data = 7 + elif data == 7: data = 8 + elif data == 8: data = 6 + elif data == 9: data = 9 + elif north == 'upper-right': + if data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + elif north == 'lower-right': + if data == 6: data = 9 + elif data == 7: data = 6 + elif data == 8: data = 8 + elif data == 9: data = 7 img = Image.new("RGBA", (24,24), bgcolor) if blockid == 27: # powered rail From eac7561ed1a326f46622a5146f171af89176c2a3 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 15 Nov 2011 15:47:33 -0500 Subject: [PATCH 45/47] redirecting output to a log file now works on Python 2.6 (Issue #535) --- overviewer_core/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/util.py b/overviewer_core/util.py index 8a2947b..26a85d5 100644 --- a/overviewer_core/util.py +++ b/overviewer_core/util.py @@ -288,7 +288,7 @@ class DumbFormatter(HighlightingFormatter): line = "*" * min(79,len(line)) + "\n" + line return line else: - return super(DumbFormatter, self).highlight(record) + return HighlightingFormatter.highlight(self, record) class ANSIColorFormatter(HighlightingFormatter): From f6651cd759d873a1def1733e5eb6f871883101a1 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Thu, 17 Nov 2011 09:33:22 -0500 Subject: [PATCH 46/47] bumped extension number to prepare for likely merge tomorrow --- overviewer_core/src/overviewer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 780b298..4329b2b 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 11 +#define OVERVIEWER_EXTENSION_VERSION 13 /* Python PIL, and numpy headers */ #include From 4473ee0673395f599bd3d46bdb810185ffffdd2f Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sat, 19 Nov 2011 13:53:29 -0500 Subject: [PATCH 47/47] Made comments in sample.settings even clearer --- sample.settings.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/sample.settings.py b/sample.settings.py index 015120b..ef50d9f 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -1,10 +1,17 @@ ################################################################################ -# Please see the README or https://github.com/overviewer/Minecraft-Overviewer/wiki/DTT-Upgrade-Guide -# for more details. +# This is a SAMPLE settings file. -# This file is not meant to be used directly, but instead it is supposed to -# provide examples of interesting things you can do with the settings file. Most -# of the time, a simple 'setting_name = value' will work. +# THIS FILE IS NOT MEANT TO BE USED DIRECTLY, BUT INSTEAD IT IS SUPPOSED TO +# PROVIDE EXAMPLES OF INTERESTING THINGS YOU CAN DO WITH THE SETTINGS FILE. + +# THIS IS NOT A BASE FOR YOUR OWN SETTINGS FILE. CREATE A BLANK SETTINGS FILE +# AND PUT YOUR OPTIONS AND CUSOMIZATIONS IN THERE. + +# See http://docs.overviewer.org/en/latest/options/#command-line-options +# for options you can set + +# See http://docs.overviewer.org/en/latest/options/#settings-file +# for more info about settings files. # This file is a python script, so you can import any python module you wish or # use any built-in python function, though this is not normally necessary @@ -169,4 +176,9 @@ north_direction = "upper-right" ### a guide. Be sure to read what each option does before you set it. ### See the README for more details. import sys -sys.exit("This sample-settings file shouldn't be used directly!") +sys.exit(""" +The sample-settings file shouldn't be used! It contains lots of things you +don't want, and is just a set of examples to give you ideas of what's possible. +You must instead create a blank file and put your options and customizations in +there. +""")