0

preliminary chunk-scan algorithm activated.

forcerender and stochastic are currently broken.
This commit is contained in:
Andrew Brown
2011-11-07 22:18:51 -05:00
parent 636913af57
commit 13f3ba90ab

View File

@@ -218,7 +218,11 @@ class QuadtreeGen(object):
def get_chunks_in_range(self, colstart, colend, rowstart, rowend): def get_chunks_in_range(self, colstart, colend, rowstart, rowend):
"""Get chunks that are relevant to the tile rendering function that's """Get chunks that are relevant to the tile rendering function that's
rendering that range""" rendering that range
Returns a list of chunks where each item is
(col, row, chunkx, chunky, regionfilename)
"""
chunklist = [] chunklist = []
unconvert_coords = self.world.unconvert_coords unconvert_coords = self.world.unconvert_coords
#get_region_path = self.world.get_region_path #get_region_path = self.world.get_region_path
@@ -250,17 +254,14 @@ class QuadtreeGen(object):
return chunklist return chunklist
def get_worldtiles(self): def get_worldtiles(self):
"""Returns an iterator over the tiles of the most detailed layer """Returns an iterator over the tiles of the most detailed layer that
need to be rendered
""" """
for path in iterate_base4(self.p): # This quadtree object gets replaced by the caller in rendernode.py,
# Get the range for this tile # but we still have to let them know which quadtree this tile belongs
tile = Tile.from_path(path) # to. Hence returning both self and the tile.
return ((self, tile) for tile in self.scan_chunks())
# Put this in the batch to be submited to the pool.
# The quadtree object gets replaced by the caller in rendernode.py,
# but we still have to let them know which quadtree this tile
# belongs to.
yield [self, tile]
def get_innertiles(self,zoom): def get_innertiles(self,zoom):
"""Same as get_worldtiles but for the inntertile routine. """Same as get_worldtiles but for the inntertile routine.
@@ -340,15 +341,21 @@ class QuadtreeGen(object):
def render_worldtile(self, tile): def render_worldtile(self, tile, check_tile=False):
"""Renders the given tile. All the other relevant information is """Renders the given tile. All the other relevant information is
already stored in this quadtree object or in self.world. already stored in this quadtree object or in self.world.
This function is typically called in the child process. The tile is
assumed to need rendering unless the check_tile flag is given.
If check_tile is true, the mtimes of the chunk are compared with the
mtime of this tile and the tile is conditionally rendered.
The image is rendered and saved to disk in the place this quadtree is The image is rendered and saved to disk in the place this quadtree is
configured to store images. configured to store images.
If there are no chunks, this tile is not saved (if it already exists, it is If there are no chunks, this tile is not saved. If this is the case but
deleted) the tile exists, it is deleted
There is no return value There is no return value
""" """
@@ -371,19 +378,26 @@ class QuadtreeGen(object):
chunks = self.get_chunks_in_range(colstart, colend, rowstart, rowend) chunks = self.get_chunks_in_range(colstart, colend, rowstart, rowend)
world = self.world world = self.world
#stat the file, we need to know if it exists or it's mtime
try: tile_mtime = None
tile_mtime = os.stat(imgpath)[stat.ST_MTIME] if check_tile:
except OSError, e: #stat the file, we need to know if it exists or it's mtime
if e.errno != errno.ENOENT: try:
raise tile_mtime = os.stat(imgpath)[stat.ST_MTIME]
tile_mtime = None except OSError, e:
# ignore only if the error was "file not found"
if e.errno != errno.ENOENT:
raise
if not chunks: if not chunks:
# No chunks were found in this tile # No chunks were found in this tile
if tile_mtime is not None: try:
os.unlink(imgpath) os.unlink(imgpath)
return None except OSError, e:
# ignore only if the error was "file not found"
if e.errno != errno.ENOENT:
raise
return
# Create the directory if not exists # Create the directory if not exists
dirdest = os.path.dirname(imgpath) dirdest = os.path.dirname(imgpath)
@@ -391,58 +405,55 @@ class QuadtreeGen(object):
try: try:
os.makedirs(dirdest) os.makedirs(dirdest)
except OSError, e: except OSError, e:
# Ignore errno EEXIST: file exists. Since this is multithreaded, # Ignore errno EEXIST: file exists. Due to a race condition,
# two processes could conceivably try and create the same directory # two processes could conceivably try and create the same
# at the same time. # directory at the same time
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise raise
# check chunk mtimes to see if they are newer if check_tile:
try: # Look at all the chunks that touch this tile and their mtimes to
needs_rerender = False # determine if this tile actually needs rendering
get_region_mtime = world.get_region_mtime try:
needs_rerender = False
for col, row, chunkx, chunky, regionfile in chunks: get_region_mtime = world.get_region_mtime
region, regionMtime = get_region_mtime(regionfile)
# don't even check if it's not in the regionlist
if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
continue
# bail early if forcerender is set
if self.forcerender:
needs_rerender = True
break
# check region file mtime first. for col, row, chunkx, chunky, regionfile in chunks:
# on windows (and possibly elsewhere) minecraft won't update region, regionMtime = get_region_mtime(regionfile)
# the region file mtime until after shutdown.
# for servers this is unacceptable, so skip this check. # don't even check if it's not in the regionlist
#if regionMtime <= tile_mtime: if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
# continue continue
# checking chunk mtime # bail early if forcerender is set
if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime: if self.forcerender:
needs_rerender = True
break
# checking chunk mtime
if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
needs_rerender = True
break
# stochastic render check
if not needs_rerender and self.rerender_probability > 0.0 and random.uniform(0, 1) < self.rerender_probability:
needs_rerender = True needs_rerender = True
break
# if after all that, we don't need a rerender, return
# stochastic render check if not needs_rerender:
if not needs_rerender and self.rerender_probability > 0.0 and random.uniform(0, 1) < self.rerender_probability: return
needs_rerender = True except OSError:
# couldn't get tile mtime, skip check and assume it does
# if after all that, we don't need a rerender, return pass
if not needs_rerender:
return None
except OSError:
# couldn't get tile mtime, skip check
pass
# We have all the necessary info and this tile has passed the checks
# and should be rendered. So do it!
#logging.debug("writing out worldtile {0}".format(imgpath)) #logging.debug("writing out worldtile {0}".format(imgpath))
# Compile this image # Compile this image
tileimg = Image.new("RGBA", (width, height), self.bgcolor) tileimg = Image.new("RGBA", (width, height), self.bgcolor)
world = self.world
rendermode = self.rendermode rendermode = self.rendermode
# col colstart will get drawn on the image starting at x coordinates -(384/2) # col colstart will get drawn on the image starting at x coordinates -(384/2)
# row rowstart will get drawn on the image starting at y coordinates -(192/2) # row rowstart will get drawn on the image starting at y coordinates -(192/2)