scan_chunks() works according to preliminary tests \o/
This commit is contained in:
@@ -462,6 +462,61 @@ class QuadtreeGen(object):
|
||||
if self.optimizeimg:
|
||||
optimize_image(imgpath, self.imgformat, self.optimizeimg)
|
||||
|
||||
def scan_chunks(self):
|
||||
"""Scans the chunks of the world object and produce an iterator over
|
||||
the tiles that need to be rendered.
|
||||
|
||||
"""
|
||||
|
||||
depth = self.p
|
||||
|
||||
dirty = DirtyTiles(depth)
|
||||
|
||||
# For each chunk, do this:
|
||||
# For each tile that the chunk touches, do this:
|
||||
# Compare the last modified time of the chunk and tile. If the
|
||||
# tile is older, mark it in a DirtyTiles object as dirty.
|
||||
#
|
||||
# IDEA: check last render time against mtime of the region to short
|
||||
# circuit checking mtimes of all chunks in a region
|
||||
for chunkx, chunky, chunkmtime in self.world.iterate_chunk_metadata():
|
||||
|
||||
chunkcol, chunkrow = self.world.convert_coords(chunkx, chunky)
|
||||
#logging.debug("Looking at chunk %s,%s", chunkcol, chunkrow)
|
||||
|
||||
# find tile coordinates
|
||||
tilex = chunkcol - chunkcol % 2
|
||||
tiley = chunkrow - chunkrow % 4
|
||||
|
||||
if chunkcol % 2 == 0:
|
||||
# This chunk is half-in one column and half-in another column.
|
||||
# tilex is the right one, also do tilex-2
|
||||
x_tiles = 2
|
||||
else:
|
||||
x_tiles = 1
|
||||
|
||||
# The tile at tilex,tiley obviously contains chunk, but so do the
|
||||
# next 4 tiles down because chunks are very tall
|
||||
for i in xrange(x_tiles):
|
||||
for j in xrange(5):
|
||||
tile = Tile.compute_path(tilex-2*i, tiley+4*j, depth)
|
||||
|
||||
tile_path = tile.get_filepath(self.full_tiledir, self.imgformat)
|
||||
try:
|
||||
tile_mtime = os.stat(tile_path)[stat.ST_MTIME]
|
||||
except OSError, e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
tile_mtime = 0
|
||||
#logging.debug("tile %s(%s) vs chunk %s,%s (%s)",
|
||||
# tile, tile_mtime, chunkcol, chunkrow, chunkmtime)
|
||||
if tile_mtime < chunkmtime:
|
||||
dirty.set_dirty(tile.path)
|
||||
#logging.debug(" Setting tile as dirty. Will render.")
|
||||
|
||||
# Now that we know which tiles need rendering, return an iterator over them
|
||||
return (Tile.from_path(tpath) for tpath in dirty.iterate_dirty())
|
||||
|
||||
|
||||
class DirtyTiles(object):
|
||||
"""This tree holds which tiles need rendering.
|
||||
@@ -503,15 +558,24 @@ class DirtyTiles(object):
|
||||
def set_dirty(self, path):
|
||||
"""Marks the requested leaf node as "dirty".
|
||||
|
||||
Path is a list of integers representing the path to the leaf node
|
||||
that is requested to be marked as dirty. Path must be presented in
|
||||
reverse order (leaf node at index 0, root node at index -1)
|
||||
Path is an iterable of integers representing the path to the leaf node
|
||||
that is requested to be marked as dirty.
|
||||
|
||||
"""
|
||||
path = list(path)
|
||||
assert len(path) == self.level
|
||||
path.reverse()
|
||||
self._set_dirty_helper(path)
|
||||
|
||||
def _set_dirty_helper(self, path):
|
||||
"""Recursive call for set_dirty()
|
||||
|
||||
Expects path to be a list in reversed order
|
||||
|
||||
If *all* the nodes below this one are dirty, this function returns
|
||||
true. Otherwise, returns None.
|
||||
|
||||
"""
|
||||
assert len(path) == self.level
|
||||
|
||||
if self.level == 1:
|
||||
# Base case
|
||||
@@ -522,8 +586,6 @@ class DirtyTiles(object):
|
||||
return True
|
||||
else:
|
||||
# Recursive case
|
||||
if not isinstance(path,list):
|
||||
path = list(path)
|
||||
|
||||
childnum = path.pop()
|
||||
child = self.children[childnum]
|
||||
@@ -531,14 +593,14 @@ class DirtyTiles(object):
|
||||
if child == False:
|
||||
# Create a new node
|
||||
child = self.__class__(self.level-1)
|
||||
child.set_dirty(path)
|
||||
child._set_dirty_helper(path)
|
||||
self.children[childnum] = child
|
||||
elif child == True:
|
||||
# Every child is already dirty. Nothing to do.
|
||||
return
|
||||
else:
|
||||
# subtree is mixed clean/dirty. Recurse
|
||||
ret = child.set_dirty(path)
|
||||
ret = child._set_dirty_helper(path)
|
||||
if ret:
|
||||
# Child says it's completely dirty, so we can purge the
|
||||
# subtree and mark it as dirty. The subtree will be garbage
|
||||
@@ -555,9 +617,10 @@ class DirtyTiles(object):
|
||||
yielded is a sequence of integers representing the quadtree path to the
|
||||
dirty tile. Yielded sequences are of length self.level.
|
||||
|
||||
Remember yielded paths are in reverse order. Leaf nodes at index 0!
|
||||
|
||||
"""
|
||||
return (reversed(rpath) for rpath in self._iterate_dirty_helper())
|
||||
|
||||
def _iterate_dirty_helper(self):
|
||||
if self.level == 1:
|
||||
# Base case
|
||||
if self.children[0]: yield [0]
|
||||
@@ -576,7 +639,7 @@ class DirtyTiles(object):
|
||||
yield x
|
||||
elif child != False:
|
||||
# Mixed dirty/clean down this subtree, recurse
|
||||
for path in child.iterate_dirty():
|
||||
for path in child._iterate_dirty_helper():
|
||||
path.append(c)
|
||||
yield path
|
||||
|
||||
@@ -620,6 +683,8 @@ class Tile(object):
|
||||
the tile and constructs a new tile object.
|
||||
|
||||
"""
|
||||
path = tuple(path)
|
||||
|
||||
depth = len(path)
|
||||
|
||||
# Radius of the world in chunk cols/rows
|
||||
|
||||
@@ -78,7 +78,6 @@ class World(object):
|
||||
# figure out chunk format is in use
|
||||
# if not mcregion, error out early
|
||||
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||
#print data
|
||||
if not ('version' in data and data['version'] == 19132):
|
||||
logging.error("Sorry, This version of Minecraft-Overviewer only works with the new McRegion chunk format")
|
||||
sys.exit(1)
|
||||
@@ -358,6 +357,20 @@ class World(object):
|
||||
elif self.north_direction == 'lower-left':
|
||||
return 0
|
||||
|
||||
def iterate_chunk_metadata(self):
|
||||
"""Returns an iterator over (x,y,chunk mtime) of every chunk loaded in
|
||||
memory. Provides a public way for external routines to iterate over the
|
||||
world.
|
||||
|
||||
Written for use in quadtree.py's QuadtreeGen.scan_chunks, which only
|
||||
needs chunk locations and mtimes.
|
||||
|
||||
"""
|
||||
|
||||
for regionx, regiony, _, mcr in self.regionfiles.itervalues():
|
||||
for chunkx, chunky in mcr.get_chunks():
|
||||
yield chunkx+32*regionx, chunky+32*regiony, mcr.get_chunk_timestamp(chunkx, chunky)
|
||||
|
||||
def _iterate_regionfiles(self,regionlist=None):
|
||||
"""Returns an iterator of all of the region files, along with their
|
||||
coordinates
|
||||
|
||||
Reference in New Issue
Block a user