Cache rotated chunk data
This results in a small performance gain for rotations, but still noticeably slower than with no rotation.
This commit is contained in:
64
chunk.py
64
chunk.py
@@ -70,21 +70,22 @@ def get_lvldata(world, filename, x, y, retries=2):
|
|||||||
if not d: raise NoSuchChunk(x,y)
|
if not d: raise NoSuchChunk(x,y)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_blockarray(level, north_direction):
|
def get_blockarray(level):
|
||||||
"""Takes the level struct as returned from get_lvldata, and returns the
|
"""Takes the level struct as returned from get_lvldata, and returns the
|
||||||
Block array, which just contains all the block ids"""
|
Block array, which just contains all the block ids"""
|
||||||
return numpy.rot90(numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), get_north_rotations(north_direction))
|
return level['Blocks']
|
||||||
|
|
||||||
def get_blockarray_fromfile(filename, north_direction):
|
def get_blockarray_fromfile(filename, north_direction):
|
||||||
"""Same as get_blockarray except takes a filename. This is a shortcut"""
|
"""Same as get_blockarray except takes a filename. This is a shortcut"""
|
||||||
|
#TODO Update this for configurable-north
|
||||||
d = nbt.load_from_region(filename, x, y, north_direction)
|
d = nbt.load_from_region(filename, x, y, north_direction)
|
||||||
level = d[1]['Level']
|
level = d[1]['Level']
|
||||||
return get_blockarray(level, north_direction)
|
return get_blockarray(level)
|
||||||
|
|
||||||
def get_skylight_array(level, north_direction):
|
def get_skylight_array(level):
|
||||||
"""Returns the skylight array. This is 4 bits per block, but it is
|
"""Returns the skylight array. This is 4 bits per block, but it is
|
||||||
expanded for you so you may index it normally."""
|
expanded for you so you may index it normally."""
|
||||||
skylight = numpy.rot90(numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction))
|
skylight = level['SkyLight']
|
||||||
# this array is 2 blocks per byte, so expand it
|
# this array is 2 blocks per byte, so expand it
|
||||||
skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
||||||
# Even elements get the lower 4 bits
|
# Even elements get the lower 4 bits
|
||||||
@@ -93,36 +94,26 @@ def get_skylight_array(level, north_direction):
|
|||||||
skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4
|
skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4
|
||||||
return skylight_expanded
|
return skylight_expanded
|
||||||
|
|
||||||
def get_blocklight_array(level, north_direction):
|
def get_blocklight_array(level):
|
||||||
"""Returns the blocklight array. This is 4 bits per block, but it
|
"""Returns the blocklight array. This is 4 bits per block, but it
|
||||||
is expanded for you so you may index it normally."""
|
is expanded for you so you may index it normally."""
|
||||||
# expand just like get_skylight_array()
|
# expand just like get_skylight_array()
|
||||||
blocklight = numpy.rot90(numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction))
|
blocklight = level['BlockLight']
|
||||||
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
||||||
blocklight_expanded[:,:,::2] = blocklight & 0x0F
|
blocklight_expanded[:,:,::2] = blocklight & 0x0F
|
||||||
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
|
blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4
|
||||||
return blocklight_expanded
|
return blocklight_expanded
|
||||||
|
|
||||||
def get_blockdata_array(level, north_direction):
|
def get_blockdata_array(level):
|
||||||
"""Returns the ancillary data from the 'Data' byte array. Data is packed
|
"""Returns the ancillary data from the 'Data' byte array. Data is packed
|
||||||
in a similar manner to skylight data"""
|
in a similar manner to skylight data"""
|
||||||
return numpy.rot90(numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction))
|
return level['Data']
|
||||||
|
|
||||||
def get_tileentity_data(level):
|
def get_tileentity_data(level):
|
||||||
"""Returns the TileEntities TAG_List from chunk dat file"""
|
"""Returns the TileEntities TAG_List from chunk dat file"""
|
||||||
data = level['TileEntities']
|
data = level['TileEntities']
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_north_rotations(north_direction):
|
|
||||||
if north_direction == "upper-left":
|
|
||||||
return 1
|
|
||||||
elif north_direction == "upper-right":
|
|
||||||
return 2
|
|
||||||
elif north_direction == "lower-right":
|
|
||||||
return 3
|
|
||||||
elif north_direction == "lower-left":
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# This set holds blocks ids that can be seen through, for occlusion calculations
|
# This set holds blocks ids that can be seen through, for occlusion calculations
|
||||||
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38,
|
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38,
|
||||||
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67,
|
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67,
|
||||||
@@ -148,14 +139,13 @@ class NoSuchChunk(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class ChunkRenderer(object):
|
class ChunkRenderer(object):
|
||||||
def __init__(self, chunkcoords, worldobj, rendermode, queue, north_direction):
|
def __init__(self, chunkcoords, worldobj, rendermode, queue):
|
||||||
"""Make a new chunk renderer for the given chunk coordinates.
|
"""Make a new chunk renderer for the given chunk coordinates.
|
||||||
chunkcoors should be a tuple: (chunkX, chunkY)
|
chunkcoors should be a tuple: (chunkX, chunkY)
|
||||||
|
|
||||||
cachedir is a directory to save the resulting chunk images to
|
cachedir is a directory to save the resulting chunk images to
|
||||||
"""
|
"""
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.north_direction = north_direction
|
|
||||||
|
|
||||||
self.regionfile = worldobj.get_region_path(*chunkcoords)
|
self.regionfile = worldobj.get_region_path(*chunkcoords)
|
||||||
#if not os.path.exists(self.regionfile):
|
#if not os.path.exists(self.regionfile):
|
||||||
@@ -192,21 +182,21 @@ class ChunkRenderer(object):
|
|||||||
def _load_blocks(self):
|
def _load_blocks(self):
|
||||||
"""Loads and returns the block array"""
|
"""Loads and returns the block array"""
|
||||||
if not hasattr(self, "_blocks"):
|
if not hasattr(self, "_blocks"):
|
||||||
self._blocks = get_blockarray(self._load_level(), self.north_direction)
|
self._blocks = get_blockarray(self._load_level())
|
||||||
return self._blocks
|
return self._blocks
|
||||||
blocks = property(_load_blocks)
|
blocks = property(_load_blocks)
|
||||||
|
|
||||||
def _load_skylight(self):
|
def _load_skylight(self):
|
||||||
"""Loads and returns skylight array"""
|
"""Loads and returns skylight array"""
|
||||||
if not hasattr(self, "_skylight"):
|
if not hasattr(self, "_skylight"):
|
||||||
self._skylight = get_skylight_array(self.level, self.north_direction)
|
self._skylight = get_skylight_array(self.level)
|
||||||
return self._skylight
|
return self._skylight
|
||||||
skylight = property(_load_skylight)
|
skylight = property(_load_skylight)
|
||||||
|
|
||||||
def _load_blocklight(self):
|
def _load_blocklight(self):
|
||||||
"""Loads and returns blocklight array"""
|
"""Loads and returns blocklight array"""
|
||||||
if not hasattr(self, "_blocklight"):
|
if not hasattr(self, "_blocklight"):
|
||||||
self._blocklight = get_blocklight_array(self.level, self.north_direction)
|
self._blocklight = get_blocklight_array(self.level)
|
||||||
return self._blocklight
|
return self._blocklight
|
||||||
blocklight = property(_load_blocklight)
|
blocklight = property(_load_blocklight)
|
||||||
|
|
||||||
@@ -215,9 +205,9 @@ class ChunkRenderer(object):
|
|||||||
chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY)
|
chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY)
|
||||||
try:
|
try:
|
||||||
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY)
|
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY)
|
||||||
self._left_skylight = get_skylight_array(chunk_data, self.north_direction)
|
self._left_skylight = get_skylight_array(chunk_data)
|
||||||
self._left_blocklight = get_blocklight_array(chunk_data, self.north_direction)
|
self._left_blocklight = get_blocklight_array(chunk_data)
|
||||||
self._left_blocks = get_blockarray(chunk_data, self.north_direction)
|
self._left_blocks = get_blockarray(chunk_data)
|
||||||
except NoSuchChunk:
|
except NoSuchChunk:
|
||||||
self._left_skylight = None
|
self._left_skylight = None
|
||||||
self._left_blocklight = None
|
self._left_blocklight = None
|
||||||
@@ -249,9 +239,9 @@ class ChunkRenderer(object):
|
|||||||
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1)
|
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1)
|
||||||
try:
|
try:
|
||||||
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1)
|
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1)
|
||||||
self._right_skylight = get_skylight_array(chunk_data, self.north_direction)
|
self._right_skylight = get_skylight_array(chunk_data)
|
||||||
self._right_blocklight = get_blocklight_array(chunk_data, self.north_direction)
|
self._right_blocklight = get_blocklight_array(chunk_data)
|
||||||
self._right_blocks = get_blockarray(chunk_data, self.north_direction)
|
self._right_blocks = get_blockarray(chunk_data)
|
||||||
except NoSuchChunk:
|
except NoSuchChunk:
|
||||||
self._right_skylight = None
|
self._right_skylight = None
|
||||||
self._right_blocklight = None
|
self._right_blocklight = None
|
||||||
@@ -283,9 +273,9 @@ class ChunkRenderer(object):
|
|||||||
chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY)
|
chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY)
|
||||||
try:
|
try:
|
||||||
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY)
|
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY)
|
||||||
self._up_right_skylight = get_skylight_array(chunk_data, self.north_direction)
|
self._up_right_skylight = get_skylight_array(chunk_data)
|
||||||
self._up_right_blocklight = get_blocklight_array(chunk_data, self.north_direction)
|
self._up_right_blocklight = get_blocklight_array(chunk_data)
|
||||||
self._up_right_blocks = get_blockarray(chunk_data, self.north_direction)
|
self._up_right_blocks = get_blockarray(chunk_data)
|
||||||
except NoSuchChunk:
|
except NoSuchChunk:
|
||||||
self._up_right_skylight = None
|
self._up_right_skylight = None
|
||||||
self._up_right_blocklight = None
|
self._up_right_blocklight = None
|
||||||
@@ -310,9 +300,9 @@ class ChunkRenderer(object):
|
|||||||
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1)
|
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1)
|
||||||
try:
|
try:
|
||||||
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1)
|
chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1)
|
||||||
self._up_left_skylight = get_skylight_array(chunk_data, self.north_direction)
|
self._up_left_skylight = get_skylight_array(chunk_data)
|
||||||
self._up_left_blocklight = get_blocklight_array(chunk_data, self.north_direction)
|
self._up_left_blocklight = get_blocklight_array(chunk_data)
|
||||||
self._up_left_blocks = get_blockarray(chunk_data, self.north_direction)
|
self._up_left_blocks = get_blockarray(chunk_data)
|
||||||
except NoSuchChunk:
|
except NoSuchChunk:
|
||||||
self._up_left_skylight = None
|
self._up_left_skylight = None
|
||||||
self._up_left_blocklight = None
|
self._up_left_blocklight = None
|
||||||
@@ -402,7 +392,7 @@ class ChunkRenderer(object):
|
|||||||
rendered, and blocks are drawn with a color tint depending on their
|
rendered, and blocks are drawn with a color tint depending on their
|
||||||
depth."""
|
depth."""
|
||||||
|
|
||||||
blockData = get_blockdata_array(self.level, self.north_direction)
|
blockData = get_blockdata_array(self.level)
|
||||||
blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
|
||||||
# Even elements get the lower 4 bits
|
# Even elements get the lower 4 bits
|
||||||
blockData_expanded[:,:,::2] = blockData & 0x0F
|
blockData_expanded[:,:,::2] = blockData & 0x0F
|
||||||
|
|||||||
5
nbt.py
5
nbt.py
@@ -210,13 +210,12 @@ class MCRFileReader(object):
|
|||||||
self._chunks = None
|
self._chunks = None
|
||||||
|
|
||||||
def get_north_rotations(self):
|
def get_north_rotations(self):
|
||||||
#Upper-left and lower-right are swapped from chunk.py rots
|
|
||||||
if self.north_direction == "upper-left":
|
if self.north_direction == "upper-left":
|
||||||
return 3
|
return 1
|
||||||
elif self.north_direction == "upper-right":
|
elif self.north_direction == "upper-right":
|
||||||
return 2
|
return 2
|
||||||
elif self.north_direction == "lower-right":
|
elif self.north_direction == "lower-right":
|
||||||
return 1
|
return 3
|
||||||
elif self.north_direction == "lower-left":
|
elif self.north_direction == "lower-left":
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ class QuadtreeGen(object):
|
|||||||
|
|
||||||
# draw the chunk!
|
# draw the chunk!
|
||||||
try:
|
try:
|
||||||
a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue, self.north_direction)
|
a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue)
|
||||||
a.chunk_render(tileimg, xpos, ypos, None)
|
a.chunk_render(tileimg, xpos, ypos, None)
|
||||||
except chunk.ChunkCorrupt:
|
except chunk.ChunkCorrupt:
|
||||||
# an error was already printed
|
# an error was already printed
|
||||||
|
|||||||
22
world.py
22
world.py
@@ -153,6 +153,18 @@ class World(object):
|
|||||||
data = nbt.read_all()
|
data = nbt.read_all()
|
||||||
level = data[1]['Level']
|
level = data[1]['Level']
|
||||||
chunk_data = level
|
chunk_data = level
|
||||||
|
chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer(
|
||||||
|
level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)),
|
||||||
|
self._get_north_rotations())
|
||||||
|
chunk_data['Data'] = numpy.rot90(numpy.frombuffer(
|
||||||
|
level['Data'], dtype=numpy.uint8).reshape((16,16,64)),
|
||||||
|
self._get_north_rotations())
|
||||||
|
chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer(
|
||||||
|
level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
||||||
|
self._get_north_rotations())
|
||||||
|
chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer(
|
||||||
|
level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)),
|
||||||
|
self._get_north_rotations())
|
||||||
#chunk_data = {}
|
#chunk_data = {}
|
||||||
#chunk_data['skylight'] = chunk.get_skylight_array(level)
|
#chunk_data['skylight'] = chunk.get_skylight_array(level)
|
||||||
#chunk_data['blocklight'] = chunk.get_blocklight_array(level)
|
#chunk_data['blocklight'] = chunk.get_blocklight_array(level)
|
||||||
@@ -276,6 +288,16 @@ class World(object):
|
|||||||
|
|
||||||
self.findTrueSpawn()
|
self.findTrueSpawn()
|
||||||
|
|
||||||
|
def _get_north_rotations(self):
|
||||||
|
if self.north_direction == "upper-left":
|
||||||
|
return 1
|
||||||
|
elif self.north_direction == "upper-right":
|
||||||
|
return 2
|
||||||
|
elif self.north_direction == "lower-right":
|
||||||
|
return 3
|
||||||
|
elif self.north_direction == "lower-left":
|
||||||
|
return 0
|
||||||
|
|
||||||
def _iterate_regionfiles(self,regionlist=None):
|
def _iterate_regionfiles(self,regionlist=None):
|
||||||
"""Returns an iterator of all of the region files, along with their
|
"""Returns an iterator of all of the region files, along with their
|
||||||
coordinates
|
coordinates
|
||||||
|
|||||||
Reference in New Issue
Block a user