minetestmapper: Fix the way ylist is computed, use cStringIO instead of rolling own Bytestream, remove some artifacts when drawing underground blocks, introduce a --drawunderground switch (defaults to of), 'blocknum' renamed to 'content'.

master
Kahrl 2011-09-27 02:38:19 +02:00
parent a7833cca22
commit 3e975d8f28
1 changed files with 66 additions and 92 deletions

View File

@ -29,10 +29,9 @@ import time
import getopt import getopt
import sys import sys
import array import array
import cStringIO
from PIL import Image, ImageDraw, ImageFont, ImageColor from PIL import Image, ImageDraw, ImageFont, ImageColor
CONTENT_WATER = [2, 9]
TRANSLATION_TABLE = { TRANSLATION_TABLE = {
1: 0x800, # CONTENT_GRASS 1: 0x800, # CONTENT_GRASS
4: 0x801, # CONTENT_TREE 4: 0x801, # CONTENT_TREE
@ -56,25 +55,6 @@ TRANSLATION_TABLE = {
28: 0x813, # CONTENT_PAPYRUS 28: 0x813, # CONTENT_PAPYRUS
29: 0x814} # CONTENT_BOOKSHELF 29: 0x814} # CONTENT_BOOKSHELF
class Bytestream:
def __init__(self, stream):
self.stream = stream
self.pos = 0
# So you can use files also
if hasattr(self.stream, 'read'):
self.read = self.stream.read
def __len__(self):
return len(self.stream)
def read(self, length = None):
if length is None:
length = len(self)
self.pos += length
return self.stream[self.pos - length:self.pos]
def close(self): pass
def hex_to_int(h): def hex_to_int(h):
i = int(h, 16) i = int(h, 16)
@ -130,14 +110,15 @@ def limit(i, l, h):
def usage(): def usage():
print "TODO: Help" print("TODO: Help")
try: try:
opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=",
"output=", "bgcolor=", "scalecolor=", "origincolor=", "output=", "bgcolor=", "scalecolor=", "origincolor=",
"playercolor=", "draworigin", "drawplayers", "drawscale"]) "playercolor=", "draworigin", "drawplayers", "drawscale",
except getopt.GetoptError, err: "drawunderground"])
except getopt.GetoptError as err:
# print help information and exit: # print help information and exit:
print str(err) # will print something like "option -a not recognized" print(str(err)) # will print something like "option -a not recognized"
usage() usage()
sys.exit(2) sys.exit(2)
@ -151,6 +132,7 @@ playercolor = "red"
drawscale = False drawscale = False
drawplayers = False drawplayers = False
draworigin = False draworigin = False
drawunderground = False
sector_xmin = -1500 / 16 sector_xmin = -1500 / 16
sector_xmax = 1500 / 16 sector_xmax = 1500 / 16
@ -180,6 +162,8 @@ for o, a in opts:
drawplayers = True drawplayers = True
elif o == "--draworigin": elif o == "--draworigin":
draworigin = True draworigin = True
elif o == "--drawunderground":
drawunderground = True
else: else:
assert False, "unhandled option" assert False, "unhandled option"
@ -263,7 +247,7 @@ maxz = max(zlist)
w = (maxx - minx) * 16 + 16 w = (maxx - minx) * 16 + 16
h = (maxz - minz) * 16 + 16 h = (maxz - minz) * 16 + 16
print "w=" + str(w) + " h=" + str(h) print("w=" + str(w) + " h=" + str(h))
im = Image.new("RGB", (w + border, h + border), bgcolor) im = Image.new("RGB", (w + border, h + border), bgcolor)
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
@ -273,12 +257,15 @@ stuff = {}
starttime = time.time() starttime = time.time()
CONTENT_WATER = 2
def data_is_air(d): def content_is_water(d):
return d in [2, 9]
def content_is_air(d):
return d in [126, 127, 254] return d in [126, 127, 254]
def read_content(mapdata, version, datapos):
def read_blocknum(mapdata, version, datapos):
if version == 20: if version == 20:
if mapdata[datapos] < 0x80: if mapdata[datapos] < 0x80:
return mapdata[datapos] return mapdata[datapos]
@ -290,7 +277,7 @@ def read_blocknum(mapdata, version, datapos):
raise Exception("Unsupported map format: " + str(version)) raise Exception("Unsupported map format: " + str(version))
def read_mapdata(f, version, pixellist, water): def read_mapdata(f, version, pixellist, water, day_night_differs):
global stuff # oh my :-) global stuff # oh my :-)
dec_o = zlib.decompressobj() dec_o = zlib.decompressobj()
@ -302,34 +289,35 @@ def read_mapdata(f, version, pixellist, water):
f.close() f.close()
if(len(mapdata) < 4096): if(len(mapdata) < 4096):
print "bad: " + xhex + "/" + zhex + "/" + yhex + " " + \ print("bad: " + xhex + "/" + zhex + "/" + yhex + " " + \
str(len(mapdata)) str(len(mapdata)))
else: else:
chunkxpos = xpos * 16 chunkxpos = xpos * 16
chunkypos = ypos * 16 chunkypos = ypos * 16
chunkzpos = zpos * 16 chunkzpos = zpos * 16
blocknum = 0 content = 0
datapos = 0 datapos = 0
for (x, z) in reversed(pixellist): for (x, z) in reversed(pixellist):
for y in reversed(range(16)): for y in reversed(range(16)):
datapos = x + y * 16 + z * 256 datapos = x + y * 16 + z * 256
blocknum = read_blocknum(mapdata, version, datapos) content = read_content(mapdata, version, datapos)
if not data_is_air(blocknum) and blocknum in colors: if content_is_air(content):
if blocknum in CONTENT_WATER: pass
water[(x, z)] += 1 elif content_is_water(content):
# Add dummy stuff for drawing sea without seabed water[(x, z)] += 1
stuff[(chunkxpos + x, chunkzpos + z)] = ( # Add dummy stuff for drawing sea without seabed
chunkypos + y, blocknum, water[(x, z)]) stuff[(chunkxpos + x, chunkzpos + z)] = (
else: chunkypos + y, content, water[(x, z)], day_night_differs)
pixellist.remove((x, z)) elif content in colors:
# Memorize information on the type and height of # Memorize information on the type and height of
# the block and for drawing the picture. # the block and for drawing the picture.
stuff[(chunkxpos + x, chunkzpos + z)] = ( stuff[(chunkxpos + x, chunkzpos + z)] = (
chunkypos + y, blocknum, water[(x, z)]) chunkypos + y, content, water[(x, z)], day_night_differs)
break pixellist.remove((x, z))
elif not data_is_air(blocknum) and blocknum not in colors: break
print "strange block: %s/%s/%s x: %d y: %d z: %d \ else:
block id: %x" % (xhex, zhex, yhex, x, y, z, blocknum) print("strange block: %s/%s/%s x: %d y: %d z: %d \
block id: %x" % (xhex, zhex, yhex, x, y, z, content))
# Go through all sectors. # Go through all sectors.
for n in range(len(xlist)): for n in range(len(xlist)):
@ -369,8 +357,9 @@ for n in range(len(xlist)):
sectortype = "" sectortype = ""
if cur: if cur:
ps = getBlockAsInteger((xpos, 0, zpos)) psmin = getBlockAsInteger((xpos, -2048, zpos))
cur.execute("SELECT `pos` FROM `blocks` WHERE `pos`>=? AND `pos`<?", (ps, ps + 4096)) psmax = getBlockAsInteger((xpos, 2047, zpos))
cur.execute("SELECT `pos` FROM `blocks` WHERE `pos`>=? AND `pos`<=? AND (`pos` - ?) % 4096 = 0", (psmin, psmax, psmin))
while True: while True:
r = cur.fetchone() r = cur.fetchone()
if not r: if not r:
@ -404,8 +393,7 @@ for n in range(len(xlist)):
if sectortype == "": if sectortype == "":
continue continue
#ylist.sort() ylist.sort()
ylist = sorted(set(ylist))
# Make a list of pixels of the sector that are to be looked for. # Make a list of pixels of the sector that are to be looked for.
pixellist = [] pixellist = []
@ -416,25 +404,22 @@ for n in range(len(xlist)):
water[(x, z)] = 0 water[(x, z)] = 0
# Go through the Y axis from top to bottom. # Go through the Y axis from top to bottom.
ylist2 = []
for ypos in reversed(ylist): for ypos in reversed(ylist):
yhex = int_to_hex4(ypos) yhex = int_to_hex4(ypos)
filename = ""
if sectortype == "sqlite": if sectortype == "sqlite":
ps = getBlockAsInteger((xpos, ypos, zpos)) ps = getBlockAsInteger((xpos, ypos, zpos))
cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (ps,)) cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (ps,))
r = cur.fetchone() r = cur.fetchone()
if not r: if not r:
continue continue
f = Bytestream(r[0]) f = cStringIO.StringIO(r[0])
else: else:
if sectortype == "old": if sectortype == "old":
filename = path + "sectors/" + sector1 + "/" + yhex.lower() filename = path + "sectors/" + sector1 + "/" + yhex.lower()
else: else:
filename = path + "sectors2/" + sector2 + "/" + yhex.lower() filename = path + "sectors2/" + sector2 + "/" + yhex.lower()
f = file(filename, "rb") f = file(filename, "rb")
# Let's just memorize these even though it's not really necessary. # Let's just memorize these even though it's not really necessary.
@ -442,38 +427,16 @@ for n in range(len(xlist)):
flags = f.read(1) flags = f.read(1)
# Checking day and night differs -flag # Checking day and night differs -flag
if not ord(flags) & 2: day_night_differs = ((ord(flags) & 2) != 0)
ylist2.append((ypos, filename))
f.close()
continue
read_mapdata(f, version, pixellist, water) read_mapdata(f, version, pixellist, water, day_night_differs)
# After finding all the pixels in the sector, we can move on to # After finding all the pixels in the sector, we can move on to
# the next sector without having to continue the Y axis. # the next sector without having to continue the Y axis.
if(len(pixellist) == 0): if(len(pixellist) == 0):
break break
if len(pixellist) > 0: print("Drawing image")
for (ypos, filename) in ylist2:
ps = getBlockAsInteger((xpos, ypos, zpos))
cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (ps,))
r = cur.fetchone()
if not r:
continue
f = Bytestream(r[0])
version = ord(f.read(1))
flags = f.read(1)
read_mapdata(f, version, pixellist, water)
# After finding all the pixels in the sector, we can move on
# to the next sector without having to continue the Y axis.
if(len(pixellist) == 0):
break
print "Drawing image"
# Drawing the picture # Drawing the picture
starttime = time.time() starttime = time.time()
n = 0 n = 0
@ -499,18 +462,29 @@ for (x, z) in stuff.iterkeys():
n += 1 n += 1
(r, g, b) = colors[stuff[(x, z)][1]] (r, g, b) = colors[stuff[(x, z)][1]]
dnd = stuff[(x, z)][3] # day/night differs?
if not dnd and not drawunderground:
if stuff[(x, z)][2] > 0: # water
(r, g, b) = colors[CONTENT_WATER]
else:
continue
# Comparing heights of a couple of adjacent blocks and changing # Comparing heights of a couple of adjacent blocks and changing
# brightness accordingly. # brightness accordingly.
try: try:
c = stuff[(x, z)][1]
c1 = stuff[(x - 1, z)][1] c1 = stuff[(x - 1, z)][1]
c2 = stuff[(x, z + 1)][1] c2 = stuff[(x, z + 1)][1]
c = stuff[(x, z)][1] dnd1 = stuff[(x - 1, z)][3]
if c1 not in CONTENT_WATER and c2 not in CONTENT_WATER and \ dnd2 = stuff[(x, z + 1)][3]
c not in CONTENT_WATER: if not dnd:
y1 = stuff[(x - 1, z)][0] d = -69
y2 = stuff[(x, z + 1)][0] elif not content_is_water(c1) and not content_is_water(c2) and \
not content_is_water(c):
y = stuff[(x, z)][0] y = stuff[(x, z)][0]
y1 = stuff[(x - 1, z)][0] if dnd1 else y
y2 = stuff[(x, z + 1)][0] if dnd2 else y
d = ((y - y1) + (y - y2)) * 12 d = ((y - y1) + (y - y2)) * 12
else: else:
d = 0 d = 0
@ -567,10 +541,10 @@ if drawplayers:
p = string.split(line) p = string.split(line)
if p[0] == "name": if p[0] == "name":
name = p[2] name = p[2]
print filename + ": name = " + name print(filename + ": name = " + name)
if p[0] == "position": if p[0] == "position":
position = string.split(p[2][1:-1], ",") position = string.split(p[2][1:-1], ",")
print filename + ": position = " + p[2] print(filename + ": position = " + p[2])
if len(name) > 0 and len(position) == 3: if len(name) > 0 and len(position) == 3:
x = (int(float(position[0]) / 10 - minx * 16)) x = (int(float(position[0]) / 10 - minx * 16))
z = int(h - (float(position[2]) / 10 - minz * 16)) z = int(h - (float(position[2]) / 10 - minz * 16))
@ -582,5 +556,5 @@ if drawplayers:
except OSError: except OSError:
pass pass
print "Saving" print("Saving")
im.save(output) im.save(output)