Merge remote-tracking branch 'upstream/master'
16
.gitignore
vendored
@@ -1,5 +1,8 @@
|
||||
*.pyc
|
||||
build
|
||||
MANIFEST
|
||||
build/
|
||||
dist/
|
||||
Minecraft_Overviewer.egg-info
|
||||
terrain.png
|
||||
cachedir*
|
||||
|
||||
@@ -14,10 +17,13 @@ ImPlatform.h
|
||||
Imaging.h
|
||||
|
||||
# various forms of compiled c_overviewer extensions
|
||||
c_overviewer.so
|
||||
c_overviewer.pyd
|
||||
c_overviewer_d.pyd
|
||||
c_overviewer.dylib
|
||||
overviewer_core/c_overviewer.so
|
||||
overviewer_core/c_overviewer.pyd
|
||||
overviewer_core/c_overviewer_d.pyd
|
||||
overviewer_core/c_overviewer.dylib
|
||||
|
||||
# generated version file
|
||||
overviewer_core/overviewer_version.py
|
||||
|
||||
# Mac OS X noise
|
||||
.DS_Store
|
||||
|
||||
@@ -4,7 +4,8 @@ Contributors
|
||||
|
||||
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
|
||||
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
|
||||
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>
|
||||
* Kyle Brantley <kyle@averageurl.com>
|
||||
* but2002 <barryt_9@hotmail.com>
|
||||
* Eric Carr <eric@carr.no>
|
||||
* cbarber <CraigBarber@taryx.com>
|
||||
* Alex Cline <cline@vivisimo.com>
|
||||
* Andrew Clunis <andrew@orospakr.ca>
|
||||
* CounterPillow <spam@tes-cheese.ch>
|
||||
* Stephen Fluin <stephen@mistuph.com>
|
||||
* Benjamin Herr <ben@0x539.de>
|
||||
* Ryan Hitchman <hitchmanr@gmail.com>
|
||||
* Jenny <jennytoo@gmail.com>
|
||||
* Michael Jensen <emjay1988@gmail.com>
|
||||
* Maciej Małecki <maciej.malecki@hotmail.com>
|
||||
* Ryan McCue <ryanmccue@cubegames.net>
|
||||
* Morlok8k <otis.spankmeyer@gmail.com>
|
||||
* Gregory Short <gshort2@gmail.com>
|
||||
|
||||
9
MANIFEST.in
Normal 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/ *
|
||||
@@ -169,6 +169,10 @@ Options
|
||||
*Note*: Currently only the overviewer.dat file is deleted when you run with
|
||||
this option
|
||||
|
||||
--forcerender
|
||||
Force re-rendering the entire map (or the given regionlist). This
|
||||
is an easier way to completely re-render without deleting the map.
|
||||
|
||||
--regionlist=regionlist
|
||||
Use this option to specify manually a list of regions to consider for
|
||||
updating. Without this option, every chunk in every region is checked for
|
||||
|
||||
@@ -22,14 +22,13 @@ if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
|
||||
|
||||
import os
|
||||
import os.path
|
||||
from configParser import ConfigOptionParser
|
||||
import re
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
import time
|
||||
import logging
|
||||
import util
|
||||
import platform
|
||||
from overviewer_core import util
|
||||
|
||||
logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
|
||||
@@ -37,10 +36,19 @@ this_dir = util.get_program_path()
|
||||
|
||||
# make sure the c_overviewer extension is available
|
||||
try:
|
||||
import c_overviewer
|
||||
from overviewer_core import c_overviewer
|
||||
except ImportError:
|
||||
## if this is a frozen windows package, the following error messages about
|
||||
## building the c_overviewer extension are not appropriate
|
||||
if hasattr(sys, "frozen"):
|
||||
print "Something has gone wrong importing the c_overviewer extension. Please"
|
||||
print "make sure the 2008 and 2010 redistributable packages from Microsoft"
|
||||
print "are installed."
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
## 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):
|
||||
print "Something has gone wrong importing the c_overviewer extension. Please"
|
||||
print "make sure it is up-to-date (clean and rebuild)"
|
||||
@@ -48,14 +56,17 @@ except ImportError:
|
||||
|
||||
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."
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if hasattr(sys, "frozen"):
|
||||
pass # we don't bother with a compat test since it should always be in sync
|
||||
elif "extension_version" in dir(c_overviewer):
|
||||
# check to make sure the binary matches the headers
|
||||
if os.path.exists(os.path.join(this_dir, "src", "overviewer.h")):
|
||||
with open(os.path.join(this_dir, "src", "overviewer.h")) as f:
|
||||
if os.path.exists(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")):
|
||||
with open(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")) as f:
|
||||
lines = f.readlines()
|
||||
lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines)
|
||||
if lines:
|
||||
@@ -67,12 +78,10 @@ else:
|
||||
print "Please rebuild your c_overviewer module. It is out of date!"
|
||||
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 = """
|
||||
%prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir>
|
||||
@@ -115,14 +124,14 @@ def main():
|
||||
|
||||
|
||||
if options.version:
|
||||
print "Minecraft-Overviewer"
|
||||
print "Git version: %s" % util.findGitVersion()
|
||||
try:
|
||||
import overviewer_version
|
||||
if hasattr(sys, "frozen"):
|
||||
print "py2exe version build on %s" % overviewer_version.BUILD_DATE
|
||||
import overviewer_core.overviewer_version as overviewer_version
|
||||
print "Minecraft-Overviewer %s" % overviewer_version.VERSION
|
||||
print "Git commit: %s" % overviewer_version.HASH
|
||||
print "built on %s" % overviewer_version.BUILD_DATE
|
||||
print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS)
|
||||
except:
|
||||
print "version info not found"
|
||||
pass
|
||||
sys.exit(0)
|
||||
|
||||
@@ -168,6 +177,11 @@ def main():
|
||||
logging.error("Invalid world number")
|
||||
sys.exit(1)
|
||||
|
||||
# final sanity check for worlddir
|
||||
if not os.path.exists(os.path.join(worlddir, 'level.dat')):
|
||||
logging.error("Invalid world path -- does not contain level.dat")
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) < 2:
|
||||
if options.delete:
|
||||
return delete_all(worlddir, None)
|
||||
|
||||
0
overviewer_core/__init__.py
Normal file
@@ -114,10 +114,10 @@ def get_tileentity_data(level):
|
||||
return data
|
||||
|
||||
# This set holds blocks ids that can be seen through, for occlusion calculations
|
||||
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38,
|
||||
39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67,
|
||||
68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85,
|
||||
90, 92, 93, 94, 96])
|
||||
transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33,
|
||||
34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64,
|
||||
65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79,
|
||||
81, 83, 85, 90, 92, 93, 94, 96])
|
||||
|
||||
# This set holds block ids that are solid blocks
|
||||
solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
@@ -321,67 +321,6 @@ class ChunkRenderer(object):
|
||||
return self._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):
|
||||
"""Renders a chunk with the given parameters, and returns the image.
|
||||
If img is given, the chunk is rendered to that image object. Otherwise,
|
||||
|
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 401 B |
|
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 672 B |
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -100,3 +100,34 @@ body {
|
||||
background-color: #fff; /* fallback */
|
||||
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;
|
||||
}
|
||||
@@ -96,6 +96,25 @@ var overviewer = {
|
||||
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
|
||||
* 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 mapcenter;
|
||||
var queryParams = overviewer.util.parseQueryString();
|
||||
if (queryParams.debug) {
|
||||
overviewerConfig.map.debug=true;
|
||||
}
|
||||
if (queryParams.lat) {
|
||||
lat = parseFloat(queryParams.lat);
|
||||
}
|
||||
@@ -291,6 +313,9 @@ var overviewer = {
|
||||
'title': jQuery.trim(item.msg),
|
||||
'icon': overviewerConfig.CONST.image.queryMarker
|
||||
});
|
||||
google.maps.event.addListener(marker, 'click', function(){ marker.setVisible(false); });
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -317,6 +342,7 @@ var overviewer = {
|
||||
'icon': iconURL,
|
||||
'visible': false
|
||||
});
|
||||
item.marker = marker;
|
||||
overviewer.util.debug(label);
|
||||
overviewer.collections.markers[label].push(marker);
|
||||
if (item.type == 'sign') {
|
||||
@@ -339,6 +365,7 @@ var overviewer = {
|
||||
'icon': iconURL,
|
||||
'visible': false
|
||||
});
|
||||
item.marker = marker;
|
||||
if (overviewer.collections.markers['__others__']) {
|
||||
overviewer.collections.markers['__others__'].push(marker);
|
||||
} else {
|
||||
@@ -370,16 +397,11 @@ var overviewer = {
|
||||
point.x, point.y, point.z));
|
||||
|
||||
}
|
||||
for (k in overviewerConfig.objectGroups.regions) {
|
||||
var regionGroup = overviewerConfig.objectGroups.regions[k];
|
||||
var clickable = regionGroup.clickable;
|
||||
var label = regionGroup.label;
|
||||
|
||||
if(region.label) {
|
||||
var name = region.label
|
||||
if (region.label) {
|
||||
var name = region.label;
|
||||
} else {
|
||||
var name = 'rawr';
|
||||
clickable = false; // if it doesn't have a name, we dont have to show it.
|
||||
var name = "rawr";
|
||||
}
|
||||
|
||||
if(region.opacity) {
|
||||
@@ -390,39 +412,61 @@ var overviewer = {
|
||||
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) {
|
||||
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
|
||||
});
|
||||
shapeOptions["fillColor"] = region.color;
|
||||
shapeOptions["fillOpacity"] = fillOpacity;
|
||||
shapeOptions["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
|
||||
});
|
||||
shapeOptions["path"] = converted;
|
||||
}
|
||||
|
||||
var matched = false;
|
||||
|
||||
for (k in overviewerConfig.objectGroups.regions) {
|
||||
var regionGroup = overviewerConfig.objectGroups.regions[k];
|
||||
var clickable = regionGroup.clickable;
|
||||
var label = regionGroup.label;
|
||||
|
||||
if (!regionGroup.match(region))
|
||||
continue;
|
||||
matched = true;
|
||||
|
||||
if (!region.label) {
|
||||
clickable = false; // if it doesn't have a name, we dont have to show it.
|
||||
}
|
||||
|
||||
if (region.closed) {
|
||||
var shape = new google.maps.Polygon(shapeOptions);
|
||||
} else {
|
||||
var shape = new google.maps.Polyline(shapeOptions);
|
||||
}
|
||||
|
||||
overviewer.collections.regions[label].push(shape);
|
||||
|
||||
if (clickable) {
|
||||
overviewer.util.createRegionInfoWindow(shape);
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't matched anything, go ahead and add it
|
||||
if (!matched) {
|
||||
if (region.closed) {
|
||||
var shape = new google.maps.Polygon(shapeOptions);
|
||||
} else {
|
||||
var shape = new google.maps.Polyline(shapeOptions);
|
||||
}
|
||||
|
||||
shape.setMap(overviewer.map);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -490,7 +534,7 @@ var overviewer = {
|
||||
// the width and height of all the highest-zoom tiles combined,
|
||||
// inverted
|
||||
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
|
||||
Math.pow(2, overviewerConfig.map.maxZoom));
|
||||
Math.pow(2, overviewerConfig.map.zoomLevels));
|
||||
|
||||
// This information about where the center column is may change with
|
||||
// a different drawing implementation -- check it again after any
|
||||
@@ -498,13 +542,13 @@ var overviewer = {
|
||||
|
||||
// point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2)
|
||||
// so the Y coordinate is at 0.5, and the X is at 0.5 -
|
||||
// ((tileSize / 2) / (tileSize * 2^maxZoom))
|
||||
// or equivalently, 0.5 - (1 / 2^(maxZoom + 1))
|
||||
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
|
||||
// ((tileSize / 2) / (tileSize * 2^zoomLevels))
|
||||
// or equivalently, 0.5 - (1 / 2^(zoomLevels + 1))
|
||||
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
|
||||
var lat = 0.5;
|
||||
|
||||
// the following metrics mimic those in ChunkRenderer.chunk_render
|
||||
// in "chunk.py" or, equivalently, chunk_render in src/iterate.c
|
||||
// the following metrics mimic those in
|
||||
// chunk_render in src/iterate.c
|
||||
|
||||
// each block on X axis adds 12px to x and subtracts 6px from y
|
||||
lng += 12 * x * perPixel;
|
||||
@@ -542,11 +586,11 @@ var overviewer = {
|
||||
// the width and height of all the highest-zoom tiles combined,
|
||||
// inverted
|
||||
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize *
|
||||
Math.pow(2, overviewerConfig.map.maxZoom));
|
||||
Math.pow(2, overviewerConfig.map.zoomLevels));
|
||||
|
||||
// Revert base positioning
|
||||
// See equivalent code in fromWorldToLatLng()
|
||||
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1));
|
||||
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.zoomLevels + 1));
|
||||
lat -= 0.5;
|
||||
|
||||
// I'll admit, I plugged this into Wolfram Alpha:
|
||||
@@ -595,7 +639,10 @@ var overviewer = {
|
||||
var coordsDiv = document.createElement('DIV');
|
||||
coordsDiv.id = 'coordsDiv';
|
||||
coordsDiv.innerHTML = '';
|
||||
if (overviewerConfig.map.controls.coordsBox) {
|
||||
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(coordsDiv);
|
||||
}
|
||||
|
||||
// Update coords on mousemove
|
||||
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
|
||||
var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng());
|
||||
@@ -662,7 +709,7 @@ var overviewer = {
|
||||
overviewer.util.createDropDown('Regions', items);
|
||||
}
|
||||
|
||||
if (overviewer.collections.overlays.length > 0) {
|
||||
if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) {
|
||||
// overlay maps control
|
||||
var items = [];
|
||||
for (i in overviewer.collections.overlays) {
|
||||
@@ -690,6 +737,9 @@ var overviewer = {
|
||||
}
|
||||
overviewer.util.createDropDown('Overlays', items);
|
||||
}
|
||||
|
||||
// call out to create search box, as it's pretty complicated
|
||||
overviewer.util.createSearchBox();
|
||||
},
|
||||
/**
|
||||
* Reusable method for creating drop-down menus
|
||||
@@ -758,6 +808,79 @@ var overviewer = {
|
||||
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";
|
||||
searchInput.value = "Sign Search"
|
||||
searchInput.title = "Sign Search"
|
||||
|
||||
/* Hey dawg, I heard you like functions.
|
||||
* So we defined a function inside your function.
|
||||
*/
|
||||
searchInput.onfocus = function() {
|
||||
if (searchInput.value == "Sign Search") {
|
||||
searchInput.value = "";
|
||||
}
|
||||
};
|
||||
searchInput.onblur = function() {
|
||||
if (searchInput.value == "") {
|
||||
searchInput.value = "Sign Search";
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
* be done in-line because of stupid Javascript scoping problems with
|
||||
@@ -50,9 +50,18 @@ var overviewerConfig = {
|
||||
*/
|
||||
'mapType': true,
|
||||
/**
|
||||
* The small box at the bottom that displays the link to the current map view.
|
||||
* The coordsBox control is the box showing the XYZ coordinates
|
||||
* under the cursor.
|
||||
*/
|
||||
'link': true
|
||||
'coordsBox': true,
|
||||
/**
|
||||
* The overlays control is the drop-down box for selecting overlays.
|
||||
*/
|
||||
'overlays': true,
|
||||
/**
|
||||
* The searchBox control is the search box for markers.
|
||||
*/
|
||||
'searchBox': true
|
||||
},
|
||||
/**
|
||||
* The zoom level when the page is loaded without a specific zoom setting
|
||||
@@ -66,6 +75,12 @@ var overviewerConfig = {
|
||||
* This controls how close you can zoom in.
|
||||
*/
|
||||
'maxZoom': {maxzoom},
|
||||
/**
|
||||
* This tells us how many total zoom levels Overviewer rendered.
|
||||
* DO NOT change this, even if you change minZoom and maxZoom, because
|
||||
* it's used for marker position calculations and map resizing.
|
||||
*/
|
||||
'zoomLevels': {zoomlevels},
|
||||
/**
|
||||
* Center on this point, in world coordinates. Should be an array, ex:
|
||||
* [0,0,0]
|
||||
|
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
|
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 708 B |
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 253 B |
@@ -24,6 +24,7 @@ import json
|
||||
|
||||
import util
|
||||
from c_overviewer import get_render_mode_inheritance
|
||||
import overviewer_version
|
||||
|
||||
"""
|
||||
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)):
|
||||
try:
|
||||
shutil.copy(os.path.join(src, entry), os.path.join(dst, entry))
|
||||
except IOError:
|
||||
except IOError as outer:
|
||||
try:
|
||||
# maybe permission problems?
|
||||
os.chmod(os.path.join(src, entry), stat.S_IRUSR)
|
||||
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))
|
||||
# if this stills throws an error, let it propagate up
|
||||
|
||||
@@ -98,7 +104,11 @@ class MapGen(object):
|
||||
blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat))
|
||||
|
||||
# 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
|
||||
if self.web_assets_path:
|
||||
mirror_dir(self.web_assets_path, self.destdir)
|
||||
@@ -109,6 +119,8 @@ class MapGen(object):
|
||||
"{minzoom}", str(0))
|
||||
config = config.replace(
|
||||
"{maxzoom}", str(zoomlevel))
|
||||
config = config.replace(
|
||||
"{zoomlevels}", str(zoomlevel))
|
||||
|
||||
config = config.replace("{spawn_coords}",
|
||||
json.dumps(list(self.world.spawn)))
|
||||
@@ -131,9 +143,9 @@ class MapGen(object):
|
||||
indexpath = os.path.join(self.destdir, "index.html")
|
||||
|
||||
index = open(indexpath, 'r').read()
|
||||
index = index.replace(
|
||||
"{time}", str(strftime("%a, %d %b %Y %H:%M:%S %Z", localtime())))
|
||||
index = index.replace("{version}", util.findGitVersion())
|
||||
index = index.replace("{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}", versionstr)
|
||||
|
||||
with open(os.path.join(self.destdir, "index.html"), 'w') as output:
|
||||
output.write(index)
|
||||
@@ -120,7 +120,7 @@ class QuadtreeGen(object):
|
||||
indexfile = os.path.join(self.destdir, "overviewerConfig.js")
|
||||
if not os.path.exists(indexfile):
|
||||
return -1
|
||||
matcher = re.compile(r"maxZoom.*:\s*(\d+)")
|
||||
matcher = re.compile(r"zoomLevels(?:\'|\")\s*:\s*(\d+)")
|
||||
p = -1
|
||||
for line in open(indexfile, "r"):
|
||||
res = matcher.search(line)
|
||||
@@ -426,6 +426,11 @@ class QuadtreeGen(object):
|
||||
needs_rerender = False
|
||||
get_region_mtime = world.get_region_mtime
|
||||
for col, row, chunkx, chunky, regionfile in chunks:
|
||||
region, regionMtime = get_region_mtime(regionfile)
|
||||
|
||||
# don't even check if it's not in the regionlist
|
||||
if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
|
||||
continue
|
||||
|
||||
# bail early if forcerender is set
|
||||
if self.forcerender:
|
||||
@@ -433,14 +438,9 @@ class QuadtreeGen(object):
|
||||
break
|
||||
|
||||
# check region file mtime first.
|
||||
region,regionMtime = get_region_mtime(regionfile)
|
||||
if regionMtime <= tile_mtime:
|
||||
continue
|
||||
|
||||
# don't even check if it's not in the regionlist
|
||||
if self.world.regionlist and os.path.abspath(region._filename) not in self.world.regionlist:
|
||||
continue
|
||||
|
||||
# checking chunk mtime
|
||||
if region.get_chunk_timestamp(chunkx, chunky) > tile_mtime:
|
||||
needs_rerender = True
|
||||
@@ -132,8 +132,8 @@ class RenderNode(object):
|
||||
else:
|
||||
if not complete % 1000 == 0:
|
||||
return
|
||||
logging.info("{0}/{1} tiles complete on level {2}/{3}".format(
|
||||
complete, total, level, self.max_p))
|
||||
logging.info("{0}/{1} ({4}%) tiles complete on level {2}/{3}".format(
|
||||
complete, total, level, self.max_p, '%.1f' % ( (100.0 * complete) / total) ))
|
||||
|
||||
def go(self, procs):
|
||||
"""Renders all tiles"""
|
||||
@@ -33,13 +33,13 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
textures = PyImport_ImportModule("textures");
|
||||
textures = PyImport_ImportModule("overviewer_core.textures");
|
||||
/* ensure none of these pointers are NULL */
|
||||
if ((!textures)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chunk_mod = PyImport_ImportModule("chunk");
|
||||
chunk_mod = PyImport_ImportModule("overviewer_core.chunk");
|
||||
/* ensure none of these pointers are NULL */
|
||||
if ((!chunk_mod)) {
|
||||
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);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
} else if ((state->block == 20) || (state->block == 79)) { /* glass and ice */
|
||||
/* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */
|
||||
if ((z != 127) && (getArrayByte3D(state->blocks, x, y, z+1) == 20)) {
|
||||
data = 0;
|
||||
} else {
|
||||
data = 16;
|
||||
}
|
||||
data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | data;
|
||||
return data;
|
||||
} else if (state->block == 85) { /* fences */
|
||||
return check_adjacent_blocks(state, x, y, z, state->block);
|
||||
|
||||
|
||||
} else if (state->block == 55) { /* redstone */
|
||||
/* three addiotional bit are added, one for on/off state, and
|
||||
* another two for going-up redstone wire in the same block
|
||||
@@ -354,6 +360,10 @@ chunk_render(PyObject *self, PyObject *args) {
|
||||
up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks");
|
||||
state.up_right_blocks = up_right_blocks_py;
|
||||
|
||||
/* set up the random number generator again for each chunk
|
||||
so tallgrass is in the same place, no matter what mode is used */
|
||||
srand(1);
|
||||
|
||||
for (state.x = 15; state.x > -1; state.x--) {
|
||||
for (state.y = 0; state.y < 16; state.y++) {
|
||||
PyObject *blockid = NULL;
|
||||
@@ -402,8 +412,18 @@ chunk_render(PyObject *self, PyObject *args) {
|
||||
PyObject *tmp;
|
||||
|
||||
unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z);
|
||||
if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) {
|
||||
state.block_data = ancilData;
|
||||
/* block that need pseudo ancildata:
|
||||
* grass, water, glass, chest, restone wire,
|
||||
* ice, fence and portal. */
|
||||
if ((state.block == 2) || (state.block == 9) ||
|
||||
(state.block == 20) || (state.block == 54) ||
|
||||
(state.block == 55) || (state.block == 79) ||
|
||||
(state.block == 85) || (state.block == 90)) {
|
||||
ancilData = generate_pseudo_data(&state, ancilData);
|
||||
state.block_pdata = ancilData;
|
||||
} else {
|
||||
state.block_pdata = 0;
|
||||
}
|
||||
|
||||
tmp = PyTuple_New(2);
|
||||
@@ -421,6 +441,7 @@ chunk_render(PyObject *self, PyObject *args) {
|
||||
if (t != NULL && t != Py_None)
|
||||
{
|
||||
PyObject *src, *mask, *mask_light;
|
||||
int randx = 0, randy = 0;
|
||||
src = PyTuple_GetItem(t, 0);
|
||||
mask = PyTuple_GetItem(t, 1);
|
||||
mask_light = PyTuple_GetItem(t, 2);
|
||||
@@ -428,7 +449,21 @@ chunk_render(PyObject *self, PyObject *args) {
|
||||
if (mask == Py_None)
|
||||
mask = src;
|
||||
|
||||
if (state.block == 31) {
|
||||
/* add a random offset to the postion of the tall grass to make it more wild */
|
||||
randx = rand() % 6 + 1 - 3;
|
||||
randy = rand() % 6 + 1 - 3;
|
||||
state.imgx += randx;
|
||||
state.imgy += randy;
|
||||
}
|
||||
|
||||
rendermode->draw(rm_data, &state, src, mask, mask_light);
|
||||
|
||||
if (state.block == 31) {
|
||||
/* undo the random offsets */
|
||||
state.imgx -= randx;
|
||||
state.imgy -= randy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ typedef struct {
|
||||
/* the block position and type, and the block array */
|
||||
int x, y, z;
|
||||
unsigned char block;
|
||||
unsigned char block_data;
|
||||
unsigned char block_pdata;
|
||||
PyObject *blockdata_expanded;
|
||||
PyObject *blocks;
|
||||
PyObject *up_left_blocks;
|
||||
@@ -293,7 +293,22 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject
|
||||
self = (RenderModeLighting *)data;
|
||||
x = state->x, y = state->y, z = state->z;
|
||||
|
||||
if (is_transparent(state->block)) {
|
||||
if ((state->block == 9) || (state->block == 79)) { /* special case for water and ice */
|
||||
/* looks like we need a new case for lighting, there are
|
||||
* blocks that are transparent for occlusion calculations and
|
||||
* need per-face shading if the face is drawn. */
|
||||
if ((state->block_pdata & 16) == 16) {
|
||||
do_shading_with_mask(self, state, x, y, z+1, self->facemasks[0]);
|
||||
}
|
||||
if ((state->block_pdata & 2) == 2) { /* bottom left */
|
||||
do_shading_with_mask(self, state, x-1, y, z, self->facemasks[1]);
|
||||
}
|
||||
if ((state->block_pdata & 4) == 4) { /* bottom right */
|
||||
do_shading_with_mask(self, state, x, y+1, z, self->facemasks[2]);
|
||||
}
|
||||
/* leaves are transparent for occlusion calculations but they
|
||||
* per face-shading to look as in game */
|
||||
} else if (is_transparent(state->block) && (state->block != 18)) {
|
||||
/* transparent: do shading on whole block */
|
||||
do_shading_with_mask(self, state, x, y, z, mask_light);
|
||||
} else {
|
||||
@@ -125,7 +125,6 @@ rendermode_normal_occluded(void *data, RenderState *state) {
|
||||
static void
|
||||
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
||||
RenderModeNormal *self = (RenderModeNormal *)data;
|
||||
int randx = 0,randy = 0;
|
||||
unsigned char data_byte;
|
||||
|
||||
/* first, check to see if we should use biome-compatible src, mask */
|
||||
@@ -133,11 +132,6 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
|
||||
if (state->block == 18) {
|
||||
src = mask = self->leaf_texture;
|
||||
} else if (state->block == 31) {
|
||||
/* add a random offset to the postion of the tall grass to make it more wild */
|
||||
randx = rand() % 6 + 1 - 3;
|
||||
randy = rand() % 6 + 1 - 3;
|
||||
state->imgx = state->imgx + randx;
|
||||
state->imgy = state->imgy + randy;
|
||||
data_byte = getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z);
|
||||
if (data_byte == 1) {
|
||||
src = mask = self->tall_grass_texture;
|
||||
@@ -14,6 +14,7 @@
|
||||
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import imp
|
||||
import os
|
||||
import os.path
|
||||
import zipfile
|
||||
@@ -27,13 +28,13 @@ import util
|
||||
import composite
|
||||
|
||||
_find_file_local_path = None
|
||||
def _find_file(filename, mode="rb"):
|
||||
def _find_file(filename, mode="rb", verbose=False):
|
||||
"""Searches for the given file and returns an open handle to it.
|
||||
This searches the following locations in this order:
|
||||
|
||||
* the textures_path given in the config file (if present)
|
||||
* The program dir (same dir as this file)
|
||||
* The program dir / textures
|
||||
* The program dir (same dir as overviewer.py)
|
||||
* The overviewer_core textures dir
|
||||
* On Darwin, in /Applications/Minecraft
|
||||
* 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:
|
||||
path = os.path.join(_find_file_local_path, filename)
|
||||
if os.path.exists(path):
|
||||
if verbose: print "Found %s in '%s'" % (filename, path)
|
||||
return open(path, mode)
|
||||
|
||||
programdir = util.get_program_path()
|
||||
path = os.path.join(programdir, filename)
|
||||
if os.path.exists(path):
|
||||
if verbose: print "Found %s in '%s'" % (filename, path)
|
||||
return open(path, mode)
|
||||
|
||||
path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
|
||||
if os.path.exists(path):
|
||||
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":
|
||||
path = os.path.join("/Applications/Minecraft", filename)
|
||||
if os.path.exists(path):
|
||||
if verbose: print "Found %s in '%s'" % (filename, path)
|
||||
return open(path, mode)
|
||||
|
||||
# Find minecraft.jar.
|
||||
@@ -79,6 +89,7 @@ def _find_file(filename, mode="rb"):
|
||||
if os.path.exists(jarpath):
|
||||
try:
|
||||
jar = zipfile.ZipFile(jarpath)
|
||||
if verbose: print "Found %s in '%s'" % (filename, jarpath)
|
||||
return jar.open(filename)
|
||||
except (KeyError, IOError):
|
||||
pass
|
||||
@@ -151,13 +162,13 @@ def transform_image_side(img, blockID=None):
|
||||
# (don't just crop img, since we want the size of
|
||||
# img to be unchanged
|
||||
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)
|
||||
img = n
|
||||
if blockID in (78,): # snow
|
||||
# make the top three quarters transparent
|
||||
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)
|
||||
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
|
||||
|
||||
"""
|
||||
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
||||
img = Image.new("RGBA", (24,24), bgcolor)
|
||||
|
||||
original_texture = top.copy()
|
||||
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.
|
||||
|
||||
If top is a tuple then first member is the top image and the second
|
||||
member is an increment (integer) from 0 to 12. This increment will
|
||||
used to crop the side images to look like a block and to paste all
|
||||
the images increment pixels lower. Using increment = 6 will create
|
||||
a half-block.
|
||||
If top is a tuple then first item is the top image and the second
|
||||
item is an increment (integer) from 0 to 16 (pixels in the
|
||||
original minecraft texture). This increment will be used to crop the
|
||||
side images and to paste the top image increment pixels lower, so if
|
||||
you use an increment of 8, it willll draw a half-block.
|
||||
|
||||
NOTE: this method uses the top of the texture image (as done in
|
||||
minecraft with beds)
|
||||
NOTE: this method uses the bottom of the texture image (as done in
|
||||
minecraft with beds and cackes)
|
||||
|
||||
"""
|
||||
|
||||
increment = 0
|
||||
if isinstance(top, tuple):
|
||||
increment = top[1]
|
||||
crop_height = int(increment * 16./12.)
|
||||
increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
|
||||
crop_height = increment
|
||||
top = top[0]
|
||||
if side1 != None:
|
||||
side1 = side1.copy()
|
||||
ImageDraw.Draw(side1).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
ImageDraw.Draw(side1).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
if side2 != None:
|
||||
side2 = side2.copy()
|
||||
ImageDraw.Draw(side2).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
ImageDraw.Draw(side2).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
if side3 != None:
|
||||
side3 = side3.copy()
|
||||
ImageDraw.Draw(side3).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
ImageDraw.Draw(side3).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
if side4 != None:
|
||||
side4 = side4.copy()
|
||||
ImageDraw.Draw(side4).rectangle((0, 16 - crop_height,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
ImageDraw.Draw(side4).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
|
||||
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
||||
img = Image.new("RGBA", (24,24), bgcolor)
|
||||
|
||||
# first back sides
|
||||
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.putalpha(sidealpha)
|
||||
|
||||
composite.alpha_over(img, side1, (0,0 + increment), side1)
|
||||
composite.alpha_over(img, side1, (0,0), side1)
|
||||
|
||||
|
||||
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.putalpha(sidealpha2)
|
||||
|
||||
composite.alpha_over(img, side2, (12,0 + increment), side2)
|
||||
composite.alpha_over(img, side2, (12,0), side2)
|
||||
|
||||
if bottom != None :
|
||||
bottom = transform_image(bottom, blockID)
|
||||
composite.alpha_over(img, bottom, (0,12), top)
|
||||
composite.alpha_over(img, bottom, (0,12), bottom)
|
||||
|
||||
# front sides
|
||||
if side3 != None :
|
||||
@@ -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.putalpha(sidealpha)
|
||||
|
||||
composite.alpha_over(img, side3, (0,6 + increment), side3)
|
||||
composite.alpha_over(img, side3, (0,6), side3)
|
||||
|
||||
if side4 != None :
|
||||
side4 = transform_image_side(side4, blockID)
|
||||
@@ -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.putalpha(sidealpha)
|
||||
|
||||
composite.alpha_over(img, side4, (12,6 + increment), side4)
|
||||
composite.alpha_over(img, side4, (12,6), side4)
|
||||
|
||||
if top != None :
|
||||
top = transform_image(top, blockID)
|
||||
@@ -414,13 +425,13 @@ def _build_blockimages():
|
||||
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
|
||||
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
34, -1, 52, 48, 49,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
|
||||
34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1,
|
||||
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
||||
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4,
|
||||
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1,
|
||||
# 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, 67,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1,
|
||||
# 80 81 82 83 84 85 86 87 88 89 90 91
|
||||
66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102 # clay?
|
||||
]
|
||||
@@ -431,13 +442,13 @@ def _build_blockimages():
|
||||
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33,
|
||||
# 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
34, -1, 52, 48, 49,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
|
||||
34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1,
|
||||
# 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
||||
55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35,
|
||||
# 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
||||
36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1,
|
||||
# 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, 67,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1,
|
||||
# 80 81 82 83 84 85 86 87 88 89 90 91
|
||||
66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118
|
||||
]
|
||||
@@ -502,9 +513,7 @@ def generate_texture_tuple(img, blockid):
|
||||
|
||||
def generate_special_texture(blockID, data):
|
||||
"""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)
|
||||
# blocks need to be handled here (and in chunk.py)
|
||||
|
||||
if blockID == 2: # grass
|
||||
# data & 0x10 means SNOW sides
|
||||
@@ -542,33 +551,41 @@ def generate_special_texture(blockID, data):
|
||||
return generate_texture_tuple(img, blockID)
|
||||
|
||||
|
||||
if blockID == 9: # spring water, flowing water and waterfall water
|
||||
|
||||
watertexture = _load_image("water.png")
|
||||
if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice
|
||||
# water,glass and ice share the way to be rendered
|
||||
if blockID == 9:
|
||||
texture = _load_image("water.png")
|
||||
elif blockID == 20:
|
||||
texture = terrain_images[49]
|
||||
else:
|
||||
texture = terrain_images[67]
|
||||
|
||||
if (data & 0b10000) == 16:
|
||||
top = watertexture
|
||||
top = texture
|
||||
|
||||
else: top = None
|
||||
|
||||
if (data & 0b0001) == 1:
|
||||
side1 = watertexture # top left
|
||||
side1 = texture # top left
|
||||
else: side1 = None
|
||||
|
||||
if (data & 0b1000) == 8:
|
||||
side2 = watertexture # top right
|
||||
side2 = texture # top right
|
||||
else: side2 = None
|
||||
|
||||
if (data & 0b0010) == 2:
|
||||
side3 = watertexture # bottom left
|
||||
side3 = texture # bottom left
|
||||
else: side3 = None
|
||||
|
||||
if (data & 0b0100) == 4:
|
||||
side4 = watertexture # bottom right
|
||||
side4 = texture # bottom right
|
||||
else: side4 = None
|
||||
|
||||
img = _build_full_block(top,None,None,side3,side4)
|
||||
# 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)
|
||||
|
||||
|
||||
@@ -594,7 +611,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
|
||||
if blockID == 26: # bed
|
||||
increment = 5
|
||||
increment = 8
|
||||
left_face = None
|
||||
right_face = None
|
||||
if data & 0x8 == 0x8: # head of the bed
|
||||
@@ -638,6 +655,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
return generate_texture_tuple(img, blockID)
|
||||
|
||||
|
||||
if blockID == 31: # tall grass
|
||||
if data == 0: # dead shrub
|
||||
texture = terrain_images[55]
|
||||
@@ -652,6 +670,132 @@ def generate_special_texture(blockID, data):
|
||||
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 data == 0: # white
|
||||
top = side = terrain_images[64]
|
||||
@@ -719,7 +863,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
# compose a torch bigger than the normal
|
||||
# (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,(-5,-2))
|
||||
composite.alpha_over(torch,small,(-3,-2))
|
||||
@@ -745,17 +889,17 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
elif data == 5: # standing on the floor
|
||||
# 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))
|
||||
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((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, small_crop, (5,5))
|
||||
composite.alpha_over(img, small_crop, (6,5))
|
||||
composite.alpha_over(img, slice, (6,6))
|
||||
composite.alpha_over(img, slice, (7,5))
|
||||
composite.alpha_over(img, small_crop, (6,6))
|
||||
composite.alpha_over(img, small_crop, (7,6))
|
||||
composite.alpha_over(img, slice, (7,7))
|
||||
|
||||
return generate_texture_tuple(img, blockID)
|
||||
|
||||
@@ -765,7 +909,7 @@ def generate_special_texture(blockID, data):
|
||||
side1 = transform_image_side(firetexture)
|
||||
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, side2, (0,0), side2)
|
||||
@@ -812,14 +956,14 @@ def generate_special_texture(blockID, data):
|
||||
composite.alpha_over(img, tmp2, (0,6))
|
||||
|
||||
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)
|
||||
composite.alpha_over(img, tmp1, (0,6))
|
||||
tmp2 = _build_full_block(half_block_l, None, None, texture, side)
|
||||
composite.alpha_over(img, tmp2)
|
||||
|
||||
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)
|
||||
composite.alpha_over(img, tmp1, (0,6))
|
||||
tmp2 = _build_full_block(half_block_d, None, None, side, texture)
|
||||
@@ -936,7 +1080,7 @@ def generate_special_texture(blockID, data):
|
||||
bottom = redstone_wire_t.copy().rotate(90)
|
||||
|
||||
else:
|
||||
bottom = Image.new("RGBA", (16,16), (38,92,255,0))
|
||||
bottom = Image.new("RGBA", (16,16), bgcolor)
|
||||
if (data & 0b0001) == 1:
|
||||
composite.alpha_over(bottom,branch_top_left)
|
||||
|
||||
@@ -980,7 +1124,7 @@ def generate_special_texture(blockID, data):
|
||||
crop2 = transform_image_side(raw_crop, blockID)
|
||||
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, crop2, (6,3), crop2)
|
||||
composite.alpha_over(img, crop3, (6,3), crop3)
|
||||
@@ -1031,7 +1175,7 @@ def generate_special_texture(blockID, data):
|
||||
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))
|
||||
|
||||
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
||||
img = Image.new("RGBA", (24,24), bgcolor)
|
||||
|
||||
# W N ~90 E S ~270
|
||||
angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.)
|
||||
@@ -1069,8 +1213,8 @@ def generate_special_texture(blockID, data):
|
||||
swung=False
|
||||
|
||||
# mask out the high bits to figure out the orientation
|
||||
img = Image.new("RGBA", (24,24), (38,92,255,0))
|
||||
if (data & 0x03) == 0:
|
||||
img = Image.new("RGBA", (24,24), bgcolor)
|
||||
if (data & 0x03) == 0: # northeast corner
|
||||
if not swung:
|
||||
tex = transform_image_side(raw_door)
|
||||
composite.alpha_over(img, tex, (0,6), tex)
|
||||
@@ -1080,7 +1224,7 @@ def generate_special_texture(blockID, data):
|
||||
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
composite.alpha_over(img, tex, (0,0), tex)
|
||||
|
||||
if (data & 0x03) == 1:
|
||||
if (data & 0x03) == 1: # southeast corner
|
||||
if not swung:
|
||||
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
||||
composite.alpha_over(img, tex, (0,0), tex)
|
||||
@@ -1088,7 +1232,7 @@ def generate_special_texture(blockID, data):
|
||||
tex = transform_image_side(raw_door)
|
||||
composite.alpha_over(img, tex, (12,0), tex)
|
||||
|
||||
if (data & 0x03) == 2:
|
||||
if (data & 0x03) == 2: # southwest corner
|
||||
if not swung:
|
||||
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
||||
composite.alpha_over(img, tex, (12,0), tex)
|
||||
@@ -1096,7 +1240,7 @@ def generate_special_texture(blockID, data):
|
||||
tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
||||
composite.alpha_over(img, tex, (12,6), tex)
|
||||
|
||||
if (data & 0x03) == 3:
|
||||
if (data & 0x03) == 3: # northwest corner
|
||||
if not swung:
|
||||
tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT)
|
||||
composite.alpha_over(img, tex, (12,6), tex)
|
||||
@@ -1108,7 +1252,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
|
||||
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]
|
||||
#print "ladder is facing: %d" % data
|
||||
if data == 5:
|
||||
@@ -1133,7 +1277,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
|
||||
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 data & 0x8 == 0: # unpowered
|
||||
@@ -1216,7 +1360,7 @@ def generate_special_texture(blockID, data):
|
||||
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
|
||||
if data == 2: # east
|
||||
@@ -1250,7 +1394,6 @@ def generate_special_texture(blockID, data):
|
||||
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_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((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
||||
|
||||
@@ -1270,10 +1413,10 @@ def generate_special_texture(blockID, data):
|
||||
fence_other_side.putalpha(othersidealpha)
|
||||
|
||||
# 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_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.
|
||||
# Create needed images
|
||||
@@ -1301,7 +1444,7 @@ def generate_special_texture(blockID, data):
|
||||
fence_small_side.putalpha(sidealpha)
|
||||
|
||||
# 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.
|
||||
# These postitions are strange because the small sticks of the
|
||||
@@ -1351,7 +1494,7 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
if blockID == 90: # portal
|
||||
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)
|
||||
otherside = side.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
@@ -1380,23 +1523,21 @@ def generate_special_texture(blockID, data):
|
||||
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
|
||||
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, otherside, (11,13), otherside) # workaround, fixes a hole
|
||||
composite.alpha_over(img, otherside, (12,12), otherside)
|
||||
composite.alpha_over(img, side, (1,6), side)
|
||||
composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole
|
||||
composite.alpha_over(img, otherside, (12,6), otherside)
|
||||
composite.alpha_over(img, top, (0,6), top)
|
||||
|
||||
return generate_texture_tuple(img, blockID)
|
||||
|
||||
|
||||
if blockID in (93, 94): # redstone repeaters, ON and OFF
|
||||
# NOTE: this function uses the redstone torches generated above,
|
||||
# this must run after the function of the torches.
|
||||
|
||||
if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF
|
||||
# generate the diode
|
||||
top = terrain_images[131] if blockID == 93 else terrain_images[147]
|
||||
side = terrain_images[5]
|
||||
increment = 9
|
||||
increment = 13
|
||||
|
||||
if (data & 0x3) == 0: # pointing east
|
||||
pass
|
||||
@@ -1412,11 +1553,21 @@ def generate_special_texture(blockID, data):
|
||||
|
||||
img = _build_full_block( (top, increment), None, None, side, side)
|
||||
|
||||
# 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])
|
||||
# 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!
|
||||
# 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))
|
||||
|
||||
@@ -1521,7 +1672,7 @@ def generate_special_texture(blockID, data):
|
||||
img = _build_full_block(None, None, None, texture, None)
|
||||
|
||||
elif data & 0x4 == 0: # closed trapdoor
|
||||
img = _build_full_block((texture, 9), None, None, texture, texture)
|
||||
img = _build_full_block((texture, 12), None, None, texture, texture)
|
||||
|
||||
return generate_texture_tuple(img, blockID)
|
||||
|
||||
@@ -1598,9 +1749,10 @@ def getBiomeData(worlddir, chunkX, chunkY):
|
||||
# (when adding new blocks here and in generate_special_textures,
|
||||
# please, if possible, keep the ascending order of blockid value)
|
||||
|
||||
special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 31, 35, 43, 44,
|
||||
50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66,
|
||||
67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94, 96])
|
||||
special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33,
|
||||
34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62,
|
||||
63, 64, 65, 66, 67, 68, 71, 75, 76, 79, 85, 86, 90,
|
||||
91, 92, 93, 94, 96])
|
||||
|
||||
# this is a map of special blockIDs to a list of all
|
||||
# possible values for ancillary data that it might have.
|
||||
@@ -1610,10 +1762,14 @@ special_map = {}
|
||||
special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings)
|
||||
special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data
|
||||
special_map[17] = range(3) # wood: normal, birch and pine
|
||||
special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data
|
||||
special_map[26] = range(12) # bed, orientation
|
||||
special_map[23] = range(6) # dispensers, orientation
|
||||
special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered
|
||||
special_map[28] = range(6) # detector rail, orientation/slope
|
||||
special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out
|
||||
special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out
|
||||
special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal
|
||||
special_map[35] = range(16) # wool, colored and white
|
||||
special_map[43] = range(4) # stone, sandstone, wooden and cobblestone double-slab
|
||||
special_map[44] = range(4) # stone, sandstone, wooden and cobblestone slab
|
||||
@@ -1622,7 +1778,7 @@ special_map[51] = range(16) # fire, position in the block (not implemented)
|
||||
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[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[61] = range(6) # furnace, orientation
|
||||
special_map[62] = range(6) # burning furnace, orientation
|
||||
@@ -1635,13 +1791,14 @@ special_map[68] = (2,3,4,5) # wall sing, orientation
|
||||
special_map[71] = range(16) # iron door, open/close and orientation
|
||||
special_map[75] = (1,2,3,4,5) # off redstone torch, orientation
|
||||
special_map[76] = (1,2,3,4,5) # on redstone torch, orientation
|
||||
special_map[79] = range(32) # ice, used to only render the exterior surface, uses pseudo data
|
||||
special_map[85] = range(17) # fences, all the possible combination, uses pseudo data
|
||||
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[91] = range(5) # jack-o-lantern, orientation
|
||||
special_map[92] = range(6) # cake!
|
||||
special_map[93] = range(16) # OFF redstone repeater, orientation and delay (delay not implemented)
|
||||
special_map[94] = range(16) # ON redstone repeater, orientation and delay (delay not implemented)
|
||||
special_map[92] = range(6) # cake, eaten amount, (not implemented)
|
||||
special_map[93] = range(16) # OFF redstone repeater, orientation and delay
|
||||
special_map[94] = range(16) # ON redstone repeater, orientation and delay
|
||||
special_map[96] = range(8) # trapdoor, open, closed, orientation
|
||||
|
||||
# grass and leaves are graysacle in terrain.png
|
||||
@@ -1656,6 +1813,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
|
||||
|
||||
# placeholders that are generated in generate()
|
||||
bgcolor = None
|
||||
terrain_images = None
|
||||
blockmap = None
|
||||
biome_grass_texture = None
|
||||
@@ -1664,9 +1822,12 @@ biome_tall_fern_texture = None
|
||||
biome_leaf_texture = None
|
||||
specialblockmap = None
|
||||
|
||||
def generate(path=None):
|
||||
global _find_file_local_path
|
||||
def generate(path=None,texture_size=24,bgc = (26,26,26,0)):
|
||||
global bgcolor
|
||||
bgcolor = bgc
|
||||
global _find_file_local_path, texture_dimensions
|
||||
_find_file_local_path = path
|
||||
texture_dimensions = (texture_size, texture_size)
|
||||
|
||||
# This maps terainids to 16x16 images
|
||||
global terrain_images
|
||||
@@ -1690,3 +1851,30 @@ def generate(path=None):
|
||||
for blockID in special_blocks:
|
||||
for data in special_map[blockID]:
|
||||
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data)
|
||||
|
||||
if texture_size != 24:
|
||||
# rescale biome textures.
|
||||
biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
biome_leaf_texture = biome_leaf_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
biome_tall_grass_texture = biome_tall_grass_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
biome_tall_fern_texture = biome_tall_fern_texture.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
|
||||
# rescale the normal block images
|
||||
for i in range(len(blockmap)):
|
||||
if blockmap[i] != None:
|
||||
block = blockmap[i]
|
||||
alpha = block[1]
|
||||
block = block[0]
|
||||
block.putalpha(alpha)
|
||||
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
blockmap[i] = generate_texture_tuple(scaled_block, i)
|
||||
|
||||
# rescale the special block images
|
||||
for blockid, data in iter(specialblockmap):
|
||||
block = specialblockmap[(blockid,data)]
|
||||
if block != None:
|
||||
alpha = block[1]
|
||||
block = block[0]
|
||||
block.putalpha(alpha)
|
||||
scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS)
|
||||
specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid)
|
||||
@@ -21,19 +21,22 @@ import imp
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
def get_program_path():
|
||||
if hasattr(sys, "frozen") or imp.is_frozen("__main__"):
|
||||
return os.path.dirname(sys.executable)
|
||||
else:
|
||||
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:
|
||||
return os.path.dirname(sys.argv[0])
|
||||
|
||||
|
||||
|
||||
def findGitVersion():
|
||||
# does not require git, very likely to work everywhere
|
||||
def findGitHash():
|
||||
this_dir = get_program_path()
|
||||
if os.path.exists(os.path.join(this_dir,".git")):
|
||||
with open(os.path.join(this_dir,".git","HEAD")) as f:
|
||||
@@ -46,6 +49,24 @@ def findGitVersion():
|
||||
else:
|
||||
return data
|
||||
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:
|
||||
import overviewer_version
|
||||
return overviewer_version.VERSION
|
||||
@@ -209,9 +209,9 @@ class World(object):
|
||||
chunkX = spawnX/16
|
||||
chunkY = spawnZ/16
|
||||
|
||||
try:
|
||||
## The filename of this chunk
|
||||
chunkFile = self.get_region_path(chunkX, chunkY)
|
||||
|
||||
if chunkFile is not None:
|
||||
data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1]
|
||||
if data is not None:
|
||||
@@ -227,7 +227,9 @@ class World(object):
|
||||
spawnY += 1
|
||||
if spawnY == 128:
|
||||
break
|
||||
|
||||
except ChunkCorrupt:
|
||||
#ignore corrupt spawn, and continue
|
||||
pass
|
||||
self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ,
|
||||
msg="Spawn", type="spawn", chunk=(chunkX, chunkY)))
|
||||
self.spawn = (spawnX, spawnY, spawnZ)
|
||||
218
setup.py
Normal file → Executable file
@@ -1,25 +1,90 @@
|
||||
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.clean import clean
|
||||
from distutils.command.build_ext import build_ext
|
||||
from distutils.command.sdist import sdist
|
||||
from distutils.cmd import Command
|
||||
from distutils.dir_util import remove_tree
|
||||
from distutils.sysconfig import get_python_inc
|
||||
from distutils import log
|
||||
import os, os.path
|
||||
import sys, os, os.path
|
||||
import glob
|
||||
import platform
|
||||
import time
|
||||
import overviewer_core.util as util
|
||||
|
||||
try:
|
||||
import py2exe
|
||||
except ImportError:
|
||||
py2exe = None
|
||||
|
||||
try:
|
||||
import py2app
|
||||
from setuptools.extension import Extension
|
||||
except ImportError:
|
||||
py2app = None
|
||||
|
||||
# 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['options'] = {}
|
||||
setup_kwargs['ext_modules'] = []
|
||||
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
|
||||
def recursive_data_files(src, dest=None):
|
||||
if dest is None:
|
||||
dest = src
|
||||
|
||||
ret = []
|
||||
for dirpath, dirnames, filenames in os.walk(src):
|
||||
current_dest = os.path.relpath(dirpath, src)
|
||||
if current_dest == '.':
|
||||
current_dest = dest
|
||||
else:
|
||||
current_dest = os.path.join(dest, current_dest)
|
||||
|
||||
current_sources = map(lambda p: os.path.join(dirpath, p), filenames)
|
||||
|
||||
ret.append((current_dest, current_sources))
|
||||
return ret
|
||||
|
||||
# helper to create a 'package_data'-type sequence recursively for a given dir
|
||||
def recursive_package_data(src, package_dir='overviewer_core'):
|
||||
full_src = os.path.join(package_dir, src)
|
||||
ret = []
|
||||
for dirpath, dirnames, filenames in os.walk(full_src, topdown=False):
|
||||
current_path = os.path.relpath(dirpath, package_dir)
|
||||
for filename in filenames:
|
||||
ret.append(os.path.join(current_path, filename))
|
||||
|
||||
return ret
|
||||
|
||||
#
|
||||
# py2exe options
|
||||
@@ -27,9 +92,9 @@ setup_kwargs['cmdclass'] = {}
|
||||
|
||||
if py2exe is not None:
|
||||
setup_kwargs['console'] = ['overviewer.py']
|
||||
setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png', 'textures/portal.png']),
|
||||
('', ['COPYING.txt', 'README.rst']),
|
||||
('web_assets', glob.glob('web_assets/*'))]
|
||||
setup_kwargs['data_files'] = [('', doc_files)]
|
||||
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/textures', 'textures')
|
||||
setup_kwargs['data_files'] += recursive_data_files('overviewer_core/data/web_assets', 'web_assets')
|
||||
setup_kwargs['zipfile'] = None
|
||||
if platform.system() == 'Windows' and '64bit' in platform.architecture():
|
||||
b = 3
|
||||
@@ -37,6 +102,27 @@ if py2exe is not None:
|
||||
b = 1
|
||||
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
|
||||
#
|
||||
@@ -52,25 +138,31 @@ except AttributeError:
|
||||
try:
|
||||
pil_include = os.environ['PIL_INCLUDE_DIR'].split(os.pathsep)
|
||||
except:
|
||||
pil_include = []
|
||||
pil_include = [ os.path.join(get_python_inc(plat_specific=1), 'Imaging') ]
|
||||
if not os.path.exists(pil_include[0]):
|
||||
pil_include = [ ]
|
||||
|
||||
|
||||
# used to figure out what files to compile
|
||||
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 += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes)
|
||||
c_overviewer_files += ['src/Draw.c']
|
||||
c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h']
|
||||
c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c']
|
||||
c_overviewer_files += map(lambda mode: 'rendermode-%s.c' % (mode,), render_modes)
|
||||
c_overviewer_files += ['Draw.c']
|
||||
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
|
||||
# (NOT in build/)
|
||||
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
|
||||
# and the version file
|
||||
class CustomClean(clean):
|
||||
def run(self):
|
||||
# do the normal cleanup
|
||||
@@ -79,7 +171,7 @@ class CustomClean(clean):
|
||||
# try to remove '_composite.{so,pyd,...}' extension,
|
||||
# regardless of the current system's extension name convention
|
||||
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
|
||||
if os.path.exists(fname):
|
||||
try:
|
||||
@@ -93,7 +185,54 @@ class CustomClean(clean):
|
||||
log.debug("'%s' does not exist -- can't clean it",
|
||||
pretty_fname)
|
||||
|
||||
class CustomBuild(build_ext):
|
||||
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)
|
||||
|
||||
# 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):
|
||||
c = self.compiler.compiler_type
|
||||
if c == "msvc":
|
||||
@@ -101,32 +240,39 @@ class CustomBuild(build_ext):
|
||||
for e in self.extensions:
|
||||
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)
|
||||
|
||||
|
||||
if py2exe is not None:
|
||||
# define a subclass of py2exe to build our version file on the fly
|
||||
class CustomPy2exe(py2exe.build_exe.py2exe):
|
||||
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:
|
||||
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
|
||||
f = _find_file("terrain.png", verbose=True)
|
||||
except IOError:
|
||||
log.error("Could not find the file terrain.png")
|
||||
return
|
||||
|
||||
h = hashlib.sha1()
|
||||
h.update(f.read())
|
||||
log.info("Hash of terrain.png file is: %s", h.hexdigest())
|
||||
|
||||
setup_kwargs['cmdclass']['clean'] = CustomClean
|
||||
setup_kwargs['cmdclass']['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)
|
||||
|
||||
|
||||
print "\nBuild Complete"
|
||||
|
||||