initial comit
This commit is contained in:
142
chunk.py
Normal file
142
chunk.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import numpy
|
||||
from PIL import Image
|
||||
from itertools import izip, count
|
||||
|
||||
import nbt
|
||||
import textures
|
||||
from textures import texturemap as txtarray
|
||||
|
||||
# General note about pasting transparent image objects onto an image with an
|
||||
# alpha channel:
|
||||
# If you use the image as its own mask, it will work fine only if the alpha
|
||||
# channel is binary. If there's any translucent parts, then the alpha channel
|
||||
# of the dest image will have its alpha channel modified. To prevent this:
|
||||
# first use im.split() and take the third item which is the alpha channel and
|
||||
# use that as the mask. Then take the image and use im.convert("RGB") to strip
|
||||
# the image from its alpha channel, and use that as the source to paste()
|
||||
|
||||
def get_lvldata(filename):
|
||||
"""Takes a filename and returns the Level struct, which contains all the
|
||||
level info"""
|
||||
return nbt.load(filename)[1]['Level']
|
||||
|
||||
def get_blockarray(level):
|
||||
"""Takes the level struct as returned from get_lvldata, and returns the
|
||||
Block array, which just contains all the block ids"""
|
||||
return numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
|
||||
|
||||
def get_blockarray_fromfile(filename):
|
||||
"""Same as get_blockarray except takes a filename and uses get_lvldata to
|
||||
open it. This is a shortcut"""
|
||||
level = get_lvldata(filename)
|
||||
return get_blockarray(level)
|
||||
|
||||
def get_skylight_array(level):
|
||||
"""Returns the skylight array. Remember this is 4 bits per block, so divide
|
||||
the z component by 2 when accessing the array. and mask off the top or
|
||||
bottom 4 bits if it's odd or even respectively
|
||||
"""
|
||||
return numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64))
|
||||
|
||||
# This set holds blocks ids that can be seen through
|
||||
transparent_blocks = set([0, 8, 9, 18, 20, 37, 38, 39, 40, 50, 51, 52, 53, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 79, 83, 85])
|
||||
|
||||
def chunk_render(chunkfile, img=None, xoff=0, yoff=0, cave=False):
|
||||
level = get_lvldata(chunkfile)
|
||||
blocks = get_blockarray(level)
|
||||
if cave:
|
||||
skylight = get_skylight_array(level)
|
||||
# Cave mode. Actually go through and 0 out all blocks that are not in a
|
||||
# cave, so that it only renders caves.
|
||||
|
||||
# 1st task: this array is 2 blocks per byte, expand it so we can just
|
||||
# do a bitwise and on the arrays
|
||||
skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
||||
# Even elements get the lower 4 bits
|
||||
skylight_expanded[:,:,::2] = skylight & 0x0F
|
||||
# Odd elements get the upper 4 bits
|
||||
skylight_expanded[:,:,1::2] = skylight >> 4
|
||||
|
||||
# Places where the skylight is not 0 (there's some amount of skylight
|
||||
# touching it) change it to something that won't get rendered, AND
|
||||
# won't get counted as "transparent".
|
||||
blocks = blocks.copy()
|
||||
blocks[skylight_expanded != 0] = 21
|
||||
|
||||
# Don't render
|
||||
|
||||
|
||||
# Each block is 24x24
|
||||
# The next block on the X axis adds 12px to x and subtracts 6px from y in the image
|
||||
# The next block on the Y axis adds 12px to x and adds 6px to y in the image
|
||||
# The next block up on the Z axis subtracts 12 from y axis in the image
|
||||
|
||||
# Since there are 16x16x128 blocks in a chunk, the image will be 384x1728
|
||||
# (height is 128*24 high, plus the size of the horizontal plane: 16*12)
|
||||
if not img:
|
||||
img = Image.new("RGBA", (384, 1728))
|
||||
|
||||
for x in xrange(15,-1,-1):
|
||||
for y in xrange(16):
|
||||
imgx = xoff + x*12 + y*12
|
||||
imgy = yoff - x*6 + y*6 + 128*12 + 16*12//2
|
||||
for z in xrange(128):
|
||||
try:
|
||||
|
||||
blockid = blocks[x,y,z]
|
||||
t = textures.blockmap[blockid]
|
||||
if not t:
|
||||
continue
|
||||
|
||||
# Check if this block is occluded
|
||||
if cave and (
|
||||
x == 0 and y != 15 and z != 127
|
||||
):
|
||||
# If it's on the x face, only render if there's a
|
||||
# transparent block in the y+1 direction OR the z-1
|
||||
# direction
|
||||
if (
|
||||
blocks[x,y+1,z] not in transparent_blocks and
|
||||
blocks[x,y,z+1] not in transparent_blocks
|
||||
):
|
||||
continue
|
||||
elif cave and (
|
||||
y == 15 and x != 0 and z != 127
|
||||
):
|
||||
# If it's on the facing y face, only render if there's
|
||||
# a transparent block in the x-1 direction OR the z-1
|
||||
# direction
|
||||
if (
|
||||
blocks[x-1,y,z] not in transparent_blocks and
|
||||
blocks[x,y,z+1] not in transparent_blocks
|
||||
):
|
||||
continue
|
||||
elif cave and (
|
||||
y == 15 and x == 0
|
||||
):
|
||||
# If it's on the facing edge, only render if what's
|
||||
# above it is transparent
|
||||
if (
|
||||
blocks[x,y,z+1] not in transparent_blocks
|
||||
):
|
||||
continue
|
||||
elif (
|
||||
# Normal block or not cave mode, check sides for
|
||||
# transparentcy or render unconditionally if it's
|
||||
# on a shown face
|
||||
x != 0 and y != 15 and z != 127 and
|
||||
blocks[x-1,y,z] not in transparent_blocks and
|
||||
blocks[x,y+1,z] not in transparent_blocks and
|
||||
blocks[x,y,z+1] not in transparent_blocks
|
||||
):
|
||||
# Don't render if all sides aren't transparent and
|
||||
# we're not on the edge
|
||||
continue
|
||||
|
||||
img.paste(t[0], (imgx, imgy), t[1])
|
||||
|
||||
finally:
|
||||
# Do this no mater how the above block exits
|
||||
imgy -= 12
|
||||
|
||||
return img
|
||||
130
nbt.py
Normal file
130
nbt.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import gzip
|
||||
import struct
|
||||
|
||||
def load(fileobj):
|
||||
if isinstance(fileobj, basestring):
|
||||
# Is actually a filename
|
||||
fileobj = open(fileobj, 'r')
|
||||
return NBTFileReader(fileobj).read_all()
|
||||
|
||||
class NBTFileReader(object):
|
||||
def __init__(self, fileobj):
|
||||
self._file = gzip.GzipFile(fileobj=fileobj, mode='r')
|
||||
|
||||
# These private methods read the payload only of the following types
|
||||
def _read_tag_end(self):
|
||||
# Nothing to read
|
||||
return 0
|
||||
|
||||
def _read_tag_byte(self):
|
||||
byte = self._file.read(1)
|
||||
return struct.unpack("b", byte)[0]
|
||||
|
||||
def _read_tag_short(self):
|
||||
bytes = self._file.read(2)
|
||||
return struct.unpack(">h", bytes)[0]
|
||||
|
||||
def _read_tag_int(self):
|
||||
bytes = self._file.read(4)
|
||||
return struct.unpack(">i", bytes)[0]
|
||||
|
||||
def _read_tag_long(self):
|
||||
bytes = self._file.read(8)
|
||||
return struct.unpack(">q", bytes)[0]
|
||||
|
||||
def _read_tag_float(self):
|
||||
bytes = self._file.read(4)
|
||||
return struct.unpack(">f", bytes)[0]
|
||||
|
||||
def _read_tag_double(self):
|
||||
bytes = self._file.read(8)
|
||||
return struct.unpack(">d", bytes)[0]
|
||||
|
||||
def _read_tag_byte_array(self):
|
||||
length = self._read_tag_int()
|
||||
bytes = self._file.read(length)
|
||||
return bytes
|
||||
|
||||
def _read_tag_string(self):
|
||||
length = self._read_tag_short()
|
||||
|
||||
# Read the string
|
||||
string = self._file.read(length)
|
||||
|
||||
# decode it and return
|
||||
return string.decode("UTF-8")
|
||||
|
||||
def _read_tag_list(self):
|
||||
tagid = self._read_tag_byte()
|
||||
length = self._read_tag_int()
|
||||
|
||||
read_tagmap = {
|
||||
0: self._read_tag_end,
|
||||
1: self._read_tag_byte,
|
||||
2: self._read_tag_short,
|
||||
3: self._read_tag_int,
|
||||
4: self._read_tag_long,
|
||||
5: self._read_tag_float,
|
||||
6: self._read_tag_double,
|
||||
7: self._read_tag_byte_array,
|
||||
8: self._read_tag_string,
|
||||
9: self._read_tag_list,
|
||||
10:self._read_tag_compound,
|
||||
}
|
||||
|
||||
read_method = read_tagmap[tagid]
|
||||
l = []
|
||||
for _ in xrange(length):
|
||||
l.append(read_method())
|
||||
return l
|
||||
|
||||
def _read_tag_compound(self):
|
||||
# Build a dictionary of all the tag names mapping to their payloads
|
||||
tags = {}
|
||||
while True:
|
||||
# Read a tag
|
||||
tagtype = ord(self._file.read(1))
|
||||
|
||||
if tagtype == 0:
|
||||
break
|
||||
|
||||
name = self._read_tag_string()
|
||||
read_tagmap = {
|
||||
0: self._read_tag_end,
|
||||
1: self._read_tag_byte,
|
||||
2: self._read_tag_short,
|
||||
3: self._read_tag_int,
|
||||
4: self._read_tag_long,
|
||||
5: self._read_tag_float,
|
||||
6: self._read_tag_double,
|
||||
7: self._read_tag_byte_array,
|
||||
8: self._read_tag_string,
|
||||
9: self._read_tag_list,
|
||||
10:self._read_tag_compound,
|
||||
}
|
||||
payload = read_tagmap[tagtype]()
|
||||
|
||||
tags[name] = payload
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
|
||||
def read_all(self):
|
||||
"""Reads the entire file and returns (name, payload)
|
||||
name is the name of the root tag, and payload is a dictionary mapping
|
||||
names to their payloads
|
||||
|
||||
"""
|
||||
# Read tag type
|
||||
tagtype = ord(self._file.read(1))
|
||||
if tagtype != 10:
|
||||
raise Exception("Expected a tag compound")
|
||||
|
||||
# Read the tag name
|
||||
name = self._read_tag_string()
|
||||
|
||||
payload = self._read_tag_compound()
|
||||
|
||||
return name, payload
|
||||
|
||||
174
textures.py
Normal file
174
textures.py
Normal file
@@ -0,0 +1,174 @@
|
||||
import os
|
||||
import os.path
|
||||
import zipfile
|
||||
from cStringIO import StringIO
|
||||
import math
|
||||
|
||||
import numpy
|
||||
from PIL import Image, ImageEnhance
|
||||
|
||||
def _get_terrain_image():
|
||||
minecraftjar = zipfile.ZipFile(os.path.join(os.environ['HOME'], ".minecraft", "bin", "minecraft.jar"))
|
||||
textures = minecraftjar.open("terrain.png")
|
||||
buffer = StringIO(textures.read())
|
||||
return Image.open(buffer)
|
||||
|
||||
def _split_terrain(terrain):
|
||||
"""Builds and returns a length 256 array of each 16x16 chunk of texture"""
|
||||
textures = []
|
||||
for y in xrange(16):
|
||||
for x in xrange(16):
|
||||
left = x*16
|
||||
upper = y*16
|
||||
right = left+16
|
||||
lower = upper+16
|
||||
region = terrain.crop((left,upper,right,lower))
|
||||
textures.append(region)
|
||||
|
||||
return textures
|
||||
|
||||
# This maps terainids to 16x16 images
|
||||
terrain_images = _split_terrain(_get_terrain_image())
|
||||
|
||||
def _transform_image(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
|
||||
|
||||
"""
|
||||
|
||||
# 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.BILINEAR)
|
||||
|
||||
# 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):
|
||||
"""Takes an image and shears it for the left side of the cube (reflect for
|
||||
the right side)"""
|
||||
|
||||
# Size of the cube side before shear
|
||||
img = img.resize((12,12))
|
||||
|
||||
# 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 _build_texturemap():
|
||||
""""""
|
||||
t = terrain_images
|
||||
|
||||
# Notes are for things I've left out or will probably have to make special
|
||||
# exception for
|
||||
top = [-1,1,0,2,16,4,15,17,205,205,237,237,18,19,32,33,
|
||||
34,21,52,48,49,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, # Cloths are left out
|
||||
-1,-1,-1,64,64,13,12,29,28,23,22,6,6,7,8,35, # Gold/iron blocks? Doublestep? TNT from above?
|
||||
36,37,80,-1,65,4,25,101,98,24,43,-1,86,1,1,-1, # Torch from above? leaving out fire. Redstone wire? Crops left out. sign post
|
||||
-1,-1,128,16,-1,-1,-1,-1,-1,51,51,-1,-1,1,66,67, # door,ladder left out. Minecart rail orientation
|
||||
66,69,72,-1,74 # clay?
|
||||
]
|
||||
side = [-1,1,3,2,16,4,15,17,205,205,237,237,18,19,32,33,
|
||||
34,21,52,48,49,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,64,64,13,12,29,28,23,22,6,6,7,8,35,
|
||||
36,37,80,-1,65,4,25,101,98,24,43,-1,86,1,1,-1,
|
||||
-1,-1,128,16,-1,-1,-1,-1,-1,51,51,-1,-1,1,66,67,
|
||||
66,69,72,-1,74
|
||||
]
|
||||
side[2] = 2
|
||||
|
||||
return (
|
||||
[(t[x] if x != -1 else None) for x in top],
|
||||
[(_transform_image(t[x]) if x != -1 else None) for x in top],
|
||||
[(_transform_image_side(t[x]) if x != -1 else None) for x in side],
|
||||
)
|
||||
# texturemap maps block ids to a 16x16 image that goes on the top face
|
||||
# perspective_texturemap does the same, except the texture is rotated and shrunk
|
||||
# shear_texturemap maps block ids to the image that goes on the side of the
|
||||
# block, sheared appropriately
|
||||
texturemap, perspective_texturemap, shear_texturemap = _build_texturemap()
|
||||
|
||||
def _render_sprite(img):
|
||||
"""Takes a 16x16 sprite image, and returns a 22x22 image to go in the
|
||||
blockmap
|
||||
This is for rendering things that are sticking out of the ground, like
|
||||
flowers and such
|
||||
torches are drawn the same way, but torches that attach to walls are
|
||||
handled differently
|
||||
"""
|
||||
pass
|
||||
|
||||
def _render_ground_image(img):
|
||||
"""Takes a 16x16 sprite image and skews it to look like it's on the ground.
|
||||
This is for things like mine track and such
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
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 maps block id to the texture that goes on the side of the block
|
||||
allimages = []
|
||||
for top, side in zip(perspective_texturemap, shear_texturemap):
|
||||
if not top or not side:
|
||||
allimages.append(None)
|
||||
continue
|
||||
img = Image.new("RGBA", (24,24))
|
||||
|
||||
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)
|
||||
if 1:
|
||||
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)
|
||||
|
||||
# Copy on the left side
|
||||
img.paste(side, (0,6), side)
|
||||
# Copy on the other side
|
||||
img.paste(otherside, (12,6), otherside)
|
||||
# Copy on the top piece (last so it's on top)
|
||||
img.paste(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)))
|
||||
|
||||
allimages.append((img.convert("RGB"), img.split()[3]))
|
||||
return allimages
|
||||
|
||||
# Maps block images to the appropriate texture on each side. This map is not
|
||||
# appropriate for all block types
|
||||
blockmap = _build_blockimages()
|
||||
|
||||
140
world.py
Normal file
140
world.py
Normal file
@@ -0,0 +1,140 @@
|
||||
import functools
|
||||
import string
|
||||
import os
|
||||
import os.path
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import chunk
|
||||
|
||||
base36decode = functools.partial(int, base=36)
|
||||
|
||||
def base36encode(number):
|
||||
"""String repr of a number in base 32"""
|
||||
if number==0: return '0'
|
||||
alphabet = string.digits + string.lowercase
|
||||
|
||||
if number < 0:
|
||||
number = -number
|
||||
neg = True
|
||||
else:
|
||||
neg = False
|
||||
base36 = ''
|
||||
while number != 0:
|
||||
number, i = divmod(number, 36)
|
||||
base36 = alphabet[i] + base36
|
||||
|
||||
if neg:
|
||||
return "-"+base36
|
||||
else:
|
||||
return base36
|
||||
|
||||
def find_chunkfiles(worlddir):
|
||||
"""Returns a list of all the chunk file locations, and the file they
|
||||
correspond to"""
|
||||
all_chunks = []
|
||||
for dirpath, dirnames, filenames in os.walk(worlddir):
|
||||
if not dirnames and filenames:
|
||||
for f in filenames:
|
||||
p = f.split(".")
|
||||
all_chunks.append((base36decode(p[1]), base36decode(p[2]),
|
||||
os.path.join(dirpath, f)))
|
||||
return all_chunks
|
||||
|
||||
def render_world(worlddir):
|
||||
print "Scanning chunks..."
|
||||
all_chunks = find_chunkfiles(worlddir)
|
||||
|
||||
total = len(all_chunks)
|
||||
print "Done! {0} chunks found".format(total)
|
||||
if not total:
|
||||
return
|
||||
|
||||
# Create an image big enough for all chunks
|
||||
# Each chunk is 352 pixels across. Each chunk is vertically 1584 pixels,
|
||||
# but are spaced only 16*11=176 pixels apart.
|
||||
|
||||
# Imagine a diagonal coordinate system to address the chunks where
|
||||
# increasing x goes up-right and increasing z goes down-right. This needs
|
||||
# to be embedded in a square. How big is this square?
|
||||
|
||||
# Each column of chunks has a constant x+z sum of their coordinates, since
|
||||
# going from a chunk to the one below it involves adding 1 to z and
|
||||
# subtracting 1 from x. Therefore, the leftmost column is the one that
|
||||
# minimizes x+z. The rightmost column maximizes x+z
|
||||
|
||||
# This means the total width of the image is max sum - the min sum, times
|
||||
# the horizontal spacing between each neighboring chunk. Since the rows are
|
||||
# staggered, each row takes up half its actual width: 352/2
|
||||
|
||||
# Similarly, each row of chunks has a constant difference between their x
|
||||
# and z coordinate, since going from from a chunk to the one to its right
|
||||
# involves an addition of 1 to both x and z.
|
||||
|
||||
# So the total height of the image must be the max diff - the min diff,
|
||||
# times the vertical chunk spacing which is half of 16*11. Additionally,
|
||||
# 1584-8*11 must be added to the height for the rest of the bottom layer of
|
||||
# chunks.
|
||||
|
||||
# Furthermore, the chunks with the minimum z-x are placed on the image at
|
||||
# y=0 (in image coordinates, not chunk coordinates). The chunks with the
|
||||
# minimum x+z are placed on the image at x=0.
|
||||
|
||||
# Find the max and min sum and difference. Start out by finding the sum and
|
||||
# diff of the first chunk
|
||||
item = all_chunks[0]
|
||||
minsum = maxsum = item[0] + item[1]
|
||||
mindiff = maxdiff = item[1] - item[0]
|
||||
|
||||
for c in all_chunks:
|
||||
s = c[0] + c[1]
|
||||
minsum = min(minsum, s)
|
||||
maxsum = max(maxsum, s)
|
||||
d = c[1] - c[0]
|
||||
mindiff = min(mindiff, d)
|
||||
maxdiff = max(maxdiff, d)
|
||||
|
||||
width = (maxsum - minsum) * 384//2
|
||||
height = (maxdiff-mindiff) * 8*12 + (12*128-8*12)
|
||||
|
||||
print "Final image will be {0}x{1}. (That's {2} bytes!)".format(
|
||||
width, height, width*height*4)
|
||||
|
||||
# Oh god create a giant ass image
|
||||
worldimg = Image.new("RGBA", (width, height))
|
||||
|
||||
# Sort the chunks by their row, so when we loop through them it goes top to
|
||||
# bottom
|
||||
print "Sorting chunks..."
|
||||
all_chunks.sort(key=lambda x: x[1]-x[0])
|
||||
|
||||
print "Processing chunks!"
|
||||
processed = 0
|
||||
starttime = time.time()
|
||||
for chunkx, chunky, chunkfile in all_chunks:
|
||||
# Read in and render the chunk at world coordinates chunkx,chunky
|
||||
# Where should this chunk go on the image?
|
||||
column = chunkx + chunky - minsum
|
||||
row = chunky - chunkx - mindiff
|
||||
# col0 is at x=0. row0 is at y=0.
|
||||
# Each col adds 384/2. Each row adds 16*12/2
|
||||
imgx = 192 * column
|
||||
imgy = 96 * row
|
||||
|
||||
print "Drawing chunk {0},{1} at pos {2},{3}".format(
|
||||
chunkx, chunky,
|
||||
imgx, imgy)
|
||||
print "It's in column {0} row {1}".format(column, row)
|
||||
|
||||
# Read it and render
|
||||
chunk.chunk_render(chunkfile, worldimg, imgx, imgy, cave=True)
|
||||
# chunk chunk chunk chunk
|
||||
|
||||
processed += 1
|
||||
|
||||
print "{0}/{1} chunks rendered. Avg {2}s per chunk".format(processed, total,
|
||||
(time.time()-starttime)/processed)
|
||||
|
||||
print "All done!"
|
||||
return worldimg
|
||||
Reference in New Issue
Block a user