0

Merge branch 'master' into rendermode-options

Conflicts:
	overviewer_core/chunk.py
	overviewer_core/src/iterate.c
This commit is contained in:
Aaron Griffith
2011-07-31 22:46:19 -04:00
13 changed files with 428 additions and 210 deletions

View File

@@ -41,6 +41,7 @@ feature.
* arrai <array.of.intellect@gmail.com>
* Kyle Brantley <kyle@averageurl.com>
* but2002 <barryt_9@hotmail.com>
* Eric Carr <eric@carr.no>
* cbarber <CraigBarber@taryx.com>
* Alex Cline <cline@vivisimo.com>

View File

@@ -4,6 +4,6 @@ include CONTRIBUTORS.rst
include overviewer.py
include sample.settings.py
recursive-include contrib/ *.py
recursive-include overviewer_core/*.py
recursive-include overviewer_core/ *.py
recursive-include overviewer_core/src/ *.c *.h
recursive-include overviewer_core/data/ *.png *.js index.html style.css
recursive-include overviewer_core/data/ *

View File

@@ -188,6 +188,11 @@ def main():
parser.print_help()
logging.error("Invalid world number")
sys.exit(1)
# final sanity check for worlddir
if not os.path.exists(os.path.join(worlddir, 'level.dat')):
logging.error("Invalid world path -- does not contain level.dat")
sys.exit(1)
if len(args) < 2:
if options.delete:

View File

@@ -114,10 +114,10 @@ def get_tileentity_data(level):
return data
# 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,
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85,
90, 92, 93, 94, 96])
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33,
34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79,
81, 83, 85, 90, 92, 93, 94, 96])
# This set holds block ids that are solid blocks
solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
@@ -335,67 +335,6 @@ class ChunkRenderer(object):
return self._up_left_blocklight
up_left_blocklight = property(_load_up_left_blocklight)
def generate_pseudo_ancildata(self,x,y,z,blockid, north_position = 0 ):
""" Generates a pseudo ancillary data for blocks that depend of
what are surrounded and don't have ancillary data
This uses a binary number of 4 digits to encode the info.
The encode is:
Bit: 1 2 3 4
Side: x y -x -y
Values: bit = 0 -> The corresponding side block has different blockid
bit = 1 -> The corresponding side block has same blockid
Example: if the bit1 is 1 that means that there is a block with
blockid in the side of the +x direction.
You can rotate the pseudo data multiplying by 2 and
if it is > 15 subtracting 15 and adding 1. (moving bits
in the left direction is like rotate 90 degree in anticlockwise
direction). In this way can be used for maps with other
north orientation.
North position can have the values 0, 1, 2, 3, corresponding to
north in bottom-left, bottom-right, top-right and top-left of
the screen.
The rotation feature is not used anywhere yet.
"""
blocks = self.blocks
up_left_blocks = self.up_left_blocks
up_right_blocks = self.up_right_blocks
left_blocks = self.left_blocks
right_blocks = self.right_blocks
pseudo_data = 0
# first check if we are in the border of a chunk, next check for chunks adjacent to this
# and finally check for a block with same blockid. I we aren't in the border of a chunk,
# check for the block having the sme blockid.
if (up_right_blocks is not None and up_right_blocks[0,y,z] == blockid) if x == 15 else blocks[x+1,y,z] == blockid:
pseudo_data = pseudo_data | 0b1000
if (right_blocks is not None and right_blocks[x,0,z] == blockid) if y == 15 else blocks[x,y + 1,z] == blockid:
pseudo_data = pseudo_data | 0b0100
if (left_blocks is not None and left_blocks[15,y,z] == blockid) if x == 0 else blocks[x - 1,y,z] == blockid:
pseudo_data = pseudo_data | 0b0010
if (up_left_blocks is not None and up_left_blocks[x,15,z] == blockid) if y == 0 else blocks[x,y - 1,z] == blockid:
pseudo_data = pseudo_data | 0b0001
# rotate the bits for other north orientations
while north_position > 0:
pseudo_data *= 2
if pseudo_data > 15:
pseudo_data -= 16
pseudo_data +=1
north_position -= 1
return pseudo_data
def chunk_render(self, img=None, xoff=0, yoff=0, cave=False):
"""Renders a chunk with the given parameters, and returns the image.
If img is given, the chunk is rendered to that image object. Otherwise,

View File

@@ -58,7 +58,6 @@ var overviewer = {
overviewer.util.initializeMarkers();
overviewer.util.initializeRegions();
overviewer.util.createMapControls();
overviewer.util.createSearchBox();
},
/**
* This adds some methods to these classes because Javascript is stupid
@@ -166,6 +165,9 @@ var overviewer = {
var zoom = overviewerConfig.map.defaultZoom;
var mapcenter;
var queryParams = overviewer.util.parseQueryString();
if (queryParams.debug) {
overviewerConfig.map.debug=true;
}
if (queryParams.lat) {
lat = parseFloat(queryParams.lat);
}
@@ -311,6 +313,9 @@ var overviewer = {
'title': jQuery.trim(item.msg),
'icon': overviewerConfig.CONST.image.queryMarker
});
google.maps.event.addListener(marker, 'click', function(){ marker.setVisible(false); });
continue;
}
@@ -392,59 +397,76 @@ var overviewer = {
point.x, point.y, point.z));
}
if (region.label) {
var name = region.label;
} else {
var name = "rawr";
}
if(region.opacity) {
var strokeOpacity = region.opacity;
var fillOpacity = region.opacity * 0.25;
} else {
var strokeOpacity = region.strokeOpacity;
var fillOpacity = region.fillOpacity;
}
var shapeOptions = {
'name': name,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'zIndex': j
};
if (region.closed) {
shapeOptions["fillColor"] = region.color;
shapeOptions["fillOpacity"] = fillOpacity;
shapeOptions["paths"] = converted;
} else {
shapeOptions["path"] = converted;
}
var matched = false;
for (k in overviewerConfig.objectGroups.regions) {
var regionGroup = overviewerConfig.objectGroups.regions[k];
var clickable = regionGroup.clickable;
var label = regionGroup.label;
if(region.label) {
var name = region.label
} else {
var name = 'rawr';
if (!regionGroup.match(region))
continue;
matched = true;
if (!region.label) {
clickable = false; // if it doesn't have a name, we dont have to show it.
}
if(region.opacity) {
var strokeOpacity = region.opacity;
var fillOpacity = region.opacity * 0.25;
if (region.closed) {
var shape = new google.maps.Polygon(shapeOptions);
} else {
var strokeOpacity = region.strokeOpacity;
var fillOpacity = region.fillOpacity;
var shape = new google.maps.Polyline(shapeOptions);
}
if (region.closed) {
var shape = new google.maps.Polygon({
'name': name,
'clickable': clickable,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'fillColor': region.color,
'fillOpacity': fillOpacity,
'zIndex': j,
'paths': converted
});
} else {
var shape = new google.maps.Polyline({
'name': name,
'clickable': clickable,
'geodesic': false,
'map': null,
'strokeColor': region.color,
'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'zIndex': j,
'path': converted
});
}
overviewer.collections.regions[label].push(shape);
if (clickable) {
overviewer.util.createRegionInfoWindow(shape);
}
}
// if we haven't matched anything, go ahead and add it
if (!matched) {
if (region.closed) {
var shape = new google.maps.Polygon(shapeOptions);
} else {
var shape = new google.maps.Polyline(shapeOptions);
}
shape.setMap(overviewer.map);
}
}
}
},
@@ -512,7 +534,7 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined,
// inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom));
Math.pow(2, overviewerConfig.map.zoomLevels));
// This information about where the center column is may change with
// a different drawing implementation -- check it again after any
@@ -520,13 +542,13 @@ var overviewer = {
// point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2)
// so the Y coordinate is at 0.5, and the X is at 0.5 -
// ((tileSize / 2) / (tileSize * 2^maxZoom))
// or equivalently, 0.5 - (1 / 2^(maxZoom + 1))
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
// ((tileSize / 2) / (tileSize * 2^zoomLevels))
// or equivalently, 0.5 - (1 / 2^(zoomLevels + 1))
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
var lat = 0.5;
// the following metrics mimic those in ChunkRenderer.chunk_render
// in "chunk.py" or, equivalently, chunk_render in src/iterate.c
// the following metrics mimic those in
// chunk_render in src/iterate.c
// each block on X axis adds 12px to x and subtracts 6px from y
lng += 12 * x * perPixel;
@@ -564,11 +586,11 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined,
// inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom));
Math.pow(2, overviewerConfig.map.zoomLevels));
// Revert base positioning
// See equivalent code in fromWorldToLatLng()
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
lat -= 0.5;
// I'll admit, I plugged this into Wolfram Alpha:
@@ -617,7 +639,10 @@ var overviewer = {
var coordsDiv = document.createElement('DIV');
coordsDiv.id = 'coordsDiv';
coordsDiv.innerHTML = '';
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(coordsDiv);
if (overviewerConfig.map.controls.coordsBox) {
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(coordsDiv);
}
// Update coords on mousemove
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng());
@@ -684,7 +709,7 @@ var overviewer = {
overviewer.util.createDropDown('Regions', items);
}
if (overviewer.collections.overlays.length > 0) {
if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) {
// overlay maps control
var items = [];
for (i in overviewer.collections.overlays) {
@@ -712,6 +737,9 @@ var overviewer = {
}
overviewer.util.createDropDown('Overlays', items);
}
// call out to create search box, as it's pretty complicated
overviewer.util.createSearchBox();
},
/**
* Reusable method for creating drop-down menus
@@ -832,8 +860,10 @@ var overviewer = {
$(searchDropDown).fadeOut();
}
});
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchControl);
if (overviewerConfig.map.controls.searchBox) {
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchControl);
}
},
/**
* Create the pop-up infobox for when you click on a region, this can't

View File

@@ -50,9 +50,18 @@ var overviewerConfig = {
*/
'mapType': true,
/**
* The small box at the bottom that displays the link to the current map view.
* The coordsBox control is the box showing the XYZ coordinates
* under the cursor.
*/
'link': true
'coordsBox': true,
/**
* The overlays control is the drop-down box for selecting overlays.
*/
'overlays': true,
/**
* The searchBox control is the search box for markers.
*/
'searchBox': true
},
/**
* The zoom level when the page is loaded without a specific zoom setting
@@ -66,6 +75,12 @@ var overviewerConfig = {
* This controls how close you can zoom in.
*/
'maxZoom': {maxzoom},
/**
* This tells us how many total zoom levels Overviewer rendered.
* DO NOT change this, even if you change minZoom and maxZoom, because
* it's used for marker position calculations and map resizing.
*/
'zoomLevels': {zoomlevels},
/**
* Center on this point, in world coordinates. Should be an array, ex:
* [0,0,0]

View File

@@ -114,6 +114,8 @@ class MapGen(object):
"{minzoom}", str(0))
config = config.replace(
"{maxzoom}", str(zoomlevel))
config = config.replace(
"{zoomlevels}", str(zoomlevel))
config = config.replace("{spawn_coords}",
json.dumps(list(self.world.spawn)))

View File

@@ -120,7 +120,7 @@ class QuadtreeGen(object):
indexfile = os.path.join(self.destdir, "overviewerConfig.js")
if not os.path.exists(indexfile):
return -1
matcher = re.compile(r"maxZoom.*:\s*(\d+)")
matcher = re.compile(r"zoomLevels(?:\'|\")\s*:\s*(\d+)")
p = -1
for line in open(indexfile, "r"):
res = matcher.search(line)
@@ -426,21 +426,21 @@ class QuadtreeGen(object):
needs_rerender = False
get_region_mtime = world.get_region_mtime
for col, row, chunkx, chunky, regionfile in chunks:
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.
region,regionMtime = get_region_mtime(regionfile)
if regionMtime <= tile_mtime:
continue
# 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
# checking chunk mtime
if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
needs_rerender = True

View File

@@ -173,12 +173,18 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f);
return data;
}
} else if (state->block == 20) { /* glass */
/* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */
if ((z != 127) && (getArrayByte3D(state->blocks, x, y, z+1) == 20)) {
data = 0;
} else {
data = 16;
}
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | data;
return data;
} else if (state->block == 85) { /* fences */
return check_adjacent_blocks(state, x, y, z, state->block);
} else if (state->block == 55) { /* redstone */
/* three addiotional bit are added, one for on/off state, and
* another two for going-up redstone wire in the same block
@@ -352,6 +358,10 @@ chunk_render(PyObject *self, PyObject *args) {
up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks");
state.up_right_blocks = up_right_blocks_py;
/* set up the random number generator again for each chunk
so tallgrass is in the same place, no matter what mode is used */
srand(1);
for (state.x = 15; state.x > -1; state.x--) {
for (state.y = 0; state.y < 16; state.y++) {
PyObject *blockid = NULL;
@@ -364,7 +374,7 @@ chunk_render(PyObject *self, PyObject *args) {
for (state.z = 0; state.z < 128; state.z++) {
state.imgy -= 12;
/* get blockid */
/* get blockid */
state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z);
if (state.block == 0) {
continue;
@@ -400,7 +410,10 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *tmp;
unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z);
if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) {
if ((state.block == 2) || (state.block == 9) ||
(state.block == 20) || (state.block == 54) ||
(state.block == 55) || (state.block == 85) ||
(state.block == 90)) {
ancilData = generate_pseudo_data(&state, ancilData);
}
@@ -419,14 +432,29 @@ chunk_render(PyObject *self, PyObject *args) {
if (t != NULL && t != Py_None)
{
PyObject *src, *mask, *mask_light;
int randx = 0, randy = 0;
src = PyTuple_GetItem(t, 0);
mask = PyTuple_GetItem(t, 1);
mask_light = PyTuple_GetItem(t, 2);
if (mask == Py_None)
mask = src;
if (state.block == 31) {
/* add a random offset to the postion of the tall grass to make it more wild */
randx = rand() % 6 + 1 - 3;
randy = rand() % 6 + 1 - 3;
state.imgx += randx;
state.imgy += randy;
}
render_mode_draw(rendermode, src, mask, mask_light);
if (state.block == 31) {
/* undo the random offsets */
state.imgx -= randx;
state.imgy -= randy;
}
}
}

View File

@@ -154,7 +154,6 @@ rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z)
static void
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeNormal *self = (RenderModeNormal *)data;
int randx = 0,randy = 0;
unsigned char data_byte;
/* first, check to see if we should use biome-compatible src, mask */
@@ -162,11 +161,6 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
if (state->block == 18) {
src = mask = self->leaf_texture;
} else if (state->block == 31) {
/* add a random offset to the postion of the tall grass to make it more wild */
randx = rand() % 6 + 1 - 3;
randy = rand() % 6 + 1 - 3;
state->imgx = state->imgx + randx;
state->imgy = state->imgy + randy;
data_byte = getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z);
if (data_byte == 1) {
src = mask = self->tall_grass_texture;

View File

@@ -28,7 +28,7 @@ import util
import composite
_find_file_local_path = None
def _find_file(filename, mode="rb"):
def _find_file(filename, mode="rb", verbose=False):
"""Searches for the given file and returns an open handle to it.
This searches the following locations in this order:
@@ -47,11 +47,13 @@ def _find_file(filename, mode="rb"):
if _find_file_local_path:
path = os.path.join(_find_file_local_path, filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
programdir = util.get_program_path()
path = os.path.join(programdir, filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
@@ -61,11 +63,13 @@ def _find_file(filename, mode="rb"):
# windows special case, when the package dir doesn't exist
path = os.path.join(programdir, "textures", filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
if sys.platform == "darwin":
path = os.path.join("/Applications/Minecraft", filename)
if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode)
# Find minecraft.jar.
@@ -85,6 +89,7 @@ def _find_file(filename, mode="rb"):
if os.path.exists(jarpath):
try:
jar = zipfile.ZipFile(jarpath)
if verbose: print "Found %s in '%s'" % (filename, jarpath)
return jar.open(filename)
except (KeyError, IOError):
pass
@@ -321,34 +326,34 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
A non transparent block uses top, side 3 and side 4.
If top is a tuple then first member is the top image and the second
member is an increment (integer) from 0 to 12. This increment will
used to crop the side images to look like a block and to paste all
the images increment pixels lower. Using increment = 6 will create
a half-block.
If top is a tuple then first item is the top image and the second
item is an increment (integer) from 0 to 16 (pixels in the
original minecraft texture). This increment will be used to crop the
side images and to paste the top image increment pixels lower, so if
you use an increment of 8, it willll draw a half-block.
NOTE: this method uses the top of the texture image (as done in
minecraft with beds)
NOTE: this method uses the bottom of the texture image (as done in
minecraft with beds and cackes)
"""
increment = 0
if isinstance(top, tuple):
increment = top[1]
crop_height = int(increment * 16./12.)
increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
crop_height = increment
top = top[0]
if side1 != None:
side1 = side1.copy()
ImageDraw.Draw(side1).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side1).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side2 != None:
side2 = side2.copy()
ImageDraw.Draw(side2).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side2).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side3 != None:
side3 = side3.copy()
ImageDraw.Draw(side3).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side3).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
if side4 != None:
side4 = side4.copy()
ImageDraw.Draw(side4).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(side4).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
img = Image.new("RGBA", (24,24), (38,92,255,0))
@@ -362,7 +367,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side1 = ImageEnhance.Brightness(side1).enhance(0.9)
side1.putalpha(sidealpha)
composite.alpha_over(img, side1, (0,0 + increment), side1)
composite.alpha_over(img, side1, (0,0), side1)
if side2 != None :
@@ -373,11 +378,11 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side2 = ImageEnhance.Brightness(side2).enhance(0.8)
side2.putalpha(sidealpha2)
composite.alpha_over(img, side2, (12,0 + increment), side2)
composite.alpha_over(img, side2, (12,0), side2)
if bottom != None :
bottom = transform_image(bottom, blockID)
composite.alpha_over(img, bottom, (0,12), top)
composite.alpha_over(img, bottom, (0,12), bottom)
# front sides
if side3 != None :
@@ -388,7 +393,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
side3.putalpha(sidealpha)
composite.alpha_over(img, side3, (0,6 + increment), side3)
composite.alpha_over(img, side3, (0,6), side3)
if side4 != None :
side4 = transform_image_side(side4, blockID)
@@ -399,7 +404,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
side4.putalpha(sidealpha)
composite.alpha_over(img, side4, (12,6 + increment), side4)
composite.alpha_over(img, side4, (12,6), side4)
if top != None :
top = transform_image(top, blockID)
@@ -420,7 +425,7 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
34, -1, 52, 48, 49,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4,
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
@@ -437,7 +442,7 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
34, -1, 52, 48, 49,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35,
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
@@ -548,33 +553,35 @@ def generate_special_texture(blockID, data):
return generate_texture_tuple(img, blockID)
if blockID == 9: # spring water, flowing water and waterfall water
watertexture = _load_image("water.png")
if blockID == 9 or blockID == 20: # spring water, flowing water and waterfall water, AND glass
# water and glass share the way to be rendered
if blockID == 9:
texture = _load_image("water.png")
else:
texture = terrain_images[49]
if (data & 0b10000) == 16:
top = watertexture
top = texture
else: top = None
if (data & 0b0001) == 1:
side1 = watertexture # top left
side1 = texture # top left
else: side1 = None
if (data & 0b1000) == 8:
side2 = watertexture # top right
side2 = texture # top right
else: side2 = None
if (data & 0b0010) == 2:
side3 = watertexture # bottom left
side3 = texture # bottom left
else: side3 = None
if (data & 0b0100) == 4:
side4 = watertexture # bottom right
side4 = texture # bottom right
else: side4 = None
img = _build_full_block(top,None,None,side3,side4)
return generate_texture_tuple(img, blockID)
@@ -600,7 +607,7 @@ def generate_special_texture(blockID, data):
if blockID == 26: # bed
increment = 5
increment = 8
left_face = None
right_face = None
if data & 0x8 == 0x8: # head of the bed
@@ -644,6 +651,7 @@ def generate_special_texture(blockID, data):
return generate_texture_tuple(img, blockID)
if blockID == 31: # tall grass
if data == 0: # dead shrub
texture = terrain_images[55]
@@ -656,8 +664,134 @@ def generate_special_texture(blockID, data):
img = _build_block(texture, texture, blockID)
return generate_texture_tuple(img,31)
if blockID in (29,33): # sticky and normal body piston.
if blockID == 29: # sticky
piston_t = terrain_images[106].copy()
else: # normal
piston_t = terrain_images[107].copy()
# other textures
side_t = terrain_images[108].copy()
back_t = terrain_images[109].copy()
interior_t = terrain_images[110].copy()
if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff
# remove piston texture from piston body
ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0))
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block(back_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x1: # up
img = _build_full_block((interior_t, 4) ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t , None, None ,side_t.rotate(90), back_t)
elif data & 0x07 == 0x3: # west
img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None)
temp = transform_image_side(interior_t, blockID)
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, temp, (9,5), temp)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270))
temp = transform_image_side(interior_t, blockID)
composite.alpha_over(img, temp, (3,5), temp)
elif data & 0x07 == 0x5: # south
img = _build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90))
else: # pushed in, normal full blocks, easy stuff
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block(back_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x1: # up
img = _build_full_block(piston_t ,None ,None ,side_t, side_t)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t)
elif data & 0x07 == 0x3: # west
img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270))
elif data & 0x07 == 0x5: # south
img = _build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90))
return generate_texture_tuple(img, blockID)
if blockID == 34: # piston extension (sticky and normal)
if (data & 0x8) == 0x8: # sticky
piston_t = terrain_images[106].copy()
else: # normal
piston_t = terrain_images[107].copy()
# other textures
side_t = terrain_images[108].copy()
back_t = terrain_images[107].copy()
# crop piston body
ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
# generate the horizontal piston extension stick
h_stick = Image.new("RGBA", (24,24), (38,92,255,0))
temp = transform_image_side(side_t, blockID)
composite.alpha_over(h_stick, temp, (1,7), temp)
temp = transform_image(side_t.rotate(90))
composite.alpha_over(h_stick, temp, (1,1), temp)
# Darken it
sidealpha = h_stick.split()[3]
h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85)
h_stick.putalpha(sidealpha)
# generate the vertical piston extension stick
v_stick = Image.new("RGBA", (24,24), (38,92,255,0))
temp = transform_image_side(side_t.rotate(90), blockID)
composite.alpha_over(v_stick, temp, (12,6), temp)
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(v_stick, temp, (1,6), temp)
# Darken it
sidealpha = v_stick.split()[3]
v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85)
v_stick.putalpha(sidealpha)
# Piston orientation is stored in the 3 first bits
if data & 0x07 == 0x0: # down
side_t = side_t.rotate(180)
img = _build_full_block((back_t, 12) ,None ,None ,side_t, side_t)
composite.alpha_over(img, v_stick, (0,-3), v_stick)
elif data & 0x07 == 0x1: # up
img = Image.new("RGBA", (24,24), (38,92,255,0))
img2 = _build_full_block(piston_t ,None ,None ,side_t, side_t)
composite.alpha_over(img, v_stick, (0,4), v_stick)
composite.alpha_over(img, img2, (0,0), img2)
elif data & 0x07 == 0x2: # east
img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), None)
temp = transform_image_side(back_t, blockID).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, temp, (2,2), temp)
composite.alpha_over(img, h_stick, (6,3), h_stick)
elif data & 0x07 == 0x3: # west
img = Image.new("RGBA", (24,24), (38,92,255,0))
img2 = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t)
composite.alpha_over(img, h_stick, (0,0), h_stick)
composite.alpha_over(img, img2, (0,0), img2)
elif data & 0x07 == 0x4: # north
img = _build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270))
composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT))
elif data & 0x07 == 0x5: # south
img = Image.new("RGBA", (24,24), (38,92,255,0))
img2 = _build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90))
temp = transform_image_side(back_t, blockID)
composite.alpha_over(img2, temp, (10,2), temp)
composite.alpha_over(img, img2, (0,0), img2)
composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT))
return generate_texture_tuple(img, blockID)
if blockID == 35: # wool
if data == 0: # white
top = side = terrain_images[64]
@@ -1076,7 +1210,7 @@ def generate_special_texture(blockID, data):
# mask out the high bits to figure out the orientation
img = Image.new("RGBA", (24,24), (38,92,255,0))
if (data & 0x03) == 0:
if (data & 0x03) == 0: # northeast corner
if not swung:
tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (0,6), tex)
@@ -1086,7 +1220,7 @@ def generate_special_texture(blockID, data):
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex)
if (data & 0x03) == 1:
if (data & 0x03) == 1: # southeast corner
if not swung:
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex)
@@ -1094,7 +1228,7 @@ def generate_special_texture(blockID, data):
tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (12,0), tex)
if (data & 0x03) == 2:
if (data & 0x03) == 2: # southwest corner
if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
composite.alpha_over(img, tex, (12,0), tex)
@@ -1102,7 +1236,7 @@ def generate_special_texture(blockID, data):
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex)
if (data & 0x03) == 3:
if (data & 0x03) == 3: # northwest corner
if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex)
@@ -1388,21 +1522,21 @@ def generate_special_texture(blockID, data):
img = Image.new("RGBA", (24,24), (38,92,255,0))
composite.alpha_over(img, side, (1,12), side)
composite.alpha_over(img, otherside, (11,13), otherside) # workaround, fixes a hole
composite.alpha_over(img, otherside, (12,12), otherside)
composite.alpha_over(img, side, (1,6), side)
composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole
composite.alpha_over(img, otherside, (12,6), otherside)
composite.alpha_over(img, top, (0,6), top)
return generate_texture_tuple(img, blockID)
if blockID in (93, 94): # redstone repeaters, ON and OFF
if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF
# NOTE: this function uses the redstone torches generated above,
# this must run after the function of the torches.
top = terrain_images[131] if blockID == 93 else terrain_images[147]
side = terrain_images[5]
increment = 9
increment = 13
if (data & 0x3) == 0: # pointing east
pass
@@ -1527,7 +1661,7 @@ def generate_special_texture(blockID, data):
img = _build_full_block(None, None, None, texture, None)
elif data & 0x4 == 0: # closed trapdoor
img = _build_full_block((texture, 9), None, None, texture, texture)
img = _build_full_block((texture, 12), None, None, texture, texture)
return generate_texture_tuple(img, blockID)
@@ -1604,9 +1738,10 @@ def getBiomeData(worlddir, chunkX, chunkY):
# (when adding new blocks here and in generate_special_textures,
# please, if possible, keep the ascending order of blockid value)
special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 31, 35, 43, 44,
50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66,
67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94, 96])
special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33,
34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62,
63, 64, 65, 66, 67, 68, 71, 75, 76, 85, 86, 90, 91,
92, 93, 94, 96])
# this is a map of special blockIDs to a list of all
# possible values for ancillary data that it might have.
@@ -1616,10 +1751,14 @@ special_map = {}
special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings)
special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data
special_map[17] = range(3) # wood: normal, birch and pine
special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data
special_map[26] = range(12) # bed, orientation
special_map[23] = range(6) # dispensers, orientation
special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered
special_map[28] = range(6) # detector rail, orientation/slope
special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out
special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out
special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal
special_map[35] = range(16) # wool, colored and white
special_map[43] = range(4) # stone, sandstone, wooden and cobblestone double-slab
special_map[44] = range(4) # stone, sandstone, wooden and cobblestone slab
@@ -1670,9 +1809,10 @@ biome_tall_fern_texture = None
biome_leaf_texture = None
specialblockmap = None
def generate(path=None):
global _find_file_local_path
def generate(path=None,texture_size=24):
global _find_file_local_path, texture_dimensions
_find_file_local_path = path
texture_dimensions = (texture_size, texture_size)
# This maps terainids to 16x16 images
global terrain_images
@@ -1696,3 +1836,30 @@ def generate(path=None):
for blockID in special_blocks:
for data in special_map[blockID]:
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data)
if texture_size != 24:
# rescale biome textures.
biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_leaf_texture = biome_leaf_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_tall_grass_texture = biome_tall_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
biome_tall_fern_texture = biome_tall_fern_texture.resize(texture_dimensions, Image.ANTIALIAS)
# rescale the normal block images
for i in range(len(blockmap)):
if blockmap[i] != None:
block = blockmap[i]
alpha = block[1]
block = block[0]
block.putalpha(alpha)
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
blockmap[i] = generate_texture_tuple(scaled_block, i)
# rescale the special block images
for blockid, data in iter(specialblockmap):
block = specialblockmap[(blockid,data)]
if block != None:
alpha = block[1]
block = block[0]
block.putalpha(alpha)
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid)

View File

@@ -209,25 +209,27 @@ class World(object):
chunkX = spawnX/16
chunkY = spawnZ/16
## The filename of this chunk
chunkFile = self.get_region_path(chunkX, chunkY)
if chunkFile is not None:
data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1]
if data is not None:
level = data['Level']
blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
try:
## The filename of this chunk
chunkFile = self.get_region_path(chunkX, chunkY)
if chunkFile is not None:
data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1]
if data is not None:
level = data['Level']
blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
## The block for spawn *within* the chunk
inChunkX = spawnX - (chunkX*16)
inChunkZ = spawnZ - (chunkY*16)
## The block for spawn *within* the chunk
inChunkX = spawnX - (chunkX*16)
inChunkZ = spawnZ - (chunkY*16)
## find the first air block
while (blockArray[inChunkX, inChunkZ, spawnY] != 0):
spawnY += 1
if spawnY == 128:
break
## find the first air block
while (blockArray[inChunkX, inChunkZ, spawnY] != 0):
spawnY += 1
if spawnY == 128:
break
except ChunkCorrupt:
#ignore corrupt spawn, and continue
pass
self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ,
msg="Spawn", type="spawn", chunk=(chunkX, chunkY)))
self.spawn = (spawnX, spawnY, spawnZ)

View File

@@ -6,6 +6,7 @@ from distutils.command.build import build
from distutils.command.clean import clean
from distutils.command.build_ext import build_ext
from distutils.command.sdist import sdist
from distutils.cmd import Command
from distutils.dir_util import remove_tree
from distutils.sysconfig import get_python_inc
from distutils import log
@@ -74,6 +75,17 @@ def recursive_data_files(src, dest=None):
ret.append((current_dest, current_sources))
return ret
# helper to create a 'package_data'-type sequence recursively for a given dir
def recursive_package_data(src, package_dir='overviewer_core'):
full_src = os.path.join(package_dir, src)
ret = []
for dirpath, dirnames, filenames in os.walk(full_src, topdown=False):
current_path = os.path.relpath(dirpath, package_dir)
for filename in filenames:
ret.append(os.path.join(current_path, filename))
return ret
#
# py2exe options
#
@@ -105,9 +117,8 @@ if py2app is not None:
setup_kwargs['packages'] = ['overviewer_core']
setup_kwargs['scripts'] = ['overviewer.py']
setup_kwargs['package_data'] = {'overviewer_core':
['data/textures/*',
'data/web_assets/*']}
setup_kwargs['package_data'] = {'overviewer_core': recursive_package_data('data/textures') + recursive_package_data('data/web_assets')}
if py2exe is None:
setup_kwargs['data_files'] = [('share/doc/minecraft-overviewer', doc_files)]
@@ -175,12 +186,15 @@ class CustomClean(clean):
pretty_fname)
versionpath = os.path.join("overviewer_core", "overviewer_version.py")
try:
if not self.dry_run:
os.remove(versionpath)
log.info("removing '%s'", versionpath)
except OSError:
log.warn("'%s' could not be cleaned -- permission denied", versionpath)
if os.path.exists(versionpath):
try:
if not self.dry_run:
os.remove(versionpath)
log.info("removing '%s'", versionpath)
except OSError:
log.warn("'%s' could not be cleaned -- permission denied", versionpath)
else:
log.debug("'%s' does not exist -- can't clean it", versionpath)
# now try to purge all *.pyc files
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")):
@@ -203,7 +217,7 @@ def generate_version_py():
f.write(outstr)
f.close()
except:
print "WARNING: failed to build overview_version file"
print "WARNING: failed to build overviewer_version file"
class CustomSDist(sdist):
def run(self):
@@ -232,11 +246,32 @@ class CustomBuildExt(build_ext):
self.inplace = True
build_ext.build_extensions(self)
class CheckTerrain(Command):
user_options=[]
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
from overviewer_core.textures import _find_file
import hashlib
import zipfile
print "checking..."
try:
f = _find_file("terrain.png", verbose=True)
except IOError:
log.error("Could not find the file terrain.png")
return
h = hashlib.sha1()
h.update(f.read())
log.info("Hash of terrain.png file is: %s", h.hexdigest())
setup_kwargs['cmdclass']['clean'] = CustomClean
setup_kwargs['cmdclass']['sdist'] = CustomSDist
setup_kwargs['cmdclass']['build'] = CustomBuild
setup_kwargs['cmdclass']['build_ext'] = CustomBuildExt
setup_kwargs['cmdclass']['check_terrain'] = CheckTerrain
###
setup(**setup_kwargs)