1866 lines
76 KiB
Python
1866 lines
76 KiB
Python
# This file is part of the Minecraft Overviewer.
|
|
#
|
|
# Minecraft Overviewer is free software: you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or (at
|
|
# your option) any later version.
|
|
#
|
|
# Minecraft Overviewer is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
# Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import sys
|
|
import imp
|
|
import os
|
|
import os.path
|
|
import zipfile
|
|
from cStringIO import StringIO
|
|
import math
|
|
from random import randint
|
|
import numpy
|
|
from PIL import Image, ImageEnhance, ImageOps, ImageDraw
|
|
|
|
import util
|
|
import composite
|
|
|
|
_find_file_local_path = None
|
|
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:
|
|
|
|
* the textures_path given in the config file (if present)
|
|
* The program dir (same dir as overviewer.py)
|
|
* The overviewer_core textures dir
|
|
* On Darwin, in /Applications/Minecraft
|
|
* Inside minecraft.jar, which is looked for at these locations
|
|
|
|
* 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
|
|
|
|
"""
|
|
|
|
if _find_file_local_path:
|
|
path = os.path.join(_find_file_local_path, filename)
|
|
if os.path.exists(path):
|
|
if verbose: print "Found %s in '%s'" % (filename, path)
|
|
return open(path, mode)
|
|
|
|
programdir = util.get_program_path()
|
|
path = os.path.join(programdir, filename)
|
|
if os.path.exists(path):
|
|
if verbose: print "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):
|
|
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):
|
|
if verbose: print "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):
|
|
if verbose: print "Found %s in '%s'" % (filename, path)
|
|
return open(path, mode)
|
|
|
|
# Find minecraft.jar.
|
|
jarpaths = []
|
|
if "APPDATA" in os.environ:
|
|
jarpaths.append( os.path.join(os.environ['APPDATA'], ".minecraft",
|
|
"bin", "minecraft.jar"))
|
|
if "HOME" in os.environ:
|
|
jarpaths.append(os.path.join(os.environ['HOME'], "Library",
|
|
"Application Support", "minecraft","bin","minecraft.jar"))
|
|
jarpaths.append(os.path.join(os.environ['HOME'], ".minecraft", "bin",
|
|
"minecraft.jar"))
|
|
jarpaths.append(programdir)
|
|
jarpaths.append(os.getcwd())
|
|
|
|
for jarpath in jarpaths:
|
|
if os.path.exists(jarpath):
|
|
try:
|
|
jar = zipfile.ZipFile(jarpath)
|
|
if verbose: print "Found %s in '%s'" % (filename, jarpath)
|
|
return jar.open(filename)
|
|
except (KeyError, IOError):
|
|
pass
|
|
|
|
raise IOError("Could not find the file {0}. Is Minecraft installed? If so, I couldn't find the minecraft.jar file.".format(filename))
|
|
|
|
def _load_image(filename):
|
|
"""Returns an image object"""
|
|
fileobj = _find_file(filename)
|
|
buffer = StringIO(fileobj.read())
|
|
return Image.open(buffer)
|
|
|
|
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 = []
|
|
(terrain_width, terrain_height) = terrain.size
|
|
texture_resolution = terrain_width / 16
|
|
for y in xrange(16):
|
|
for x in xrange(16):
|
|
left = x*texture_resolution
|
|
upper = y*texture_resolution
|
|
right = left+texture_resolution
|
|
lower = upper+texture_resolution
|
|
region = terrain.transform(
|
|
(16, 16),
|
|
Image.EXTENT,
|
|
(left,upper,right,lower),
|
|
Image.BICUBIC)
|
|
textures.append(region)
|
|
|
|
return textures
|
|
|
|
def transform_image(img, blockID=None):
|
|
"""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
|
|
|
|
"""
|
|
|
|
# Resize to 17x17, since the diagonal is approximately 24 pixels, a nice
|
|
# even number that can be split in half twice
|
|
img = img.resize((17, 17), Image.ANTIALIAS)
|
|
|
|
# Build the Affine transformation matrix for this perspective
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
# Translate up and left, since rotations are about the origin
|
|
transform *= numpy.matrix([[1,0,8.5],[0,1,8.5],[0,0,1]])
|
|
# Rotate 45 degrees
|
|
ratio = math.cos(math.pi/4)
|
|
#transform *= numpy.matrix("[0.707,-0.707,0;0.707,0.707,0;0,0,1]")
|
|
transform *= numpy.matrix([[ratio,-ratio,0],[ratio,ratio,0],[0,0,1]])
|
|
# Translate back down and right
|
|
transform *= numpy.matrix([[1,0,-12],[0,1,-12],[0,0,1]])
|
|
# scale the image down by a factor of 2
|
|
transform *= numpy.matrix("[1,0,0;0,2,0;0,0,1]")
|
|
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((24,12), Image.AFFINE, transform)
|
|
return newimg
|
|
|
|
def transform_image_side(img, blockID=None):
|
|
"""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, (38,92,255,0))
|
|
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, (38,92,255,0))
|
|
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)
|
|
|
|
# Apply shear
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
transform *= numpy.matrix("[1,0,0;-0.5,1,0;0,0,1]")
|
|
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((12,18), Image.AFFINE, transform)
|
|
return newimg
|
|
|
|
def transform_image_slope(img, blockID=None):
|
|
"""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"""
|
|
|
|
# Take the same size as trasform_image_side
|
|
img = img.resize((12,12), Image.ANTIALIAS)
|
|
|
|
# Apply shear
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
transform *= numpy.matrix("[0.75,-0.5,3;0.25,0.5,-3;0,0,1]")
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((24,24), Image.AFFINE, transform)
|
|
|
|
return newimg
|
|
|
|
|
|
def transform_image_angle(img, angle, blockID=None):
|
|
"""Takes an image an shears it in arbitrary angle with the axis of
|
|
rotation being vertical.
|
|
|
|
WARNING! Don't use angle = pi/2 (or multiplies), it will return
|
|
a blank image (or maybe garbage).
|
|
|
|
NOTE: angle is in the image not in game, so for the left side of a
|
|
block angle = 30 degree.
|
|
"""
|
|
|
|
# Take the same size as trasform_image_side
|
|
img = img.resize((12,12), Image.ANTIALIAS)
|
|
|
|
# some values
|
|
cos_angle = math.cos(angle)
|
|
sin_angle = math.sin(angle)
|
|
|
|
# function_x and function_y are used to keep the result image in the
|
|
# same position, and constant_x and constant_y are the coordinates
|
|
# for the center for angle = 0.
|
|
constant_x = 6.
|
|
constant_y = 6.
|
|
function_x = 6.*(1-cos_angle)
|
|
function_y = -6*sin_angle
|
|
big_term = ( (sin_angle * (function_x + constant_x)) - cos_angle* (function_y + constant_y))/cos_angle
|
|
|
|
# The numpy array is not really used, but is helpful to
|
|
# see the matrix used for the transformation.
|
|
transform = numpy.array([[1./cos_angle, 0, -(function_x + constant_x)/cos_angle],
|
|
[-sin_angle/(cos_angle), 1., big_term ],
|
|
[0, 0, 1.]])
|
|
|
|
transform = tuple(transform[0]) + tuple(transform[1])
|
|
|
|
newimg = img.transform((24,24), Image.AFFINE, transform)
|
|
|
|
return newimg
|
|
|
|
|
|
def _build_block(top, side, blockID=None):
|
|
"""From a top texture and a side texture, build a block image.
|
|
top and side should be 16x16 image objects. Returns a 24x24 image
|
|
|
|
"""
|
|
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
|
|
|
original_texture = top.copy()
|
|
top = transform_image(top, blockID)
|
|
|
|
if not side:
|
|
composite.alpha_over(img, top, (0,0), top)
|
|
return img
|
|
|
|
side = transform_image_side(side, blockID)
|
|
otherside = 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 = 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)
|
|
|
|
## special case for tall-grass, fern and dead shrub,
|
|
if blockID in (31,32):
|
|
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, side, (0,6), side)
|
|
composite.alpha_over(img, otherside, (12,6), otherside)
|
|
composite.alpha_over(img, top, (0,0), top)
|
|
|
|
# Manually touch up 6 pixels that leave a gap because of how the
|
|
# shearing works out. This makes the blocks perfectly tessellate-able
|
|
for x,y in [(13,23), (17,21), (21,19)]:
|
|
# Copy a pixel to x,y from x-1,y
|
|
img.putpixel((x,y), img.getpixel((x-1,y)))
|
|
for x,y in [(3,4), (7,2), (11,0)]:
|
|
# Copy a pixel to x,y from x+1,y
|
|
img.putpixel((x,y), img.getpixel((x+1,y)))
|
|
|
|
return img
|
|
|
|
|
|
def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=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.
|
|
|
|
side1 is in the -y face of the cube (top left, east)
|
|
side2 is in the +x (top right, south)
|
|
side3 is in the -x (bottom left, north)
|
|
side4 is in the +y (bottom right, west)
|
|
|
|
A non transparent block uses top, side 3 and side 4.
|
|
|
|
If top is a tuple then first item is the top image and the second
|
|
item is an increment (integer) from 0 to 16 (pixels in the
|
|
original minecraft texture). This increment will be used to crop the
|
|
side images and to paste the top image increment pixels lower, so if
|
|
you use an increment of 8, it willll draw a half-block.
|
|
|
|
NOTE: this method uses the bottom of the texture image (as done in
|
|
minecraft with beds and cackes)
|
|
|
|
"""
|
|
|
|
increment = 0
|
|
if isinstance(top, tuple):
|
|
increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
|
|
crop_height = increment
|
|
top = top[0]
|
|
if side1 != None:
|
|
side1 = side1.copy()
|
|
ImageDraw.Draw(side1).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side2 != None:
|
|
side2 = side2.copy()
|
|
ImageDraw.Draw(side2).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side3 != None:
|
|
side3 = side3.copy()
|
|
ImageDraw.Draw(side3).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side4 != None:
|
|
side4 = side4.copy()
|
|
ImageDraw.Draw(side4).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
|
|
|
# first back sides
|
|
if side1 != None :
|
|
side1 = transform_image_side(side1, blockID)
|
|
side1 = side1.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Darken this side.
|
|
sidealpha = side1.split()[3]
|
|
side1 = ImageEnhance.Brightness(side1).enhance(0.9)
|
|
side1.putalpha(sidealpha)
|
|
|
|
composite.alpha_over(img, side1, (0,0), side1)
|
|
|
|
|
|
if side2 != None :
|
|
side2 = transform_image_side(side2, blockID)
|
|
|
|
# Darken this side.
|
|
sidealpha2 = side2.split()[3]
|
|
side2 = ImageEnhance.Brightness(side2).enhance(0.8)
|
|
side2.putalpha(sidealpha2)
|
|
|
|
composite.alpha_over(img, side2, (12,0), side2)
|
|
|
|
if bottom != None :
|
|
bottom = transform_image(bottom, blockID)
|
|
composite.alpha_over(img, bottom, (0,12), bottom)
|
|
|
|
# front sides
|
|
if side3 != None :
|
|
side3 = transform_image_side(side3, blockID)
|
|
|
|
# Darken this side
|
|
sidealpha = side3.split()[3]
|
|
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
|
|
side3.putalpha(sidealpha)
|
|
|
|
composite.alpha_over(img, side3, (0,6), side3)
|
|
|
|
if side4 != None :
|
|
side4 = transform_image_side(side4, blockID)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Darken this side
|
|
sidealpha = side4.split()[3]
|
|
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
|
|
side4.putalpha(sidealpha)
|
|
|
|
composite.alpha_over(img, side4, (12,6), side4)
|
|
|
|
if top != None :
|
|
top = transform_image(top, blockID)
|
|
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, 67,
|
|
# 80 81 82 83 84 85 86 87 88 89 90 91
|
|
66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102 # clay?
|
|
]
|
|
|
|
# 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, 67,
|
|
# 80 81 82 83 84 85 86 87 88 89 90 91
|
|
66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118
|
|
]
|
|
|
|
# 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")
|
|
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")
|
|
lavablock = _build_block(lavatexture, lavatexture)
|
|
blockmap[10] = generate_texture_tuple(lavablock,10)
|
|
blockmap[11] = blockmap[10]
|
|
|
|
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
|
|
smallers than 50, and sets every other value to 255. """
|
|
|
|
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 list and specialblockmap dictionary."""
|
|
return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img))
|
|
|
|
def generate_special_texture(blockID, data):
|
|
"""Generates a special texture, such as a correctly facing minecraft track"""
|
|
#print "%s has ancillary data: %X" %(blockID, data)
|
|
# TODO ladders, stairs, levers, buttons, and signs
|
|
# all need to behandled here (and in chunkpy)
|
|
|
|
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:
|
|
colored = tintTexture(biome_grass_texture, (115, 175, 71))
|
|
composite.alpha_over(img, colored, (0, 0), colored)
|
|
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: # spring water, flowing water and waterfall water, AND glass
|
|
# water and glass share the way to be rendered
|
|
if blockID == 9:
|
|
texture = _load_image("water.png")
|
|
else:
|
|
texture = terrain_images[49]
|
|
|
|
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
|
|
|
|
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 = tintTexture(terrain_images[52], (37, 118, 25))
|
|
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].copy()
|
|
texture = tintTexture(texture, (115, 175, 71))
|
|
elif data == 2: # fern
|
|
texture = terrain_images[56].copy()
|
|
texture = tintTexture(texture, (115, 175, 71))
|
|
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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]
|
|
if data == 1: # stone slab
|
|
top = terrain_images[176]
|
|
side = terrain_images[192]
|
|
if data == 2: # wooden slab
|
|
top = side = terrain_images[4]
|
|
if data == 3: # cobblestone slab
|
|
top = side = terrain_images[16]
|
|
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
|
|
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, (6,4))
|
|
composite.alpha_over(img, small_crop, (5,5))
|
|
composite.alpha_over(img, small_crop, (6,5))
|
|
composite.alpha_over(img, slice, (6,6))
|
|
|
|
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), (38,92,255,0))
|
|
|
|
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): # wooden and cobblestone stairs.
|
|
|
|
if blockID == 53: # wooden
|
|
texture = terrain_images[4]
|
|
|
|
elif blockID == 67: # cobblestone
|
|
texture = terrain_images[16]
|
|
|
|
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), (38,92,255,0)) # 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), (38,92,255,0)) # 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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
|
|
# 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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
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), (38,92,255,0))
|
|
|
|
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), (38,92,255,0))
|
|
|
|
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 == 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,15,0),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), (38,92,255,0))
|
|
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,1),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), (38,92,255,0))
|
|
|
|
# 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), (38,92,255,0))
|
|
|
|
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), (38,92,255,0))
|
|
|
|
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
|
|
# NOTE: this function uses the redstone torches generated above,
|
|
# this must run after the function of the torches.
|
|
|
|
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)
|
|
|
|
# paste redstone torches everywhere!
|
|
t = specialblockmap[(75,5)] if blockID == 93 else specialblockmap[(76,5)]
|
|
torch = t[0].copy() # textures are stored as tuples (RGB,A)
|
|
torch.putalpha(t[1])
|
|
|
|
# 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)
|
|
|
|
|
|
return None
|
|
|
|
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
|
|
|
|
currentBiomeFile = None
|
|
currentBiomeData = None
|
|
grasscolor = None
|
|
foliagecolor = None
|
|
|
|
def prepareBiomeData(worlddir):
|
|
global grasscolor, foliagecolor
|
|
|
|
# skip if the color files are already loaded
|
|
if grasscolor and foliagecolor:
|
|
return
|
|
|
|
biomeDir = os.path.join(worlddir, "biomes")
|
|
if not os.path.exists(biomeDir):
|
|
raise Exception("biomes not found")
|
|
|
|
# try to find the biome color images. If _find_file can't locate them
|
|
# then try looking in the EXTRACTEDBIOMES folder
|
|
try:
|
|
grasscolor = list(Image.open(_find_file("grasscolor.png")).getdata())
|
|
foliagecolor = list(Image.open(_find_file("foliagecolor.png")).getdata())
|
|
except IOError:
|
|
try:
|
|
grasscolor = list(Image.open(os.path.join(biomeDir,"grasscolor.png")).getdata())
|
|
foliagecolor = list(Image.open(os.path.join(biomeDir,"foliagecolor.png")).getdata())
|
|
except:
|
|
# clear anything that managed to get set
|
|
grasscolor = None
|
|
foliagecolor = None
|
|
|
|
def getBiomeData(worlddir, chunkX, chunkY):
|
|
'''Opens the worlddir and reads in the biome color information
|
|
from the .biome files. See also:
|
|
http://www.minecraftforum.net/viewtopic.php?f=25&t=80902
|
|
'''
|
|
|
|
global currentBiomeFile, currentBiomeData
|
|
|
|
biomeFile = "b.%d.%d.biome" % (chunkX // 32, chunkY // 32)
|
|
if biomeFile == currentBiomeFile:
|
|
return currentBiomeData
|
|
|
|
try:
|
|
with open(os.path.join(worlddir, "biomes", biomeFile), "rb") as f:
|
|
rawdata = f.read()
|
|
# make sure the file size is correct
|
|
if not len(rawdata) == 512 * 512 * 2:
|
|
raise Exception("Biome file %s is not valid." % (biomeFile,))
|
|
data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2"))
|
|
except IOError:
|
|
data = None
|
|
pass # no biome data
|
|
|
|
currentBiomeFile = biomeFile
|
|
currentBiomeData = data
|
|
return data
|
|
|
|
# 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, 71, 75, 76, 85, 86, 90, 91,
|
|
92, 93, 94, 96])
|
|
|
|
# this is a map of special blockIDs to a list of all
|
|
# possible values for ancillary data that it might have.
|
|
|
|
special_map = {}
|
|
|
|
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[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[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(4) # stone, sandstone, wooden and cobblestone double-slab
|
|
special_map[44] = range(4) # 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
|
|
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[71] = range(16) # iron door, open/close and orientation
|
|
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[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!
|
|
special_map[93] = range(16) # OFF redstone repeater, orientation and delay (delay not implemented)
|
|
special_map[94] = range(16) # ON redstone repeater, orientation and delay (delay not implemented)
|
|
special_map[96] = range(8) # trapdoor, open, closed, orientation
|
|
|
|
# grass and leaves are graysacle in terrain.png
|
|
# we treat them as special so we can manually tint them
|
|
# it is unknown how the specific tint (biomes) is calculated
|
|
# also, 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[18] = range(16) # leaves, birch, normal or pine leaves (not implemented)
|
|
special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself
|
|
|
|
# placeholders that are generated in generate()
|
|
terrain_images = None
|
|
blockmap = None
|
|
biome_grass_texture = None
|
|
biome_tall_grass_texture = None
|
|
biome_tall_fern_texture = None
|
|
biome_leaf_texture = None
|
|
specialblockmap = None
|
|
|
|
def generate(path=None,texture_size=24):
|
|
global _find_file_local_path, texture_dimensions
|
|
_find_file_local_path = path
|
|
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 = _build_blockimages()
|
|
load_water()
|
|
|
|
# generate biome (still grayscale) leaf, grass textures
|
|
global biome_grass_texture, biome_leaf_texture, biome_tall_grass_texture, biome_tall_fern_texture
|
|
biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2)
|
|
biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18)
|
|
biome_tall_grass_texture = _build_block(terrain_images[39], terrain_images[39], 31)
|
|
biome_tall_fern_texture = _build_block(terrain_images[56], terrain_images[56], 31)
|
|
|
|
# generate the special blocks
|
|
global specialblockmap, special_blocks
|
|
specialblockmap = {}
|
|
for blockID in special_blocks:
|
|
for data in special_map[blockID]:
|
|
specialblockmap[(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)
|
|
biome_leaf_texture = biome_leaf_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
|
biome_tall_grass_texture = biome_tall_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
|
biome_tall_fern_texture = biome_tall_fern_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)]
|
|
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)
|