0

Merge remote-tracking branch 'upstream/master' into configurable-north

This commit is contained in:
Ryan Rector
2011-08-03 20:41:09 -06:00
49 changed files with 806 additions and 315 deletions

16
.gitignore vendored
View File

@@ -1,5 +1,8 @@
*.pyc *.pyc
build MANIFEST
build/
dist/
Minecraft_Overviewer.egg-info
terrain.png terrain.png
cachedir* cachedir*
@@ -14,10 +17,13 @@ ImPlatform.h
Imaging.h Imaging.h
# various forms of compiled c_overviewer extensions # various forms of compiled c_overviewer extensions
c_overviewer.so overviewer_core/c_overviewer.so
c_overviewer.pyd overviewer_core/c_overviewer.pyd
c_overviewer_d.pyd overviewer_core/c_overviewer_d.pyd
c_overviewer.dylib overviewer_core/c_overviewer.dylib
# generated version file
overviewer_core/overviewer_version.py
# Mac OS X noise # Mac OS X noise
.DS_Store .DS_Store

View File

@@ -4,7 +4,8 @@ Contributors
This file contains a list of every person who has contributed code to This file contains a list of every person who has contributed code to
Overviewer. It was created from the git commit log, and should include Overviewer. It was created from the git commit log, and should include
everyone, but we may have missed a few. everyone, but we may have missed a few and it is manually updated
now. If you feel like you've been left out, feel free to tell us!
Not currently included (but hopefully soon) are countless testers and bug Not currently included (but hopefully soon) are countless testers and bug
reporters that helped fixed the many bugs that have popped up in the course of reporters that helped fixed the many bugs that have popped up in the course of
@@ -40,14 +41,18 @@ feature.
* arrai <array.of.intellect@gmail.com> * arrai <array.of.intellect@gmail.com>
* Kyle Brantley <kyle@averageurl.com> * Kyle Brantley <kyle@averageurl.com>
* but2002 <barryt_9@hotmail.com>
* Eric Carr <eric@carr.no>
* cbarber <CraigBarber@taryx.com> * cbarber <CraigBarber@taryx.com>
* Alex Cline <cline@vivisimo.com> * Alex Cline <cline@vivisimo.com>
* Andrew Clunis <andrew@orospakr.ca>
* CounterPillow <spam@tes-cheese.ch> * CounterPillow <spam@tes-cheese.ch>
* Stephen Fluin <stephen@mistuph.com> * Stephen Fluin <stephen@mistuph.com>
* Benjamin Herr <ben@0x539.de> * Benjamin Herr <ben@0x539.de>
* Ryan Hitchman <hitchmanr@gmail.com> * Ryan Hitchman <hitchmanr@gmail.com>
* Jenny <jennytoo@gmail.com> * Jenny <jennytoo@gmail.com>
* Michael Jensen <emjay1988@gmail.com> * Michael Jensen <emjay1988@gmail.com>
* Maciej Małecki <maciej.malecki@hotmail.com>
* Ryan McCue <ryanmccue@cubegames.net> * Ryan McCue <ryanmccue@cubegames.net>
* Morlok8k <otis.spankmeyer@gmail.com> * Morlok8k <otis.spankmeyer@gmail.com>
* Gregory Short <gshort2@gmail.com> * Gregory Short <gshort2@gmail.com>

9
MANIFEST.in Normal file
View File

@@ -0,0 +1,9 @@
include COPYING.txt
include README.rst
include CONTRIBUTORS.rst
include overviewer.py
include sample.settings.py
recursive-include contrib/ *.py
recursive-include overviewer_core/ *.py
recursive-include overviewer_core/src/ *.c *.h
recursive-include overviewer_core/data/ *

View File

@@ -22,14 +22,13 @@ if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
import os import os
import os.path import os.path
from configParser import ConfigOptionParser
import re import re
import subprocess import subprocess
import multiprocessing import multiprocessing
import time import time
import logging import logging
import util
import platform import platform
from overviewer_core import util
logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s") logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s")
@@ -37,7 +36,7 @@ this_dir = util.get_program_path()
# make sure the c_overviewer extension is available # make sure the c_overviewer extension is available
try: try:
import c_overviewer from overviewer_core import c_overviewer
except ImportError: except ImportError:
## if this is a frozen windows package, the following error messages about ## if this is a frozen windows package, the following error messages about
## building the c_overviewer extension are not appropriate ## building the c_overviewer extension are not appropriate
@@ -49,7 +48,7 @@ except ImportError:
## try to find the build extension ## try to find the build extension
ext = os.path.join(this_dir, "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so")) ext = os.path.join(this_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so"))
if os.path.exists(ext): if os.path.exists(ext):
print "Something has gone wrong importing the c_overviewer extension. Please" print "Something has gone wrong importing the c_overviewer extension. Please"
print "make sure it is up-to-date (clean and rebuild)" print "make sure it is up-to-date (clean and rebuild)"
@@ -57,14 +56,17 @@ except ImportError:
print "You need to compile the c_overviewer module to run Minecraft Overviewer." print "You need to compile the c_overviewer module to run Minecraft Overviewer."
print "Run `python setup.py build`, or see the README for details." print "Run `python setup.py build`, or see the README for details."
import traceback
traceback.print_exc()
sys.exit(1) sys.exit(1)
if hasattr(sys, "frozen"): if hasattr(sys, "frozen"):
pass # we don't bother with a compat test since it should always be in sync pass # we don't bother with a compat test since it should always be in sync
elif "extension_version" in dir(c_overviewer): elif "extension_version" in dir(c_overviewer):
# check to make sure the binary matches the headers # check to make sure the binary matches the headers
if os.path.exists(os.path.join(this_dir, "src", "overviewer.h")): if os.path.exists(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")):
with open(os.path.join(this_dir, "src", "overviewer.h")) as f: with open(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")) as f:
lines = f.readlines() lines = f.readlines()
lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines) lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines)
if lines: if lines:
@@ -76,12 +78,10 @@ else:
print "Please rebuild your c_overviewer module. It is out of date!" print "Please rebuild your c_overviewer module. It is out of date!"
sys.exit(1) sys.exit(1)
from overviewer_core.configParser import ConfigOptionParser
from overviewer_core import optimizeimages, world, quadtree
from overviewer_core import googlemap, rendernode
import optimizeimages
import world
import quadtree
import googlemap
import rendernode
helptext = """ helptext = """
%prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir> %prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir>
@@ -126,14 +126,14 @@ def main():
if options.version: if options.version:
print "Minecraft-Overviewer"
print "Git version: %s" % util.findGitVersion()
try: try:
import overviewer_version import overviewer_core.overviewer_version as overviewer_version
if hasattr(sys, "frozen"): print "Minecraft-Overviewer %s" % overviewer_version.VERSION
print "py2exe version build on %s" % overviewer_version.BUILD_DATE print "Git commit: %s" % overviewer_version.HASH
print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS) print "built on %s" % overviewer_version.BUILD_DATE
print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS)
except: except:
print "version info not found"
pass pass
sys.exit(0) sys.exit(0)
@@ -178,6 +178,11 @@ def main():
parser.print_help() parser.print_help()
logging.error("Invalid world number") logging.error("Invalid world number")
sys.exit(1) 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 len(args) < 2:
if options.delete: if options.delete:

View File

View File

@@ -127,10 +127,10 @@ def get_tileentity_data(level):
return data return data
# 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, 29, 30, 31, 32, 33,
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64,
68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79,
90, 92, 93, 94, 96]) 81, 83, 85, 90, 92, 93, 94, 96])
# This set holds block ids that are solid blocks # 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, solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
@@ -334,67 +334,6 @@ class ChunkRenderer(object):
return self._up_left_skylight return self._up_left_skylight
up_left_skylight = property(_load_up_left_skylight) up_left_skylight = property(_load_up_left_skylight)
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): def chunk_render(self, img=None, xoff=0, yoff=0, cave=False):
"""Renders a chunk with the given parameters, and returns the image. """Renders a chunk with the given parameters, and returns the image.
If img is given, the chunk is rendered to that image object. Otherwise, If img is given, the chunk is rendered to that image object. Otherwise,

View File

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 563 B

View File

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 401 B

View File

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 672 B

View File

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 374 B

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -100,3 +100,34 @@ body {
background-color: #fff; /* fallback */ background-color: #fff; /* fallback */
background-color: rgba(255,255,255,0.8); background-color: rgba(255,255,255,0.8);
} }
#searchControl {
padding: 5px;
height: 20px;
font-family: Arial, sans-serif;
}
#searchControl > input {
border: 2px solid #000;
font-size: 12pt;
width: 20em;
background-colour: #fff;
}
div#searchDropDown {
border: 1px solid #000;
width: 17em;
font-size: 14pt;
background-color: #fff;
display: none;
}
div.searchResultItem {
overflow: hidden;
text-overflow: ellipsis;
}
div.searchResultItem img {
width: 24px;
height: 24px;
}

View File

@@ -96,6 +96,25 @@ var overviewer = {
return div; return div;
}; };
}, },
/**
* Quote an arbitrary string for use in a regex matcher.
* WTB parametized regexes, JavaScript...
*
* From http://kevin.vanzonneveld.net
* original by: booeyOH
* improved by: Ates Goral (http://magnetiq.com)
* improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
* bugfixed by: Onno Marsman
* example 1: preg_quote("$40");
* returns 1: '\$40'
* example 2: preg_quote("*RRRING* Hello?");
* returns 2: '\*RRRING\* Hello\?'
* example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
* returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
*/
"pregQuote": function(str) {
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
},
/** /**
* Setup the varous mapTypes before we actually create the map. This used * Setup the varous mapTypes before we actually create the map. This used
* to be a bunch of crap down at the bottom of functions.js * to be a bunch of crap down at the bottom of functions.js
@@ -146,6 +165,9 @@ var overviewer = {
var zoom = overviewerConfig.map.defaultZoom; var zoom = overviewerConfig.map.defaultZoom;
var mapcenter; var mapcenter;
var queryParams = overviewer.util.parseQueryString(); var queryParams = overviewer.util.parseQueryString();
if (queryParams.debug) {
overviewerConfig.map.debug=true;
}
if (queryParams.lat) { if (queryParams.lat) {
lat = parseFloat(queryParams.lat); lat = parseFloat(queryParams.lat);
} }
@@ -291,6 +313,9 @@ var overviewer = {
'title': jQuery.trim(item.msg), 'title': jQuery.trim(item.msg),
'icon': overviewerConfig.CONST.image.queryMarker 'icon': overviewerConfig.CONST.image.queryMarker
}); });
google.maps.event.addListener(marker, 'click', function(){ marker.setVisible(false); });
continue; continue;
} }
@@ -317,6 +342,7 @@ var overviewer = {
'icon': iconURL, 'icon': iconURL,
'visible': false 'visible': false
}); });
item.marker = marker;
overviewer.util.debug(label); overviewer.util.debug(label);
overviewer.collections.markers[label].push(marker); overviewer.collections.markers[label].push(marker);
if (item.type == 'sign') { if (item.type == 'sign') {
@@ -339,6 +365,7 @@ var overviewer = {
'icon': iconURL, 'icon': iconURL,
'visible': false 'visible': false
}); });
item.marker = marker;
if (overviewer.collections.markers['__others__']) { if (overviewer.collections.markers['__others__']) {
overviewer.collections.markers['__others__'].push(marker); overviewer.collections.markers['__others__'].push(marker);
} else { } else {
@@ -370,59 +397,76 @@ var overviewer = {
point.x, point.y, point.z)); 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) { for (k in overviewerConfig.objectGroups.regions) {
var regionGroup = overviewerConfig.objectGroups.regions[k]; var regionGroup = overviewerConfig.objectGroups.regions[k];
var clickable = regionGroup.clickable; var clickable = regionGroup.clickable;
var label = regionGroup.label; var label = regionGroup.label;
if(region.label) { if (!regionGroup.match(region))
var name = region.label continue;
} else { matched = true;
var name = 'rawr';
if (!region.label) {
clickable = false; // if it doesn't have a name, we dont have to show it. clickable = false; // if it doesn't have a name, we dont have to show it.
} }
if(region.opacity) { if (region.closed) {
var strokeOpacity = region.opacity; var shape = new google.maps.Polygon(shapeOptions);
var fillOpacity = region.opacity * 0.25;
} else { } else {
var strokeOpacity = region.strokeOpacity; var shape = new google.maps.Polyline(shapeOptions);
var fillOpacity = region.fillOpacity;
} }
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); overviewer.collections.regions[label].push(shape);
if (clickable) { if (clickable) {
overviewer.util.createRegionInfoWindow(shape); 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);
}
} }
} }
}, },
@@ -490,7 +534,7 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined, // the width and height of all the highest-zoom tiles combined,
// inverted // inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom)); Math.pow(2, overviewerConfig.map.zoomLevels));
if(overviewerConfig.map.north_direction == 'upper-right'){ if(overviewerConfig.map.north_direction == 'upper-right'){
x = -x-1; x = -x-1;
@@ -511,13 +555,13 @@ var overviewer = {
// point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2) // 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 - // so the Y coordinate is at 0.5, and the X is at 0.5 -
// ((tileSize / 2) / (tileSize * 2^maxZoom)) // ((tileSize / 2) / (tileSize * 2^zoomLevels))
// or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) // or equivalently, 0.5 - (1 / 2^(zoomLevels + 1))
lat = 0.5; var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); var lat = 0.5;
// the following metrics mimic those in ChunkRenderer.chunk_render // the following metrics mimic those in
// in "chunk.py" or, equivalently, chunk_render in src/iterate.c // chunk_render in src/iterate.c
// each block on X axis adds 12px to x and subtracts 6px from y // each block on X axis adds 12px to x and subtracts 6px from y
lng += 12 * x * perPixel; lng += 12 * x * perPixel;
@@ -555,14 +599,13 @@ var overviewer = {
// the width and height of all the highest-zoom tiles combined, // the width and height of all the highest-zoom tiles combined,
// inverted // inverted
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
Math.pow(2, overviewerConfig.map.maxZoom)); Math.pow(2, overviewerConfig.map.zoomLevels));
// Revert base positioning // Revert base positioning
// See equivalent code in fromWorldToLatLng() // See equivalent code in fromWorldToLatLng()
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
lat -= 0.5; lat -= 0.5;
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
// I'll admit, I plugged this into Wolfram Alpha: // I'll admit, I plugged this into Wolfram Alpha:
// a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r)
// And I don't know the math behind solving for for X and Z given // And I don't know the math behind solving for for X and Z given
@@ -622,7 +665,10 @@ var overviewer = {
var coordsDiv = document.createElement('DIV'); var coordsDiv = document.createElement('DIV');
coordsDiv.id = 'coordsDiv'; coordsDiv.id = 'coordsDiv';
coordsDiv.innerHTML = ''; 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 // Update coords on mousemove
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) { google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng()); var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng());
@@ -689,7 +735,7 @@ var overviewer = {
overviewer.util.createDropDown('Regions', items); overviewer.util.createDropDown('Regions', items);
} }
if (overviewer.collections.overlays.length > 0) { if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) {
// overlay maps control // overlay maps control
var items = []; var items = [];
for (i in overviewer.collections.overlays) { for (i in overviewer.collections.overlays) {
@@ -717,6 +763,9 @@ var overviewer = {
} }
overviewer.util.createDropDown('Overlays', items); 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 * Reusable method for creating drop-down menus
@@ -785,6 +834,63 @@ var overviewer = {
itemDiv.appendChild(textNode); itemDiv.appendChild(textNode);
} }
}, },
/**
* Create search box and dropdown in the top right google maps area.
*/
'createSearchBox': function() {
var searchControl = document.createElement("div");
searchControl.id = "searchControl";
var searchInput = document.createElement("input");
searchInput.type = "text";
searchControl.appendChild(searchInput);
var searchDropDown = document.createElement("div");
searchDropDown.id = "searchDropDown";
searchControl.appendChild(searchDropDown);
$(searchInput).keyup(function(e) {
var newline_stripper = new RegExp("[\\r\\n]", "g")
if(searchInput.value.length !== 0) {
$(searchDropDown).fadeIn();
$(searchDropDown).empty();
overviewer.collections.markerDatas.forEach(function(marker_list) {
marker_list.forEach(function(sign) {
var regex = new RegExp(overviewer.util.pregQuote(searchInput.value), "mi");
if(sign.msg.match(regex)) {
if(sign.marker !== undefined && sign.marker.getVisible()) {
var t = document.createElement("div");
t.className = "searchResultItem";
var i = document.createElement("img");
i.src = sign.marker.getIcon();
t.appendChild(i);
var s = document.createElement("span");
$(s).text(sign.msg.replace(newline_stripper, ""));
t.appendChild(s);
searchDropDown.appendChild(t);
$(t).click(function(e) {
$(searchDropDown).fadeOut();
overviewer.map.setZoom(7);
overviewer.map.setCenter(sign.marker.getPosition());
});
}
}
});
});
} else {
$(searchDropDown).fadeOut();
}
});
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 * Create the pop-up infobox for when you click on a region, this can't
* be done in-line because of stupid Javascript scoping problems with * be done in-line because of stupid Javascript scoping problems with

View File

@@ -50,9 +50,18 @@ var overviewerConfig = {
*/ */
'mapType': true, '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 * 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. * This controls how close you can zoom in.
*/ */
'maxZoom': {maxzoom}, '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: * Center on this point, in world coordinates. Should be an array, ex:
* [0,0,0] * [0,0,0]

View File

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 368 B

View File

Before

Width:  |  Height:  |  Size: 708 B

After

Width:  |  Height:  |  Size: 708 B

View File

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -24,6 +24,7 @@ import json
import util import util
from c_overviewer import get_render_mode_inheritance from c_overviewer import get_render_mode_inheritance
import overviewer_version
""" """
This module has routines related to generating a Google Maps-based This module has routines related to generating a Google Maps-based
@@ -52,10 +53,15 @@ def mirror_dir(src, dst, entities=None):
elif os.path.isfile(os.path.join(src,entry)): elif os.path.isfile(os.path.join(src,entry)):
try: try:
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry)) shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
except IOError: except IOError as outer:
# maybe permission problems? try:
os.chmod(os.path.join(src, entry), stat.S_IRUSR) # maybe permission problems?
os.chmod(os.path.join(dst, entry), stat.S_IWUSR) src_stat = os.stat(os.path.join(src, entry))
os.chmod(os.path.join(src, entry), src_stat.st_mode | stat.S_IRUSR)
dst_stat = os.stat(os.path.join(dst, entry))
os.chmod(os.path.join(dst, entry), dst_stat.st_mode | stat.S_IWUSR)
except OSError: # we don't care if this fails
pass
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry)) shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
# if this stills throws an error, let it propagate up # if this stills throws an error, let it propagate up
@@ -99,7 +105,11 @@ class MapGen(object):
blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat)) blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat))
# copy web assets into destdir: # copy web assets into destdir:
mirror_dir(os.path.join(util.get_program_path(), "web_assets"), self.destdir) global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
if not os.path.isdir(global_assets):
global_assets = os.path.join(util.get_program_path(), "web_assets")
mirror_dir(global_assets, self.destdir)
# do the same with the local copy, if we have it # do the same with the local copy, if we have it
if self.web_assets_path: if self.web_assets_path:
mirror_dir(self.web_assets_path, self.destdir) mirror_dir(self.web_assets_path, self.destdir)
@@ -110,6 +120,8 @@ class MapGen(object):
"{minzoom}", str(0)) "{minzoom}", str(0))
config = config.replace( config = config.replace(
"{maxzoom}", str(zoomlevel)) "{maxzoom}", str(zoomlevel))
config = config.replace(
"{zoomlevels}", str(zoomlevel))
config = config.replace( config = config.replace(
"{north_direction}", self.north_direction) "{north_direction}", self.north_direction)
@@ -134,9 +146,9 @@ class MapGen(object):
indexpath = os.path.join(self.destdir, "index.html") indexpath = os.path.join(self.destdir, "index.html")
index = open(indexpath, 'r').read() index = open(indexpath, 'r').read()
index = index.replace( index = index.replace("{time}", str(strftime("%a, %d %b %Y %H:%M:%S %Z", localtime())))
"{time}", str(strftime("%a, %d %b %Y %H:%M:%S %Z", localtime()))) versionstr = "%s (%s)" % (overviewer_version.VERSION, overviewer_version.HASH[:7])
index = index.replace("{version}", util.findGitVersion()) index = index.replace("{version}", versionstr)
with open(os.path.join(self.destdir, "index.html"), 'w') as output: with open(os.path.join(self.destdir, "index.html"), 'w') as output:
output.write(index) output.write(index)

View File

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

View File

@@ -133,8 +133,8 @@ class RenderNode(object):
else: else:
if not complete % 1000 == 0: if not complete % 1000 == 0:
return return
logging.info("{0}/{1} tiles complete on level {2}/{3}".format( logging.info("{0}/{1} ({4}%) tiles complete on level {2}/{3}".format(
complete, total, level, self.max_p)) complete, total, level, self.max_p, '%.1f' % ( (100.0 * complete) / total) ))
def go(self, procs): def go(self, procs):
"""Renders all tiles""" """Renders all tiles"""

View File

@@ -33,13 +33,13 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) {
return NULL; return NULL;
} }
textures = PyImport_ImportModule("textures"); textures = PyImport_ImportModule("overviewer_core.textures");
/* ensure none of these pointers are NULL */ /* ensure none of these pointers are NULL */
if ((!textures)) { if ((!textures)) {
return NULL; return NULL;
} }
chunk_mod = PyImport_ImportModule("chunk"); chunk_mod = PyImport_ImportModule("overviewer_core.chunk");
/* ensure none of these pointers are NULL */ /* ensure none of these pointers are NULL */
if ((!chunk_mod)) { if ((!chunk_mod)) {
return NULL; return NULL;
@@ -174,12 +174,18 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f); data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f);
return data; 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 */ } else if (state->block == 85) { /* fences */
return check_adjacent_blocks(state, x, y, z, state->block); return check_adjacent_blocks(state, x, y, z, state->block);
} else if (state->block == 55) { /* redstone */ } else if (state->block == 55) { /* redstone */
/* three addiotional bit are added, one for on/off state, and /* three addiotional bit are added, one for on/off state, and
* another two for going-up redstone wire in the same block * another two for going-up redstone wire in the same block
@@ -354,6 +360,10 @@ chunk_render(PyObject *self, PyObject *args) {
up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks"); up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks");
state.up_right_blocks = up_right_blocks_py; 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.x = 15; state.x > -1; state.x--) {
for (state.y = 0; state.y < 16; state.y++) { for (state.y = 0; state.y < 16; state.y++) {
PyObject *blockid = NULL; PyObject *blockid = NULL;
@@ -366,7 +376,7 @@ chunk_render(PyObject *self, PyObject *args) {
for (state.z = 0; state.z < 128; state.z++) { for (state.z = 0; state.z < 128; state.z++) {
state.imgy -= 12; state.imgy -= 12;
/* get blockid */ /* get blockid */
state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z); state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z);
if (state.block == 0) { if (state.block == 0) {
continue; continue;
@@ -402,7 +412,10 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *tmp; PyObject *tmp;
unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); 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); ancilData = generate_pseudo_data(&state, ancilData);
} }
@@ -421,14 +434,29 @@ chunk_render(PyObject *self, PyObject *args) {
if (t != NULL && t != Py_None) if (t != NULL && t != Py_None)
{ {
PyObject *src, *mask, *mask_light; PyObject *src, *mask, *mask_light;
int randx = 0, randy = 0;
src = PyTuple_GetItem(t, 0); src = PyTuple_GetItem(t, 0);
mask = PyTuple_GetItem(t, 1); mask = PyTuple_GetItem(t, 1);
mask_light = PyTuple_GetItem(t, 2); mask_light = PyTuple_GetItem(t, 2);
if (mask == Py_None) if (mask == Py_None)
mask = src; 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;
}
rendermode->draw(rm_data, &state, src, mask, mask_light); rendermode->draw(rm_data, &state, src, mask, mask_light);
if (state.block == 31) {
/* undo the random offsets */
state.imgx -= randx;
state.imgy -= randy;
}
} }
} }

View File

@@ -125,7 +125,6 @@ rendermode_normal_occluded(void *data, RenderState *state) {
static void static void
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeNormal *self = (RenderModeNormal *)data; RenderModeNormal *self = (RenderModeNormal *)data;
int randx = 0,randy = 0;
unsigned char data_byte; unsigned char data_byte;
/* first, check to see if we should use biome-compatible src, mask */ /* first, check to see if we should use biome-compatible src, mask */
@@ -133,11 +132,6 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
if (state->block == 18) { if (state->block == 18) {
src = mask = self->leaf_texture; src = mask = self->leaf_texture;
} else if (state->block == 31) { } 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); data_byte = getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z);
if (data_byte == 1) { if (data_byte == 1) {
src = mask = self->tall_grass_texture; src = mask = self->tall_grass_texture;

View File

@@ -14,6 +14,7 @@
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>. # with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
import sys import sys
import imp
import os import os
import os.path import os.path
import zipfile import zipfile
@@ -27,13 +28,13 @@ import util
import composite import composite
_find_file_local_path = None _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. """Searches for the given file and returns an open handle to it.
This searches the following locations in this order: This searches the following locations in this order:
* the textures_path given in the config file (if present) * the textures_path given in the config file (if present)
* The program dir (same dir as this file) * The program dir (same dir as overviewer.py)
* The program dir / textures * The overviewer_core textures dir
* On Darwin, in /Applications/Minecraft * On Darwin, in /Applications/Minecraft
* Inside minecraft.jar, which is looked for at these locations * Inside minecraft.jar, which is looked for at these locations
@@ -46,20 +47,29 @@ def _find_file(filename, mode="rb"):
if _find_file_local_path: if _find_file_local_path:
path = os.path.join(_find_file_local_path, filename) path = os.path.join(_find_file_local_path, filename)
if os.path.exists(path): if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode) return open(path, mode)
programdir = util.get_program_path() programdir = util.get_program_path()
path = os.path.join(programdir, filename) path = os.path.join(programdir, filename)
if os.path.exists(path): if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode) return open(path, mode)
path = os.path.join(programdir, "textures", filename) path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
if os.path.exists(path): if os.path.exists(path):
return open(path, mode) return open(path, mode)
elif hasattr(sys, "frozen") or imp.is_frozen("__main__"):
# 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": if sys.platform == "darwin":
path = os.path.join("/Applications/Minecraft", filename) path = os.path.join("/Applications/Minecraft", filename)
if os.path.exists(path): if os.path.exists(path):
if verbose: print "Found %s in '%s'" % (filename, path)
return open(path, mode) return open(path, mode)
# Find minecraft.jar. # Find minecraft.jar.
@@ -79,6 +89,7 @@ def _find_file(filename, mode="rb"):
if os.path.exists(jarpath): if os.path.exists(jarpath):
try: try:
jar = zipfile.ZipFile(jarpath) jar = zipfile.ZipFile(jarpath)
if verbose: print "Found %s in '%s'" % (filename, jarpath)
return jar.open(filename) return jar.open(filename)
except (KeyError, IOError): except (KeyError, IOError):
pass pass
@@ -151,13 +162,13 @@ def transform_image_side(img, blockID=None):
# (don't just crop img, since we want the size of # (don't just crop img, since we want the size of
# img to be unchanged # img to be unchanged
mask = img.crop((0,8,16,16)) mask = img.crop((0,8,16,16))
n = Image.new(img.mode, img.size, (38,92,255,0)) n = Image.new(img.mode, img.size, bgcolor)
composite.alpha_over(n, mask,(0,0,16,8), mask) composite.alpha_over(n, mask,(0,0,16,8), mask)
img = n img = n
if blockID in (78,): # snow if blockID in (78,): # snow
# make the top three quarters transparent # make the top three quarters transparent
mask = img.crop((0,12,16,16)) mask = img.crop((0,12,16,16))
n = Image.new(img.mode, img.size, (38,92,255,0)) n = Image.new(img.mode, img.size, bgcolor)
composite.alpha_over(n, mask,(0,12,16,16), mask) composite.alpha_over(n, mask,(0,12,16,16), mask)
img = n img = n
@@ -235,7 +246,7 @@ def _build_block(top, side, blockID=None):
top and side should be 16x16 image objects. Returns a 24x24 image top and side should be 16x16 image objects. Returns a 24x24 image
""" """
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
original_texture = top.copy() original_texture = top.copy()
top = transform_image(top, blockID) top = transform_image(top, blockID)
@@ -315,36 +326,36 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
A non transparent block uses top, side 3 and side 4. 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 If top is a tuple then first item is the top image and the second
member is an increment (integer) from 0 to 12. This increment will item is an increment (integer) from 0 to 16 (pixels in the
used to crop the side images to look like a block and to paste all original minecraft texture). This increment will be used to crop the
the images increment pixels lower. Using increment = 6 will create side images and to paste the top image increment pixels lower, so if
a half-block. 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 NOTE: this method uses the bottom of the texture image (as done in
minecraft with beds) minecraft with beds and cackes)
""" """
increment = 0 increment = 0
if isinstance(top, tuple): if isinstance(top, tuple):
increment = top[1] increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
crop_height = int(increment * 16./12.) crop_height = increment
top = top[0] top = top[0]
if side1 != None: if side1 != None:
side1 = side1.copy() 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: if side2 != None:
side2 = side2.copy() 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: if side3 != None:
side3 = side3.copy() 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: if side4 != None:
side4 = side4.copy() 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)) img = Image.new("RGBA", (24,24), bgcolor)
# first back sides # first back sides
if side1 != None : if side1 != None :
@@ -356,7 +367,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side1 = ImageEnhance.Brightness(side1).enhance(0.9) side1 = ImageEnhance.Brightness(side1).enhance(0.9)
side1.putalpha(sidealpha) side1.putalpha(sidealpha)
composite.alpha_over(img, side1, (0,0 + increment), side1) composite.alpha_over(img, side1, (0,0), side1)
if side2 != None : if side2 != None :
@@ -367,11 +378,11 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side2 = ImageEnhance.Brightness(side2).enhance(0.8) side2 = ImageEnhance.Brightness(side2).enhance(0.8)
side2.putalpha(sidealpha2) side2.putalpha(sidealpha2)
composite.alpha_over(img, side2, (12,0 + increment), side2) composite.alpha_over(img, side2, (12,0), side2)
if bottom != None : if bottom != None :
bottom = transform_image(bottom, blockID) bottom = transform_image(bottom, blockID)
composite.alpha_over(img, bottom, (0,12), top) composite.alpha_over(img, bottom, (0,12), bottom)
# front sides # front sides
if side3 != None : if side3 != None :
@@ -382,7 +393,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side3 = ImageEnhance.Brightness(side3).enhance(0.9) side3 = ImageEnhance.Brightness(side3).enhance(0.9)
side3.putalpha(sidealpha) side3.putalpha(sidealpha)
composite.alpha_over(img, side3, (0,6 + increment), side3) composite.alpha_over(img, side3, (0,6), side3)
if side4 != None : if side4 != None :
side4 = transform_image_side(side4, blockID) side4 = transform_image_side(side4, blockID)
@@ -393,7 +404,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side4 = ImageEnhance.Brightness(side4).enhance(0.8) side4 = ImageEnhance.Brightness(side4).enhance(0.8)
side4.putalpha(sidealpha) side4.putalpha(sidealpha)
composite.alpha_over(img, side4, (12,6 + increment), side4) composite.alpha_over(img, side4, (12,6), side4)
if top != None : if top != None :
top = transform_image(top, blockID) top = transform_image(top, blockID)
@@ -414,7 +425,7 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 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, 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 # 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 # 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, 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 # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
@@ -431,7 +442,7 @@ def _build_blockimages():
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 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, 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 # 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 # 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, 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 # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
@@ -502,12 +513,11 @@ def generate_texture_tuple(img, blockid):
def generate_special_texture(blockID, data, north_direction): def generate_special_texture(blockID, data, north_direction):
"""Generates a special texture, such as a correctly facing minecraft track""" """Generates a special texture, such as a correctly facing minecraft track"""
#print "%s has ancillary data: %X" %(blockID, data)
# TODO ladders, stairs, levers, buttons, and signs
# all need to behandled here (and in chunkpy)
data = convert_data(blockID, data, north_direction) data = convert_data(blockID, data, north_direction)
# blocks need to be handled here (and in chunk.py)
if blockID == 2: # grass if blockID == 2: # grass
# data & 0x10 means SNOW sides # data & 0x10 means SNOW sides
side_img = terrain_images[3] side_img = terrain_images[3]
@@ -544,33 +554,39 @@ def generate_special_texture(blockID, data, north_direction):
return generate_texture_tuple(img, blockID) return generate_texture_tuple(img, blockID)
if blockID == 9: # spring water, flowing water and waterfall water if blockID == 9 or blockID == 20: # spring water, flowing water and waterfall water, AND glass
# water and glass share the way to be rendered
watertexture = _load_image("water.png") if blockID == 9:
texture = _load_image("water.png")
else:
texture = terrain_images[49]
if (data & 0b10000) == 16: if (data & 0b10000) == 16:
top = watertexture top = texture
else: top = None else: top = None
if (data & 0b0001) == 1: if (data & 0b0001) == 1:
side1 = watertexture # top left side1 = texture # top left
else: side1 = None else: side1 = None
if (data & 0b1000) == 8: if (data & 0b1000) == 8:
side2 = watertexture # top right side2 = texture # top right
else: side2 = None else: side2 = None
if (data & 0b0010) == 2: if (data & 0b0010) == 2:
side3 = watertexture # bottom left side3 = texture # bottom left
else: side3 = None else: side3 = None
if (data & 0b0100) == 4: if (data & 0b0100) == 4:
side4 = watertexture # bottom right side4 = texture # bottom right
else: side4 = None else: side4 = None
img = _build_full_block(top,None,None,side3,side4) # if nothing shown do not draw at all
if top == side3 == side4 == None:
return None
img = _build_full_block(top,None,None,side3,side4)
return generate_texture_tuple(img, blockID) return generate_texture_tuple(img, blockID)
@@ -596,7 +612,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID == 26: # bed if blockID == 26: # bed
increment = 5 increment = 8
left_face = None left_face = None
right_face = None right_face = None
if data & 0x8 == 0x8: # head of the bed if data & 0x8 == 0x8: # head of the bed
@@ -640,6 +656,7 @@ def generate_special_texture(blockID, data, north_direction):
return generate_texture_tuple(img, blockID) return generate_texture_tuple(img, blockID)
if blockID == 31: # tall grass if blockID == 31: # tall grass
if data == 0: # dead shrub if data == 0: # dead shrub
texture = terrain_images[55] texture = terrain_images[55]
@@ -652,8 +669,134 @@ def generate_special_texture(blockID, data, north_direction):
img = _build_block(texture, texture, blockID) img = _build_block(texture, texture, blockID)
return generate_texture_tuple(img,31) 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), bgcolor)
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), bgcolor)
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), bgcolor)
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), bgcolor)
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), bgcolor)
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 blockID == 35: # wool
if data == 0: # white if data == 0: # white
top = side = terrain_images[64] top = side = terrain_images[64]
@@ -710,6 +853,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID in (50,75,76): # torch, off redstone torch, on redstone torch if blockID in (50,75,76): # torch, off redstone torch, on redstone torch
# choose the proper texture # choose the proper texture
if blockID == 50: # torch if blockID == 50: # torch
small = terrain_images[80] small = terrain_images[80]
@@ -720,7 +864,7 @@ def generate_special_texture(blockID, data, north_direction):
# compose a torch bigger than the normal # compose a torch bigger than the normal
# (better for doing transformations) # (better for doing transformations)
torch = Image.new("RGBA", (16,16), (38,92,255,0)) torch = Image.new("RGBA", (16,16), bgcolor)
composite.alpha_over(torch,small,(-4,-3)) composite.alpha_over(torch,small,(-4,-3))
composite.alpha_over(torch,small,(-5,-2)) composite.alpha_over(torch,small,(-5,-2))
composite.alpha_over(torch,small,(-3,-2)) composite.alpha_over(torch,small,(-3,-2))
@@ -746,17 +890,17 @@ def generate_special_texture(blockID, data, north_direction):
elif data == 5: # standing on the floor elif data == 5: # standing on the floor
# compose a "3d torch". # compose a "3d torch".
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
small_crop = small.crop((2,2,14,14)) small_crop = small.crop((2,2,14,14))
slice = small_crop.copy() slice = small_crop.copy()
ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0))
composite.alpha_over(img, slice, (6,4)) composite.alpha_over(img, slice, (7,5))
composite.alpha_over(img, small_crop, (5,5)) composite.alpha_over(img, small_crop, (6,6))
composite.alpha_over(img, small_crop, (6,5)) composite.alpha_over(img, small_crop, (7,6))
composite.alpha_over(img, slice, (6,6)) composite.alpha_over(img, slice, (7,7))
return generate_texture_tuple(img, blockID) return generate_texture_tuple(img, blockID)
@@ -766,7 +910,7 @@ def generate_special_texture(blockID, data, north_direction):
side1 = transform_image_side(firetexture) side1 = transform_image_side(firetexture)
side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT)
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, side1, (12,0), side1) composite.alpha_over(img, side1, (12,0), side1)
composite.alpha_over(img, side2, (0,0), side2) composite.alpha_over(img, side2, (0,0), side2)
@@ -813,14 +957,14 @@ def generate_special_texture(blockID, data, north_direction):
composite.alpha_over(img, tmp2, (0,6)) composite.alpha_over(img, tmp2, (0,6))
elif data == 1: # ascending north elif data == 1: # ascending north
img = Image.new("RGBA", (24,24), (38,92,255,0)) # first paste the texture in the back img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back
tmp1 = transform_image(half_block_r) tmp1 = transform_image(half_block_r)
composite.alpha_over(img, tmp1, (0,6)) composite.alpha_over(img, tmp1, (0,6))
tmp2 = _build_full_block(half_block_l, None, None, texture, side) tmp2 = _build_full_block(half_block_l, None, None, texture, side)
composite.alpha_over(img, tmp2) composite.alpha_over(img, tmp2)
elif data == 2: # ascending west elif data == 2: # ascending west
img = Image.new("RGBA", (24,24), (38,92,255,0)) # first paste the texture in the back img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back
tmp1 = transform_image(half_block_u) tmp1 = transform_image(half_block_u)
composite.alpha_over(img, tmp1, (0,6)) composite.alpha_over(img, tmp1, (0,6))
tmp2 = _build_full_block(half_block_d, None, None, side, texture) tmp2 = _build_full_block(half_block_d, None, None, side, texture)
@@ -937,7 +1081,7 @@ def generate_special_texture(blockID, data, north_direction):
bottom = redstone_wire_t.copy().rotate(90) bottom = redstone_wire_t.copy().rotate(90)
else: else:
bottom = Image.new("RGBA", (16,16), (38,92,255,0)) bottom = Image.new("RGBA", (16,16), bgcolor)
if (data & 0b0001) == 1: if (data & 0b0001) == 1:
composite.alpha_over(bottom,branch_top_left) composite.alpha_over(bottom,branch_top_left)
@@ -981,7 +1125,7 @@ def generate_special_texture(blockID, data, north_direction):
crop2 = transform_image_side(raw_crop, blockID) crop2 = transform_image_side(raw_crop, blockID)
crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT)
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, crop1, (0,12), crop1) composite.alpha_over(img, crop1, (0,12), crop1)
composite.alpha_over(img, crop2, (6,3), crop2) composite.alpha_over(img, crop2, (6,3), crop2)
composite.alpha_over(img, crop3, (6,3), crop3) composite.alpha_over(img, crop3, (6,3), crop3)
@@ -1014,6 +1158,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID == 63: # singposts if blockID == 63: # singposts
texture = terrain_images[4].copy() texture = terrain_images[4].copy()
# cut the planks to the size of a signpost # cut the planks to the size of a signpost
ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
@@ -1031,7 +1176,7 @@ def generate_special_texture(blockID, data, north_direction):
texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS)
ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
# W N ~90 E S ~270 # W N ~90 E S ~270
angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.)
@@ -1069,8 +1214,8 @@ def generate_special_texture(blockID, data, north_direction):
swung=False swung=False
# mask out the high bits to figure out the orientation # mask out the high bits to figure out the orientation
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
if (data & 0x03) == 0: if (data & 0x03) == 0: # northeast corner
if not swung: if not swung:
tex = transform_image_side(raw_door) tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (0,6), tex) composite.alpha_over(img, tex, (0,6), tex)
@@ -1080,7 +1225,7 @@ def generate_special_texture(blockID, data, north_direction):
tex = tex.transpose(Image.FLIP_LEFT_RIGHT) tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex) composite.alpha_over(img, tex, (0,0), tex)
if (data & 0x03) == 1: if (data & 0x03) == 1: # southeast corner
if not swung: if not swung:
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (0,0), tex) composite.alpha_over(img, tex, (0,0), tex)
@@ -1088,7 +1233,7 @@ def generate_special_texture(blockID, data, north_direction):
tex = transform_image_side(raw_door) tex = transform_image_side(raw_door)
composite.alpha_over(img, tex, (12,0), tex) composite.alpha_over(img, tex, (12,0), tex)
if (data & 0x03) == 2: if (data & 0x03) == 2: # southwest corner
if not swung: if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
composite.alpha_over(img, tex, (12,0), tex) composite.alpha_over(img, tex, (12,0), tex)
@@ -1096,7 +1241,7 @@ def generate_special_texture(blockID, data, north_direction):
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex) composite.alpha_over(img, tex, (12,6), tex)
if (data & 0x03) == 3: if (data & 0x03) == 3: # northwest corner
if not swung: if not swung:
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT)
composite.alpha_over(img, tex, (12,6), tex) composite.alpha_over(img, tex, (12,6), tex)
@@ -1108,7 +1253,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID == 65: # ladder if blockID == 65: # ladder
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
raw_texture = terrain_images[83] raw_texture = terrain_images[83]
#print "ladder is facing: %d" % data #print "ladder is facing: %d" % data
if data == 5: if data == 5:
@@ -1133,7 +1278,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID in (27, 28, 66): # minetrack: if blockID in (27, 28, 66): # minetrack:
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
if blockID == 27: # powered rail if blockID == 27: # powered rail
if data & 0x8 == 0: # unpowered if data & 0x8 == 0: # unpowered
@@ -1216,7 +1361,7 @@ def generate_special_texture(blockID, data, north_direction):
texture.putpixel((x,y),(0,0,0,255)) texture.putpixel((x,y),(0,0,0,255))
""" """
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
incrementx = 0 incrementx = 0
if data == 2: # east if data == 2: # east
@@ -1250,7 +1395,6 @@ def generate_special_texture(blockID, data, north_direction):
ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
@@ -1270,10 +1414,10 @@ def generate_special_texture(blockID, data, north_direction):
fence_other_side.putalpha(othersidealpha) fence_other_side.putalpha(othersidealpha)
# Compose the fence big stick # Compose the fence big stick
fence_big = Image.new("RGBA", (24,24), (38,92,255,0)) fence_big = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(fence_big,fence_side, (5,4),fence_side) composite.alpha_over(fence_big,fence_side, (5,4),fence_side)
composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side)
composite.alpha_over(fence_big,fence_top, (0,1),fence_top) composite.alpha_over(fence_big,fence_top, (0,0),fence_top)
# Now render the small sticks. # Now render the small sticks.
# Create needed images # Create needed images
@@ -1301,7 +1445,7 @@ def generate_special_texture(blockID, data, north_direction):
fence_small_side.putalpha(sidealpha) fence_small_side.putalpha(sidealpha)
# Create img to compose the fence # Create img to compose the fence
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
# Position of fence small sticks in img. # Position of fence small sticks in img.
# These postitions are strange because the small sticks of the # These postitions are strange because the small sticks of the
@@ -1351,7 +1495,7 @@ def generate_special_texture(blockID, data, north_direction):
if blockID == 90: # portal if blockID == 90: # portal
portaltexture = _load_image("portal.png") portaltexture = _load_image("portal.png")
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
side = transform_image_side(portaltexture) side = transform_image_side(portaltexture)
otherside = side.transpose(Image.FLIP_TOP_BOTTOM) otherside = side.transpose(Image.FLIP_TOP_BOTTOM)
@@ -1380,23 +1524,21 @@ def generate_special_texture(blockID, data, north_direction):
otherside = ImageEnhance.Brightness(otherside).enhance(0.8) otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
otherside.putalpha(othersidealpha) otherside.putalpha(othersidealpha)
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), bgcolor)
composite.alpha_over(img, side, (1,12), side) composite.alpha_over(img, side, (1,6), side)
composite.alpha_over(img, otherside, (11,13), otherside) # workaround, fixes a hole composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole
composite.alpha_over(img, otherside, (12,12), otherside) composite.alpha_over(img, otherside, (12,6), otherside)
composite.alpha_over(img, top, (0,6), top) composite.alpha_over(img, top, (0,6), top)
return generate_texture_tuple(img, blockID) 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, # generate the diode
# this must run after the function of the torches.
top = terrain_images[131] if blockID == 93 else terrain_images[147] top = terrain_images[131] if blockID == 93 else terrain_images[147]
side = terrain_images[5] side = terrain_images[5]
increment = 9 increment = 13
if (data & 0x3) == 0: # pointing east if (data & 0x3) == 0: # pointing east
pass pass
@@ -1411,12 +1553,22 @@ def generate_special_texture(blockID, data, north_direction):
top = top.rotate(90) top = top.rotate(90)
img = _build_full_block( (top, increment), None, None, side, side) img = _build_full_block( (top, increment), None, None, side, side)
# compose a "3d" redstone torch
t = terrain_images[115].copy() if blockID == 93 else terrain_images[99].copy()
torch = Image.new("RGBA", (24,24), bgcolor)
t_crop = t.crop((2,2,14,14))
slice = t_crop.copy()
ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0))
composite.alpha_over(torch, slice, (6,4))
composite.alpha_over(torch, t_crop, (5,5))
composite.alpha_over(torch, t_crop, (6,5))
composite.alpha_over(torch, slice, (6,6))
# paste redstone torches everywhere! # paste redstone torches everywhere!
t = specialblockmap[(75,5)] if blockID == 93 else specialblockmap[(76,5)]
torch = t[0].copy() # textures are stored as tuples (RGB,A)
torch.putalpha(t[1])
# the torch is too tall for the repeater, crop the bottom. # the torch is too tall for the repeater, crop the bottom.
ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0))
@@ -1521,10 +1673,11 @@ def generate_special_texture(blockID, data, north_direction):
img = _build_full_block(None, None, None, texture, None) img = _build_full_block(None, None, None, texture, None)
elif data & 0x4 == 0: # closed trapdoor 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) return generate_texture_tuple(img, blockID)
return None return None
def convert_data(blockID, data, north_direction): def convert_data(blockID, data, north_direction):
@@ -1830,9 +1983,10 @@ def getBiomeData(worlddir, chunkX, chunkY):
# (when adding new blocks here and in generate_special_textures, # (when adding new blocks here and in generate_special_textures,
# please, if possible, keep the ascending order of blockid value) # 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, special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33,
50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62,
67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94, 96]) 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 # this is a map of special blockIDs to a list of all
# possible values for ancillary data that it might have. # possible values for ancillary data that it might have.
@@ -1842,10 +1996,14 @@ special_map = {}
special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings) 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[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[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[26] = range(12) # bed, orientation
special_map[23] = range(6) # dispensers, orientation special_map[23] = range(6) # dispensers, orientation
special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered
special_map[28] = range(6) # detector rail, orientation/slope 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[35] = range(16) # wool, colored and white
special_map[43] = range(4) # stone, sandstone, wooden and cobblestone double-slab special_map[43] = range(4) # stone, sandstone, wooden and cobblestone double-slab
special_map[44] = range(4) # stone, sandstone, wooden and cobblestone slab special_map[44] = range(4) # stone, sandstone, wooden and cobblestone slab
@@ -1854,7 +2012,7 @@ special_map[51] = range(16) # fire, position in the block (not implemented)
special_map[53] = range(4) # wooden stairs, orientation special_map[53] = range(4) # wooden stairs, orientation
special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data
special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data
special_map[58] = (0,) # crafting table special_map[58] = (0,) # crafting table, it has 2 different sides
special_map[59] = range(8) # crops, grow from 0 to 7 special_map[59] = range(8) # crops, grow from 0 to 7
special_map[61] = range(6) # furnace, orientation special_map[61] = range(6) # furnace, orientation
special_map[62] = range(6) # burning furnace, orientation special_map[62] = range(6) # burning furnace, orientation
@@ -1871,9 +2029,9 @@ special_map[85] = range(17) # fences, all the possible combination, uses pseudo
special_map[86] = range(5) # pumpkin, orientation special_map[86] = range(5) # pumpkin, orientation
special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data
special_map[91] = range(5) # jack-o-lantern, orientation special_map[91] = range(5) # jack-o-lantern, orientation
special_map[92] = range(6) # cake! special_map[92] = range(6) # cake, eaten amount, (not implemented)
special_map[93] = range(16) # OFF redstone repeater, orientation and delay (delay not implemented) special_map[93] = range(16) # OFF redstone repeater, orientation and delay
special_map[94] = range(16) # ON redstone repeater, orientation and delay (delay not implemented) special_map[94] = range(16) # ON redstone repeater, orientation and delay
special_map[96] = range(8) # trapdoor, open, closed, orientation special_map[96] = range(8) # trapdoor, open, closed, orientation
# grass and leaves are graysacle in terrain.png # grass and leaves are graysacle in terrain.png
@@ -1888,6 +2046,7 @@ special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemen
special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself
# placeholders that are generated in generate() # placeholders that are generated in generate()
bgcolor = None
terrain_images = None terrain_images = None
blockmap = None blockmap = None
biome_grass_texture = None biome_grass_texture = None
@@ -1896,11 +2055,15 @@ biome_tall_fern_texture = None
biome_leaf_texture = None biome_leaf_texture = None
specialblockmap = None specialblockmap = None
def generate(north_direction, path=None): def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'):
global _north global _north
_north = north_direction _north = north_direction
global _find_file_local_path global _find_file_local_path
global bgcolor
bgcolor = bgc
global _find_file_local_path, texture_dimensions
_find_file_local_path = path _find_file_local_path = path
texture_dimensions = (texture_size, texture_size)
# This maps terainids to 16x16 images # This maps terainids to 16x16 images
global terrain_images global terrain_images
@@ -1924,3 +2087,30 @@ def generate(north_direction, path=None):
for blockID in special_blocks: for blockID in special_blocks:
for data in special_map[blockID]: for data in special_map[blockID]:
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction) specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction)
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

@@ -21,19 +21,22 @@ import imp
import os import os
import os.path import os.path
import sys import sys
from subprocess import Popen, PIPE
def get_program_path(): def get_program_path():
if hasattr(sys, "frozen") or imp.is_frozen("__main__"): if hasattr(sys, "frozen") or imp.is_frozen("__main__"):
return os.path.dirname(sys.executable) return os.path.dirname(sys.executable)
else: else:
try: try:
return os.path.dirname(__file__) # normally, we're in ./overviewer_core/util.py
# we want ./
return os.path.dirname(os.path.dirname(__file__))
except NameError: except NameError:
return os.path.dirname(sys.argv[0]) return os.path.dirname(sys.argv[0])
# does not require git, very likely to work everywhere
def findGitVersion(): def findGitHash():
this_dir = get_program_path() this_dir = get_program_path()
if os.path.exists(os.path.join(this_dir,".git")): if os.path.exists(os.path.join(this_dir,".git")):
with open(os.path.join(this_dir,".git","HEAD")) as f: with open(os.path.join(this_dir,".git","HEAD")) as f:
@@ -46,6 +49,24 @@ def findGitVersion():
else: else:
return data return data
else: else:
try:
import overviewer_version
return overviewer_version.HASH
except:
return "unknown"
def findGitVersion():
try:
p = Popen(['git', 'describe', '--tags'], stdout=PIPE, stderr=PIPE)
p.stderr.close()
line = p.stdout.readlines()[0]
if line.startswith('release-'):
line = line.split('-', 1)[1]
# turn 0.1.2-50-somehash into 0.1.2-50
# and 0.1.3 into 0.1.3
line = '-'.join(line.split('-', 2)[:2])
return line.strip()
except:
try: try:
import overviewer_version import overviewer_version
return overviewer_version.VERSION return overviewer_version.VERSION

View File

@@ -196,12 +196,17 @@ class World(object):
in the chunk coordinate system, and figures out the row and column in the chunk coordinate system, and figures out the row and column
in the image each one should be. Returns (col, row).""" in the image each one should be. Returns (col, row)."""
# columns are determined by the sum of the chunk coords, rows are the
# difference
# change this function, and you MUST change unconvert_coords # change this function, and you MUST change unconvert_coords
return (chunkx + chunky, chunky - chunkx) return (chunkx + chunky, chunky - chunkx)
def unconvert_coords(self, col, row): def unconvert_coords(self, col, row):
"""Undoes what convert_coords does. Returns (chunkx, chunky).""" """Undoes what convert_coords does. Returns (chunkx, chunky)."""
return ((col - row) / 2, (row + col) / 2)
# col + row = chunky + chunky => (col + row)/2 = chunky
# col - row = chunkx + chunkx => (col - row)/2 = chunkx
return ((col - row) / 2, (col + row) / 2)
def findTrueSpawn(self): def findTrueSpawn(self):
"""Adds the true spawn location to self.POI. The spawn Y coordinate """Adds the true spawn location to self.POI. The spawn Y coordinate
@@ -229,25 +234,27 @@ class World(object):
chunkX = spawnX/16 chunkX = spawnX/16
chunkY = spawnZ/16 chunkY = spawnZ/16
## The filename of this chunk try:
chunkFile = self.get_region_path(chunkX, chunkY) ## The filename of this chunk
chunkFile = self.get_region_path(chunkX, chunkY)
if chunkFile is not None: if chunkFile is not None:
data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1]
if data is not None: if data is not None:
level = data['Level'] level = data['Level']
blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
## The block for spawn *within* the chunk ## The block for spawn *within* the chunk
inChunkX = spawnX - (chunkX*16) inChunkX = spawnX - (chunkX*16)
inChunkZ = spawnZ - (chunkY*16) inChunkZ = spawnZ - (chunkY*16)
## find the first air block ## find the first air block
while (blockArray[inChunkX, inChunkZ, spawnY] != 0): while (blockArray[inChunkX, inChunkZ, spawnY] != 0):
spawnY += 1 spawnY += 1
if spawnY == 128: if spawnY == 128:
break break
except ChunkCorrupt:
#ignore corrupt spawn, and continue
pass
self.POI.append( dict(x=disp_spawnX, y=spawnY, z=disp_spawnZ, self.POI.append( dict(x=disp_spawnX, y=spawnY, z=disp_spawnZ,
msg="Spawn", type="spawn", chunk=(chunkX, chunkY))) msg="Spawn", type="spawn", chunk=(chunkX, chunkY)))
self.spawn = (disp_spawnX, spawnY, disp_spawnZ) self.spawn = (disp_spawnX, spawnY, disp_spawnZ)
@@ -314,7 +321,6 @@ class World(object):
world. world.
Returns (regionx, regiony, filename)""" Returns (regionx, regiony, filename)"""
join = os.path.join join = os.path.join
if regionlist is not None: if regionlist is not None:
for path in regionlist: for path in regionlist:

196
setup.py Normal file → Executable file
View File

@@ -1,26 +1,61 @@
from distutils.core import setup, Extension #!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension
from distutils.command.build import build from distutils.command.build import build
from distutils.command.clean import clean from distutils.command.clean import clean
from distutils.command.build_ext import build_ext 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.dir_util import remove_tree
from distutils.sysconfig import get_python_inc from distutils.sysconfig import get_python_inc
from distutils import log from distutils import log
import os, os.path import sys, os, os.path
import glob import glob
import platform import platform
import time import time
import overviewer_core.util as util
try: try:
import py2exe import py2exe
except ImportError: except ImportError:
py2exe = None py2exe = None
try:
import py2app
from setuptools.extension import Extension
except ImportError:
py2app = None
# now, setup the keyword arguments for setup # now, setup the keyword arguments for setup
# (because we don't know until runtime if py2exe is available) # (because we don't know until runtime if py2exe/py2app is available)
setup_kwargs = {} setup_kwargs = {}
setup_kwargs['options'] = {}
setup_kwargs['ext_modules'] = [] setup_kwargs['ext_modules'] = []
setup_kwargs['cmdclass'] = {} setup_kwargs['cmdclass'] = {}
setup_kwargs['options'] = {}
#
# metadata
#
# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup_kwargs['name'] = 'Minecraft-Overviewer'
setup_kwargs['version'] = util.findGitVersion()
setup_kwargs['description'] = 'Generates large resolution images of a Minecraft map.'
setup_kwargs['url'] = 'http://overviewer.org/'
setup_kwargs['author'] = 'Andrew Brown'
setup_kwargs['author_email'] = 'brownan@gmail.com'
setup_kwargs['license'] = 'GNU General Public License v3'
setup_kwargs['long_description'] = read('README.rst')
# top-level files that should be included as documentation
doc_files = ['COPYING.txt', 'README.rst', 'CONTRIBUTORS.rst', 'sample.settings.py']
# helper to create a 'data_files'-type sequence recursively for a given dir # helper to create a 'data_files'-type sequence recursively for a given dir
def recursive_data_files(src, dest=None): def recursive_data_files(src, dest=None):
@@ -40,15 +75,26 @@ def recursive_data_files(src, dest=None):
ret.append((current_dest, current_sources)) ret.append((current_dest, current_sources))
return ret 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 # py2exe options
# #
if py2exe is not None: if py2exe is not None:
setup_kwargs['console'] = ['overviewer.py'] setup_kwargs['console'] = ['overviewer.py']
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png', 'textures/portal.png']), setup_kwargs['data_files'] = [('', doc_files)]
('', ['COPYING.txt', 'README.rst'])] setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures')
setup_kwargs['data_files'] += recursive_data_files('web_assets') setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/web_assets', 'web_assets')
setup_kwargs['zipfile'] = None setup_kwargs['zipfile'] = None
if platform.system() == 'Windows' and '64bit' in platform.architecture(): if platform.system() == 'Windows' and '64bit' in platform.architecture():
b = 3 b = 3
@@ -56,6 +102,27 @@ if py2exe is not None:
b = 1 b = 1
setup_kwargs['options']['py2exe'] = {'bundle_files' : b, 'excludes': 'Tkinter'} setup_kwargs['options']['py2exe'] = {'bundle_files' : b, 'excludes': 'Tkinter'}
#
# py2app options
#
if py2app is not None:
setup_kwargs['app'] = ['overviewer.py']
setup_kwargs['options']['py2app'] = {'argv_emulation' : False}
setup_kwargs['setup_requires'] = ['py2app']
#
# script, package, and data
#
setup_kwargs['packages'] = ['overviewer_core']
setup_kwargs['scripts'] = ['overviewer.py']
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)]
# #
# c_overviewer extension # c_overviewer extension
# #
@@ -79,20 +146,23 @@ except:
# used to figure out what files to compile # used to figure out what files to compile
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave'] render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave']
c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c', 'src/rendermodes.c'] c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c']
c_overviewer_files += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes) c_overviewer_files += map(lambda mode: 'rendermode-%s.c' % (mode,), render_modes)
c_overviewer_files += ['src/Draw.c'] c_overviewer_files += ['Draw.c']
c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h'] c_overviewer_includes = ['overviewer.h', 'rendermodes.h']
c_overviewer_files = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_files)
c_overviewer_includes = map(lambda s: 'overviewer_core/src/'+s, c_overviewer_includes)
setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
# tell build_ext to build the extension in-place # tell build_ext to build the extension in-place
# (NOT in build/) # (NOT in build/)
setup_kwargs['options']['build_ext'] = {'inplace' : 1} setup_kwargs['options']['build_ext'] = {'inplace' : 1}
# tell the build command to only run build_ext
build.sub_commands = [('build_ext', None)]
# custom clean command to remove in-place extension # custom clean command to remove in-place extension
# and the version file
class CustomClean(clean): class CustomClean(clean):
def run(self): def run(self):
# do the normal cleanup # do the normal cleanup
@@ -101,7 +171,7 @@ class CustomClean(clean):
# try to remove '_composite.{so,pyd,...}' extension, # try to remove '_composite.{so,pyd,...}' extension,
# regardless of the current system's extension name convention # regardless of the current system's extension name convention
build_ext = self.get_finalized_command('build_ext') build_ext = self.get_finalized_command('build_ext')
pretty_fname = build_ext.get_ext_filename('c_overviewer') pretty_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer')
fname = pretty_fname fname = pretty_fname
if os.path.exists(fname): if os.path.exists(fname):
try: try:
@@ -114,8 +184,55 @@ class CustomClean(clean):
else: else:
log.debug("'%s' does not exist -- can't clean it", log.debug("'%s' does not exist -- can't clean it",
pretty_fname) pretty_fname)
versionpath = os.path.join("overviewer_core", "overviewer_version.py")
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)
class CustomBuild(build_ext): # now try to purge all *.pyc files
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")):
for f in files:
if f.endswith(".pyc"):
if self.dry_run:
log.warn("Would remove %s", os.path.join(root,f))
else:
os.remove(os.path.join(root, f))
def generate_version_py():
try:
outstr = ""
outstr += "VERSION=%r\n" % util.findGitVersion()
outstr += "HASH=%r\n" % util.findGitHash()
outstr += "BUILD_DATE=%r\n" % time.asctime()
outstr += "BUILD_PLATFORM=%r\n" % platform.processor()
outstr += "BUILD_OS=%r\n" % platform.platform()
f = open("overviewer_core/overviewer_version.py", "w")
f.write(outstr)
f.close()
except:
print "WARNING: failed to build overviewer_version file"
class CustomSDist(sdist):
def run(self):
# generate the version file
generate_version_py()
sdist.run(self)
class CustomBuild(build):
def run(self):
# generate the version file
generate_version_py()
build.run(self)
print "\nBuild Complete"
class CustomBuildExt(build_ext):
def build_extensions(self): def build_extensions(self):
c = self.compiler.compiler_type c = self.compiler.compiler_type
if c == "msvc": if c == "msvc":
@@ -123,32 +240,39 @@ class CustomBuild(build_ext):
for e in self.extensions: for e in self.extensions:
e.extra_link_args.append("/MANIFEST") e.extra_link_args.append("/MANIFEST")
# build in place, and in the build/ tree
self.inplace = False
build_ext.build_extensions(self)
self.inplace = True
build_ext.build_extensions(self) 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
if py2exe is not None: h = hashlib.sha1()
# define a subclass of py2exe to build our version file on the fly h.update(f.read())
class CustomPy2exe(py2exe.build_exe.py2exe): log.info("Hash of terrain.png file is: %s", h.hexdigest())
def run(self):
try:
import util
f = open("overviewer_version.py", "w")
f.write("VERSION=%r\n" % util.findGitVersion())
f.write("BUILD_DATE=%r\n" % time.asctime())
f.write("BUILD_PLATFORM=%r\n" % platform.processor())
f.write("BUILD_OS=%r\n" % platform.platform())
f.close()
setup_kwargs['data_files'].append(('.', ['overviewer_version.py']))
except:
print "WARNING: failed to build overview_version file"
py2exe.build_exe.py2exe.run(self)
setup_kwargs['cmdclass']['py2exe'] = CustomPy2exe
setup_kwargs['cmdclass']['clean'] = CustomClean setup_kwargs['cmdclass']['clean'] = CustomClean
setup_kwargs['cmdclass']['build_ext'] = CustomBuild 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) setup(**setup_kwargs)
print "\nBuild Complete"