0

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrew Chin
2011-05-30 16:02:52 -04:00
19 changed files with 659 additions and 187 deletions

View File

@@ -28,6 +28,7 @@ for many different parts of the code.
* Aaron Griffith <aargri@gmail.com> * Aaron Griffith <aargri@gmail.com>
* Alex Headley <aheadley@waysaboutstuff.com> * Alex Headley <aheadley@waysaboutstuff.com>
* Alex Jurkiewicz <alex@bluebottle.net.au> * Alex Jurkiewicz <alex@bluebottle.net.au>
* Michael Writhe <michael@writhem.com>
* Xon <Xon@localhost> * Xon <Xon@localhost>
------------------------ ------------------------
@@ -41,6 +42,7 @@ feature.
* Kyle Brantley <kyle@averageurl.com> * Kyle Brantley <kyle@averageurl.com>
* cbarber <CraigBarber@taryx.com> * cbarber <CraigBarber@taryx.com>
* Alex Cline <cline@vivisimo.com> * Alex Cline <cline@vivisimo.com>
* 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>
@@ -51,3 +53,4 @@ feature.
* Gregory Short <gshort2@gmail.com> * Gregory Short <gshort2@gmail.com>
* Sam Steele <sam@sigbox.c99.org> * Sam Steele <sam@sigbox.c99.org>
* timwolla <timwolla@mail.develfusion.com> * timwolla <timwolla@mail.develfusion.com>
* Jeffrey Warren <warren@mit.edu>

View File

@@ -191,11 +191,18 @@ Options
--list-rendermodes --list-rendermodes
List the available render modes, and a short description of each. List the available render modes, and a short description of each.
--settings=PATH
Use this option to load settings from a file. The format of this file is
given below.
Settings Settings
-------- --------
You can optionally store settings in a file named settings.py. It is a regular
python script, so you can use any python functions or modules you want. You can optionally store settings in a file named settings.py (or really,
anything you want). It is a regular python script, so you can use any python
functions or modules you want. To use a settings file, use the --settings
command line option.
For a sample settings file, look at 'sample.settings.py'. Note that this file For a sample settings file, look at 'sample.settings.py'. Note that this file
is not meant to be used directly, but instead it should be used as a is not meant to be used directly, but instead it should be used as a
@@ -241,6 +248,18 @@ web_assets_hook
This function should accept one argument: a QuadtreeGen object. This function should accept one argument: a QuadtreeGen object.
web_assets_path
This option lets you provide alternative web assets to use when
rendering. The contents of this folder will be copied into the output folder
during render, and will overwrite any default files already copied by
Overviewer. See the web_assets folder included with Overviewer for the
default assets.
textures_path
This is like web_assets_path, but instead it provides an alternative texture
source. Overviewer looks in here for terrain.png and other textures before
it looks anywhere else.
Viewing the Results Viewing the Results
------------------- -------------------
Within the output directory you will find two things: an index.html file, and a Within the output directory you will find two things: an index.html file, and a

View File

@@ -114,9 +114,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, 27, 28, 37, 38, 39, 40, 44, 50, transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 37, 38, 39, 40,
51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69,
72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 92]) 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92,
93, 94])
# 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,

View File

@@ -22,6 +22,9 @@ class ConfigOptionParser(object):
self.customArgs = ["required", "commandLineOnly", "default", "listify", "listdelim", "choices"] self.customArgs = ["required", "commandLineOnly", "default", "listify", "listdelim", "choices"]
self.requiredArgs = [] self.requiredArgs = []
# add the *very* special config-file path option
self.add_option("--settings", dest="config_file", help="Specifies a settings file to load, by name. This file's format is discussed in the README.", metavar="PATH", type="string", commandLineOnly=True)
def display_config(self): def display_config(self):
logging.info("Using the following settings:") logging.info("Using the following settings:")
@@ -68,8 +71,14 @@ class ConfigOptionParser(object):
# if this has a default, use that to seed the globals dict # if this has a default, use that to seed the globals dict
if a.get("default", None): g[n] = a['default'] if a.get("default", None): g[n] = a['default']
g['args'] = args g['args'] = args
try: try:
if options.config_file:
self.configFile = options.config_file
elif os.path.exists(self.configFile):
# warn about automatic loading
logging.warning("Automatic settings.py loading is DEPRECATED, and may be removed in the future. Please use --settings instead.")
if os.path.exists(self.configFile): if os.path.exists(self.configFile):
execfile(self.configFile, g, l) execfile(self.configFile, g, l)
except NameError, ex: except NameError, ex:

View File

@@ -68,6 +68,7 @@ class MapGen(object):
self.skipjs = configInfo.get('skipjs', None) self.skipjs = configInfo.get('skipjs', None)
self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_hook = configInfo.get('web_assets_hook', None)
self.web_assets_path = configInfo.get('web_assets_path', None)
self.bg_color = configInfo.get('bg_color') self.bg_color = configInfo.get('bg_color')
if not len(quadtrees) > 0: if not len(quadtrees) > 0:
@@ -81,14 +82,28 @@ class MapGen(object):
raise ValueError("all the given quadtrees must have the same destdir and world") raise ValueError("all the given quadtrees must have the same destdir and world")
self.quadtrees = quadtrees self.quadtrees = quadtrees
def go(self, procs): def go(self, procs):
"""Writes out config.js, marker.js, and region.js """Writes out config.js, marker.js, and region.js
Copies web assets into the destdir""" Copies web assets into the destdir"""
zoomlevel = self.p zoomlevel = self.p
configpath = os.path.join(util.get_program_path(), "overviewerConfig.js")
config = open(configpath, 'r').read() bgcolor = (int(self.bg_color[1:3],16), int(self.bg_color[3:5],16), int(self.bg_color[5:7],16), 0)
blank = Image.new("RGBA", (1,1), bgcolor)
# Write a blank image
for quadtree in self.quadtrees:
tileDir = os.path.join(self.destdir, quadtree.tiledir)
if not os.path.exists(tileDir): os.mkdir(tileDir)
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)
# do the same with the local copy, if we have it
if self.web_assets_path:
mirror_dir(self.web_assets_path, self.destdir)
# replace the config js stuff
config = open(os.path.join(self.destdir, 'overviewerConfig.js'), 'r').read()
config = config.replace( config = config.replace(
"{minzoom}", str(0)) "{minzoom}", str(0))
config = config.replace( config = config.replace(
@@ -111,23 +126,13 @@ class MapGen(object):
with open(os.path.join(self.destdir, "overviewerConfig.js"), 'w') as output: with open(os.path.join(self.destdir, "overviewerConfig.js"), 'w') as output:
output.write(config) output.write(config)
bgcolor = (int(self.bg_color[1:3],16), int(self.bg_color[3:5],16), int(self.bg_color[5:7],16), 0) # Add time and version in index.html
blank = Image.new("RGBA", (1,1), bgcolor)
# Write a blank image
for quadtree in self.quadtrees:
tileDir = os.path.join(self.destdir, quadtree.tiledir)
if not os.path.exists(tileDir): os.mkdir(tileDir)
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)
# Add time in index.html
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 +0000", gmtime()))) "{time}", str(strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())))
index = index.replace("{version}", util.findGitVersion())
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

@@ -93,7 +93,7 @@ def main():
parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int") parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int")
parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True) parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True)
parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True) parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True)
parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.") parser.add_option("--regionlist", dest="regionlist", help="A file containing, on each line, a path to a regionlist to update. Instead of scanning the world directory for regions, it will just use this list. Normal caching rules still apply.")
parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by commas. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True) parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by commas. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True)
parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", help="List available render modes and exit.", commandLineOnly=True) parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", help="List available render modes and exit.", commandLineOnly=True)
parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.", configFileOnly=True ) parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.", configFileOnly=True )
@@ -101,6 +101,8 @@ def main():
parser.add_option("--bg_color", dest="bg_color", help="Configures the background color for the GoogleMap output. Specify in #RRGGBB format", configFileOnly=True, type="string", default="#1A1A1A") parser.add_option("--bg_color", dest="bg_color", help="Configures the background color for the GoogleMap output. Specify in #RRGGBB format", configFileOnly=True, type="string", default="#1A1A1A")
parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True) parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True)
parser.add_option("--web-assets-hook", dest="web_assets_hook", help="If provided, run this function after the web assets have been copied, but before actual tile rendering begins. It should accept a QuadtreeGen object as its only argument.", action="store", metavar="SCRIPT", type="function", configFileOnly=True) parser.add_option("--web-assets-hook", dest="web_assets_hook", help="If provided, run this function after the web assets have been copied, but before actual tile rendering begins. It should accept a QuadtreeGen object as its only argument.", action="store", metavar="SCRIPT", type="function", configFileOnly=True)
parser.add_option("--web-assets-path", dest="web_assets_path", help="Specifies a non-standard web_assets directory to use. Files here will overwrite the default web assets.", metavar="PATH", type="string", configFileOnly=True)
parser.add_option("--textures-path", dest="textures_path", help="Specifies a non-standard textures path, from which terrain.png and other textures are loaded.", metavar="PATH", type="string", configFileOnly=True)
parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.") parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.")
parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.") parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.")
parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js")
@@ -164,10 +166,18 @@ def main():
logging.error("Invalid world number") logging.error("Invalid world number")
sys.exit(1) sys.exit(1)
if len(args) != 2: if len(args) < 2:
if options.delete: if options.delete:
return delete_all(worlddir, None) return delete_all(worlddir, None)
parser.error("Where do you want to save the tiles?") logging.error("Where do you want to save the tiles?")
sys.exit(1)
elif len(args) > 2:
if options.delete:
return delete_all(worlddir, None)
parser.print_help()
logging.error("Sorry, you specified too many arguments")
sys.exit(1)
destdir = args[1] destdir = args[1]
if options.display_config: if options.display_config:
@@ -179,10 +189,10 @@ def main():
if options.delete: if options.delete:
return delete_all(worlddir, destdir) return delete_all(worlddir, destdir)
if options.chunklist: if options.regionlist:
chunklist = open(options.chunklist, 'r') regionlist = map(str.strip, open(options.regionlist, 'r'))
else: else:
chunklist = None regionlist = None
if options.imgformat: if options.imgformat:
if options.imgformat not in ('jpg','png'): if options.imgformat not in ('jpg','png'):
@@ -211,7 +221,7 @@ def main():
logging.info("Notice: Not using biome data for tinting") logging.info("Notice: Not using biome data for tinting")
# First do world-level preprocessing # First do world-level preprocessing
w = world.World(worlddir, useBiomeData=useBiomeData) w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist)
w.go(options.procs) w.go(options.procs)
logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode))
@@ -234,7 +244,7 @@ def main():
qtree.go(options.procs) qtree.go(options.procs)
# create the distributed render # create the distributed render
r = rendernode.RenderNode(q) r = rendernode.RenderNode(q, options)
# write out the map and web assets # write out the map and web assets
m = googlemap.MapGen(q, configInfo=options) m = googlemap.MapGen(q, configInfo=options)

View File

@@ -246,7 +246,7 @@ class QuadtreeGen(object):
regiony = regiony_ regiony = regiony_
_, _, c, mcr = get_region((regionx, regiony),(None,None,None,None)) _, _, c, mcr = get_region((regionx, regiony),(None,None,None,None))
if c is not None and mcr.chunkExists(chunkx,chunky): if c is not None and mcr.chunkExists(chunkx,chunky):
chunklist.append((col, row, chunkx, chunky, c)) chunklist.append((col, row, chunkx, chunky, c))
return chunklist return chunklist
@@ -427,6 +427,8 @@ class QuadtreeGen(object):
for col, row, chunkx, chunky, regionfile in chunks: for col, row, chunkx, chunky, regionfile in chunks:
# check region file mtime first. # check region file mtime first.
region,regionMtime = get_region_mtime(regionfile) region,regionMtime = get_region_mtime(regionfile)
if self.world.regionlist and region._filename not in self.world.regionlist:
continue
if regionMtime <= tile_mtime: if regionMtime <= tile_mtime:
continue continue

View File

@@ -26,6 +26,8 @@ import collections
import json import json
import logging import logging
import util import util
import textures
import c_overviewer
import cPickle import cPickle
import stat import stat
import errno import errno
@@ -59,14 +61,22 @@ def pool_initializer(rendernode):
logging.debug("Child process {0}".format(os.getpid())) logging.debug("Child process {0}".format(os.getpid()))
#stash the quadtree objects in a global variable after fork() for windows compat. #stash the quadtree objects in a global variable after fork() for windows compat.
global child_rendernode global child_rendernode
child_rendernode = rendernode child_rendernode = rendernode
# make sure textures are generated for this process
# and initialize c_overviewer
textures.generate(path=rendernode.options.get('textures_path', None))
c_overviewer.init_chunk_render()
# load biome data in each process, if needed
for quadtree in rendernode.quadtrees: for quadtree in rendernode.quadtrees:
if quadtree.world.useBiomeData: if quadtree.world.useBiomeData:
import textures
# make sure we've at least *tried* to load the color arrays in this process... # make sure we've at least *tried* to load the color arrays in this process...
textures.prepareBiomeData(quadtree.world.worlddir) textures.prepareBiomeData(quadtree.world.worlddir)
if not textures.grasscolor or not textures.foliagecolor: if not textures.grasscolor or not textures.foliagecolor:
raise Exception("Can't find grasscolor.png or foliagecolor.png") raise Exception("Can't find grasscolor.png or foliagecolor.png")
# only load biome data once
break
#http://docs.python.org/library/itertools.html #http://docs.python.org/library/itertools.html
def roundrobin(iterables): def roundrobin(iterables):
@@ -84,12 +94,13 @@ def roundrobin(iterables):
class RenderNode(object): class RenderNode(object):
def __init__(self, quadtrees): def __init__(self, quadtrees, options):
"""Distributes the rendering of a list of quadtrees.""" """Distributes the rendering of a list of quadtrees."""
if not len(quadtrees) > 0: if not len(quadtrees) > 0:
raise ValueError("there must be at least one quadtree to work on") raise ValueError("there must be at least one quadtree to work on")
self.options = options
self.quadtrees = quadtrees self.quadtrees = quadtrees
#bind an index value to the quadtree so we can find it again #bind an index value to the quadtree so we can find it again
#and figure out which worlds are where #and figure out which worlds are where

View File

@@ -27,8 +27,8 @@ setup_kwargs['cmdclass'] = {}
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']), setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png', 'textures/portal.png']),
('', ['overviewerConfig.js', 'COPYING.txt', 'README.rst']), ('', ['COPYING.txt', 'README.rst']),
('web_assets', glob.glob('web_assets/*'))] ('web_assets', glob.glob('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():

View File

@@ -24,43 +24,41 @@ static PyObject *special_blocks = NULL;
static PyObject *specialblockmap = NULL; static PyObject *specialblockmap = NULL;
static PyObject *transparent_blocks = NULL; static PyObject *transparent_blocks = NULL;
int init_chunk_render(void) { PyObject *init_chunk_render(PyObject *self, PyObject *args) {
/* if blockmap (or any of these) is not NULL, then that means that we've /* this function only needs to be called once, anything more is an
* somehow called this function twice. error out so we can notice this * error... */
* */ if (blockmap) {
if (blockmap) return 1; PyErr_SetString(PyExc_RuntimeError, "init_chunk_render should only be called once per process.");
return NULL;
}
textures = PyImport_ImportModule("textures"); textures = PyImport_ImportModule("textures");
/* ensure none of these pointers are NULL */ /* ensure none of these pointers are NULL */
if ((!textures)) { if ((!textures)) {
fprintf(stderr, "\ninit_chunk_render failed to load; textures\n"); return NULL;
PyErr_Print();
return 1;
} }
chunk_mod = PyImport_ImportModule("chunk"); chunk_mod = PyImport_ImportModule("chunk");
/* ensure none of these pointers are NULL */ /* ensure none of these pointers are NULL */
if ((!chunk_mod)) { if ((!chunk_mod)) {
fprintf(stderr, "\ninit_chunk_render failed to load; chunk\n"); return NULL;
PyErr_Print();
return 1;
} }
blockmap = PyObject_GetAttrString(textures, "blockmap"); blockmap = PyObject_GetAttrString(textures, "blockmap");
if (!blockmap)
return NULL;
special_blocks = PyObject_GetAttrString(textures, "special_blocks"); special_blocks = PyObject_GetAttrString(textures, "special_blocks");
if (!special_blocks)
return NULL;
specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); specialblockmap = PyObject_GetAttrString(textures, "specialblockmap");
if (!specialblockmap)
return NULL;
transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks"); transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks");
if (!transparent_blocks)
return NULL;
/* ensure none of these pointers are NULL */ Py_RETURN_NONE;
if ((!transparent_blocks) || (!blockmap) || (!special_blocks) || (!specialblockmap)) {
fprintf(stderr, "\ninit_chunk_render failed\n");
PyErr_Print();
return 1;
}
return 0;
} }
int int
@@ -276,6 +274,8 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
return final_data; return final_data;
} else if (state->block == 90) {
return check_adjacent_blocks(state, x, y, z, state->block);
} }
@@ -308,7 +308,7 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *t = NULL; PyObject *t = NULL;
if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &blockdata_expanded)) if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &blockdata_expanded))
return Py_BuildValue("i", "-1"); return NULL;
/* fill in important modules */ /* fill in important modules */
state.textures = textures; state.textures = textures;
@@ -362,6 +362,12 @@ 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 */
state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z);
if (state.block == 0) {
continue;
}
/* make sure we're rendering inside the image boundaries */ /* make sure we're rendering inside the image boundaries */
if ((state.imgx >= imgsize0 + 24) || (state.imgx <= -24)) { if ((state.imgx >= imgsize0 + 24) || (state.imgx <= -24)) {
@@ -371,11 +377,6 @@ chunk_render(PyObject *self, PyObject *args) {
continue; continue;
} }
/* get blockid */
state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z);
if (state.block == 0) {
continue;
}
/* decref'd on replacement *and* at the end of the z for block */ /* decref'd on replacement *and* at the end of the z for block */
if (blockid) { if (blockid) {
@@ -398,7 +399,7 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *tmp; PyObject *tmp;
unsigned char ancilData = getArrayByte3D(blockdata_expanded, state.x, state.y, state.z); unsigned char ancilData = getArrayByte3D(blockdata_expanded, state.x, state.y, state.z);
if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2)) { if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) {
ancilData = generate_pseudo_data(&state, ancilData); ancilData = generate_pseudo_data(&state, ancilData);
} }
@@ -432,7 +433,7 @@ chunk_render(PyObject *self, PyObject *args) {
blockid = NULL; blockid = NULL;
} }
} }
} }
/* free up the rendermode info */ /* free up the rendermode info */
rendermode->finish(rm_data, &state); rendermode->finish(rm_data, &state);

View File

@@ -26,6 +26,8 @@ static PyMethodDef COverviewerMethods[] = {
{"alpha_over", alpha_over_wrap, METH_VARARGS, {"alpha_over", alpha_over_wrap, METH_VARARGS,
"alpha over composite function"}, "alpha over composite function"},
{"init_chunk_render", init_chunk_render, METH_VARARGS,
"Initializes the stuffs renderer."},
{"render_loop", chunk_render, METH_VARARGS, {"render_loop", chunk_render, METH_VARARGS,
"Renders stuffs"}, "Renders stuffs"},
@@ -53,12 +55,6 @@ initc_overviewer(void)
(void)Py_InitModule("c_overviewer", COverviewerMethods); (void)Py_InitModule("c_overviewer", COverviewerMethods);
/* for numpy */ /* for numpy */
import_array(); import_array();
/* initialize some required variables in iterage.c */
if (init_chunk_render()) {
fprintf(stderr, "failed to init_chunk_render\n");
exit(1); // TODO better way to indicate error?
}
init_endian(); init_endian();
} }

View File

@@ -26,7 +26,7 @@
// increment this value if you've made a change to the c extesion // increment this value if you've made a change to the c extesion
// and want to force users to rebuild // and want to force users to rebuild
#define OVERVIEWER_EXTENSION_VERSION 5 #define OVERVIEWER_EXTENSION_VERSION 6
/* Python PIL, and numpy headers */ /* Python PIL, and numpy headers */
#include <Python.h> #include <Python.h>
@@ -76,7 +76,7 @@ typedef struct {
PyObject *left_blocks; PyObject *left_blocks;
PyObject *right_blocks; PyObject *right_blocks;
} RenderState; } RenderState;
int init_chunk_render(void); PyObject *init_chunk_render(PyObject *self, PyObject *args);
int is_transparent(unsigned char b); int is_transparent(unsigned char b);
PyObject *chunk_render(PyObject *self, PyObject *args); PyObject *chunk_render(PyObject *self, PyObject *args);

View File

@@ -177,7 +177,7 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
int increment=0; int increment=0;
if (state->block == 44) // half-step if (state->block == 44) // half-step
increment=6; increment=6;
else if (state->block == 78) // snow else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off)
increment=9; increment=9;
if ((state->x == 15) && (state->up_right_blocks != Py_None)) { if ((state->x == 15) && (state->up_right_blocks != Py_None)) {

View File

@@ -19,18 +19,21 @@ import os.path
import zipfile import zipfile
from cStringIO import StringIO from cStringIO import StringIO
import math import math
from random import randint
import numpy import numpy
from PIL import Image, ImageEnhance, ImageOps, ImageDraw from PIL import Image, ImageEnhance, ImageOps, ImageDraw
import util import util
import composite import composite
_find_file_local_path = None
def _find_file(filename, mode="rb"): def _find_file(filename, mode="rb"):
"""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 program dir (same dir as this file) * The program dir (same dir as this file)
* The program dir / textures
* 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
@@ -38,14 +41,21 @@ def _find_file(filename, mode="rb"):
* On Darwin, at $HOME/Library/Application Support/minecraft/bin/minecraft.jar * On Darwin, at $HOME/Library/Application Support/minecraft/bin/minecraft.jar
* at $HOME/.minecraft/bin/minecraft.jar * at $HOME/.minecraft/bin/minecraft.jar
* The current working directory
* The program dir / textures
""" """
if _find_file_local_path:
path = os.path.join(_find_file_local_path, filename)
if os.path.exists(path):
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):
return open(path, mode) return open(path, mode)
path = os.path.join(programdir, "textures", filename)
if os.path.exists(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)
@@ -73,14 +83,6 @@ def _find_file(filename, mode="rb"):
except (KeyError, IOError): except (KeyError, IOError):
pass pass
path = filename
if os.path.exists(path):
return open(path, mode)
path = os.path.join(programdir, "textures", filename)
if os.path.exists(path):
return open(path, mode)
raise IOError("Could not find the file {0}. Is Minecraft installed? If so, I couldn't find the minecraft.jar file.".format(filename)) raise IOError("Could not find the file {0}. Is Minecraft installed? If so, I couldn't find the minecraft.jar file.".format(filename))
def _load_image(filename): def _load_image(filename):
@@ -112,23 +114,15 @@ def _split_terrain(terrain):
return textures return textures
# This maps terainids to 16x16 images
terrain_images = _split_terrain(_get_terrain_image())
def transform_image(img, blockID=None): def transform_image(img, blockID=None):
"""Takes a PIL image and rotates it left 45 degrees and shrinks the y axis """Takes a PIL image and rotates it left 45 degrees and shrinks the y axis
by a factor of 2. Returns the resulting image, which will be 24x12 pixels by a factor of 2. Returns the resulting image, which will be 24x12 pixels
""" """
if blockID in (81,92): # cacti and cake # Resize to 17x17, since the diagonal is approximately 24 pixels, a nice
# Resize to 15x15, since the cactus and the cake textures are a little smaller than the other textures # even number that can be split in half twice
img = img.resize((15, 15), Image.ANTIALIAS) img = img.resize((17, 17), Image.ANTIALIAS)
else:
# Resize to 17x17, since the diagonal is approximately 24 pixels, a nice
# even number that can be split in half twice
img = img.resize((17, 17), Image.ANTIALIAS)
# Build the Affine transformation matrix for this perspective # Build the Affine transformation matrix for this perspective
transform = numpy.matrix(numpy.identity(3)) transform = numpy.matrix(numpy.identity(3))
@@ -194,7 +188,47 @@ def transform_image_slope(img, blockID=None):
newimg = img.transform((24,24), Image.AFFINE, transform) newimg = img.transform((24,24), Image.AFFINE, transform)
return newimg return newimg
def transform_image_angle(img, angle, blockID=None):
"""Takes an image an shears it in arbitrary angle with the axis of
rotation being vertical.
WARNING! Don't use angle = pi/2 (or multiplies), it will return
a blank image (or maybe garbage).
NOTE: angle is in the image not in game, so for the left side of a
block angle = 30 degree.
"""
# Take the same size as trasform_image_side
img = img.resize((12,12), Image.ANTIALIAS)
# some values
cos_angle = math.cos(angle)
sin_angle = math.sin(angle)
# function_x and function_y are used to keep the result image in the
# same position, and constant_x and constant_y are the coordinates
# for the center for angle = 0.
constant_x = 6.
constant_y = 6.
function_x = 6.*(1-cos_angle)
function_y = -6*sin_angle
big_term = ( (sin_angle * (function_x + constant_x)) - cos_angle* (function_y + constant_y))/cos_angle
# The numpy array is not really used, but is helpful to
# see the matrix used for the transformation.
transform = numpy.array([[1./cos_angle, 0, -(function_x + constant_x)/cos_angle],
[-sin_angle/(cos_angle), 1., big_term ],
[0, 0, 1.]])
transform = tuple(transform[0]) + tuple(transform[1])
newimg = img.transform((24,24), Image.AFFINE, transform)
return newimg
def _build_block(top, side, blockID=None): def _build_block(top, side, blockID=None):
"""From a top texture and a side texture, build a block image. """From a top texture and a side texture, build a block image.
@@ -223,7 +257,7 @@ def _build_block(top, side, blockID=None):
otherside.putalpha(othersidealpha) otherside.putalpha(othersidealpha)
## special case for non-block things ## special case for non-block things
if blockID in (37,38,6,39,40,83): ## flowers, sapling, mushrooms, reeds if blockID in (37,38,6,39,40,83,30): ## flowers, sapling, mushrooms, reeds, web
# #
# instead of pasting these blocks at the cube edges, place them in the middle: # instead of pasting these blocks at the cube edges, place them in the middle:
# and omit the top # and omit the top
@@ -233,9 +267,9 @@ def _build_block(top, side, blockID=None):
if blockID in (81,): # cacti! if blockID in (81,): # cacti!
composite.alpha_over(img, side, (2,6), side) composite.alpha_over(img, side, (1,6), side)
composite.alpha_over(img, otherside, (10,6), otherside) composite.alpha_over(img, otherside, (11,6), otherside)
composite.alpha_over(img, top, (0,2), top) composite.alpha_over(img, top, (0,0), top)
elif blockID in (44,): # half step elif blockID in (44,): # half step
# shift each texture down 6 pixels # shift each texture down 6 pixels
composite.alpha_over(img, side, (0,12), side) composite.alpha_over(img, side, (0,12), side)
@@ -273,9 +307,37 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None
side3 is in the -x (bottom left, north) side3 is in the -x (bottom left, north)
side4 is in the +y (bottom right, west) side4 is in the +y (bottom right, west)
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
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.
NOTE: this method uses the top of the texture image (as done in
minecraft with beds)
""" """
increment = 0
if isinstance(top, tuple):
increment = top[1]
crop_height = int(increment * 16./12.)
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))
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))
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))
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))
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), (38,92,255,0))
# first back sides # first back sides
@@ -288,7 +350,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), side1) composite.alpha_over(img, side1, (0,0 + increment), side1)
if side2 != None : if side2 != None :
@@ -299,7 +361,7 @@ 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), side2) composite.alpha_over(img, side2, (12,0 + increment), side2)
if bottom != None : if bottom != None :
bottom = transform_image(bottom, blockID) bottom = transform_image(bottom, blockID)
@@ -314,7 +376,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), side3) composite.alpha_over(img, side3, (0,6 + increment), side3)
if side4 != None : if side4 != None :
side4 = transform_image_side(side4, blockID) side4 = transform_image_side(side4, blockID)
@@ -325,11 +387,11 @@ 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), side4) composite.alpha_over(img, side4, (12,6 + increment), side4)
if top != None : if top != None :
top = transform_image(top, blockID) top = transform_image(top, blockID)
composite.alpha_over(img, top, (0,0), top) composite.alpha_over(img, top, (0, increment), top)
return img return img
@@ -346,13 +408,13 @@ 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, -1, -1, # Cloths are left out, sandstone (it has top, side, and bottom wich is ignored here), note block 34, -1, 52, 48, 49,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
-1, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 4, # Gold/iron blocks? Doublestep? TNT from above? -1, -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
36, 37, -1, -1, 65, -1, -1, -1, 98, 24, -1, -1, 86, -1, -1, -1, # Torch from above? leaving out fire. Redstone wire? Crops/furnaces handled elsewhere. sign post 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 # 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, # door,ladder left out. Minecart rail orientation, redstone torches -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, 67,
# 80 81 82 83 84 85 86 87 88 89 90 91 # 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? 66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102 # clay?
] ]
@@ -363,15 +425,15 @@ 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, -1, -1, 34, -1, 52, 48, 49,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
-1, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, -1, -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
36, 37, -1, -1, 65, -1, -1,101, 98, 24, -1, -1, 86, -1, -1, -1, 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 # 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, 67,
# 80 81 82 83 84 85 86 87 88 89 90 91 # 80 81 82 83 84 85 86 87 88 89 90 91
66, 69, 72, 73, 74,-1 ,118,103,104,105, -1, 118 66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118
] ]
# This maps block id to the texture that goes on the side of the block # This maps block id to the texture that goes on the side of the block
@@ -397,7 +459,6 @@ def _build_blockimages():
while len(allimages) < 256: while len(allimages) < 256:
allimages.append(None) allimages.append(None)
return allimages return allimages
blockmap = _build_blockimages()
def load_water(): def load_water():
"""Evidentially, the water and lava textures are not loaded from any files """Evidentially, the water and lava textures are not loaded from any files
@@ -419,8 +480,6 @@ def load_water():
lavablock = _build_block(lavatexture, lavatexture) lavablock = _build_block(lavatexture, lavatexture)
blockmap[10] = lavablock.convert("RGB"), lavablock blockmap[10] = lavablock.convert("RGB"), lavablock
blockmap[11] = blockmap[10] blockmap[11] = blockmap[10]
load_water()
def generate_special_texture(blockID, data): def generate_special_texture(blockID, data):
"""Generates a special texture, such as a correctly facing minecraft track""" """Generates a special texture, such as a correctly facing minecraft track"""
@@ -516,6 +575,52 @@ def generate_special_texture(blockID, data):
return (img.convert("RGB"), img.split()[3]) return (img.convert("RGB"), img.split()[3])
if blockID == 26: # bed
increment = 5
left_face = None
right_face = None
if data & 0x8 == 0x8: # head of the bed
top = terrain_images[135]
if data & 0x00 == 0x00: # head pointing to West
top = top.copy().rotate(270)
left_face = terrain_images[151]
right_face = terrain_images[152]
if data & 0x01 == 0x01: # ... North
top = top.rotate(270)
left_face = terrain_images[152]
right_face = terrain_images[151]
if data & 0x02 == 0x02: # East
top = top.rotate(180)
left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT)
right_face = None
if data & 0x03 == 0x03: # South
right_face = None
right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT)
else: # foot of the bed
top = terrain_images[134]
if data & 0x00 == 0x00: # head pointing to West
top = top.rotate(270)
left_face = terrain_images[150]
right_face = None
if data & 0x01 == 0x01: # ... North
top = top.rotate(270)
left_face = None
right_face = terrain_images[150]
if data & 0x02 == 0x02: # East
top = top.rotate(180)
left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT)
right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT)
if data & 0x03 == 0x03: # South
left_face = terrain_images[149]
right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT)
top = (top, increment)
img = _build_full_block(top, None, None, left_face, right_face)
return (img.convert("RGB"), img.split()[3])
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]
@@ -913,6 +1018,49 @@ def generate_special_texture(blockID, data):
return (img.convert("RGB"), img.split()[3]) return (img.convert("RGB"), img.split()[3])
if blockID == 63: # singposts
texture = terrain_images[4].copy()
# 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))
# If the signpost is looking directly to the image, draw some
# random dots, they will look as text.
if data in (0,1,2,3,4,5,15):
for i in range(15):
x = randint(4,11)
y = randint(3,7)
texture.putpixel((x,y),(0,0,0,255))
# Minecraft uses wood texture for the signpost stick
texture_stick = terrain_images[20]
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))
# W N ~90 E S ~270
angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.)
angle = math.radians(angles[data])
post = transform_image_angle(texture, angle)
# choose the position of the "3D effect"
incrementx = 0
if data in (1,6,7,8,9,14):
incrementx = -1
elif data in (3,4,5,11,12,13):
incrementx = +1
composite.alpha_over(img, texture_stick,(11, 8),texture_stick)
# post2 is a brighter signpost pasted with a small sift,
# gives to the signpost some 3D effect.
post2 = ImageEnhance.Brightness(post).enhance(1.2)
composite.alpha_over(img, post2,(incrementx, -3),post2)
composite.alpha_over(img, post, (0,-2), post)
return (img.convert("RGB"), img.split()[3])
if blockID in (64,71): #wooden door, or iron door if blockID in (64,71): #wooden door, or iron door
if data & 0x8 == 0x8: # top of the door if data & 0x8 == 0x8: # top of the door
raw_door = terrain_images[81 if blockID == 64 else 82] raw_door = terrain_images[81 if blockID == 64 else 82]
@@ -1071,6 +1219,42 @@ def generate_special_texture(blockID, data):
return (img.convert("RGB"), img.split()[3]) return (img.convert("RGB"), img.split()[3])
if blockID == 68: # wall sign
texture = terrain_images[4].copy()
# 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))
# draw some random black dots, they will look as text
""" don't draw text at the moment, they are used in blank for decoration
if data in (3,4):
for i in range(15):
x = randint(4,11)
y = randint(3,7)
texture.putpixel((x,y),(0,0,0,255))
"""
img = Image.new("RGBA", (24,24), (38,92,255,0))
incrementx = 0
if data == 2: # east
incrementx = +1
sign = _build_full_block(None, None, None, None, texture)
elif data == 3: # west
incrementx = -1
sign = _build_full_block(None, texture, None, None, None)
elif data == 4: # north
incrementx = +1
sign = _build_full_block(None, None, texture, None, None)
elif data == 5: # south
incrementx = -1
sign = _build_full_block(None, None, None, texture, None)
sign2 = ImageEnhance.Brightness(sign).enhance(1.2)
composite.alpha_over(img, sign2,(incrementx, 2),sign2)
composite.alpha_over(img, sign, (0,3), sign)
return (img.convert("RGB"), img.split()[3])
if blockID == 85: # fences if blockID == 85: # fences
# create needed images for Big stick fence # create needed images for Big stick fence
@@ -1186,6 +1370,22 @@ def generate_special_texture(blockID, data):
return (img.convert("RGB"), img.split()[3]) return (img.convert("RGB"), img.split()[3])
if blockID == 90: # portal
portaltexture = _load_image("portal.png")
img = Image.new("RGBA", (24,24), (38,92,255,0))
side = transform_image_side(portaltexture)
otherside = side.transpose(Image.FLIP_TOP_BOTTOM)
if data in (1,4):
composite.alpha_over(img, side, (5,4), side)
if data in (2,8):
composite.alpha_over(img, otherside, (5,4), otherside)
return (img.convert("RGB"), img.split()[3])
if blockID == 92: # cake! (without bites, at the moment) if blockID == 92: # cake! (without bites, at the moment)
top = terrain_images[121] top = terrain_images[121]
@@ -1203,13 +1403,129 @@ def generate_special_texture(blockID, data):
img = Image.new("RGBA", (24,24), (38,92,255,0)) img = Image.new("RGBA", (24,24), (38,92,255,0))
composite.alpha_over(img, side, (2,12), side) composite.alpha_over(img, side, (1,12), side)
composite.alpha_over(img, otherside, (10,12), otherside) composite.alpha_over(img, otherside, (11,13), otherside) # workaround, fixes a hole
composite.alpha_over(img, top, (0,8), top) composite.alpha_over(img, otherside, (12,12), otherside)
composite.alpha_over(img, top, (0,6), top)
return (img.convert("RGB"), img.split()[3])
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.
top = terrain_images[131] if blockID == 93 else terrain_images[147]
side = terrain_images[5]
increment = 9
#~ composite.alpha_over(img, side, (2,6), side) if (data & 0x3) == 0: # pointing east
#~ composite.alpha_over(img, otherside, (10,6), otherside) pass
#~ composite.alpha_over(img, top, (0,2), top)
if (data & 0x3) == 1: # pointing south
top = top.rotate(270)
if (data & 0x3) == 2: # pointing west
top = top.rotate(180)
if (data & 0x3) == 3: # pointing north
top = top.rotate(90)
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])
# 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))
# touch up the 3d effect with big rectangles, just in case, for other texture packs
ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0))
ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0))
# torch positions for every redstone torch orientation.
#
# This is a horrible list of torch orientations. I tried to
# obtain these orientations by rotating the positions for one
# orientation, but pixel rounding is horrible and messes the
# torches.
if (data & 0x3) == 0: # pointing east
if (data & 0xC) == 0: # one tick delay
moving_torch = (1,1)
static_torch = (-3,-1)
elif (data & 0xC) == 4: # two ticks delay
moving_torch = (2,2)
static_torch = (-3,-1)
elif (data & 0xC) == 8: # three ticks delay
moving_torch = (3,2)
static_torch = (-3,-1)
elif (data & 0xC) == 12: # four ticks delay
moving_torch = (4,3)
static_torch = (-3,-1)
elif (data & 0x3) == 1: # pointing south
if (data & 0xC) == 0: # one tick delay
moving_torch = (1,1)
static_torch = (5,-1)
elif (data & 0xC) == 4: # two ticks delay
moving_torch = (0,2)
static_torch = (5,-1)
elif (data & 0xC) == 8: # three ticks delay
moving_torch = (-1,2)
static_torch = (5,-1)
elif (data & 0xC) == 12: # four ticks delay
moving_torch = (-2,3)
static_torch = (5,-1)
elif (data & 0x3) == 2: # pointing west
if (data & 0xC) == 0: # one tick delay
moving_torch = (1,1)
static_torch = (5,3)
elif (data & 0xC) == 4: # two ticks delay
moving_torch = (0,0)
static_torch = (5,3)
elif (data & 0xC) == 8: # three ticks delay
moving_torch = (-1,0)
static_torch = (5,3)
elif (data & 0xC) == 12: # four ticks delay
moving_torch = (-2,-1)
static_torch = (5,3)
elif (data & 0x3) == 3: # pointing north
if (data & 0xC) == 0: # one tick delay
moving_torch = (1,1)
static_torch = (-3,3)
elif (data & 0xC) == 4: # two ticks delay
moving_torch = (2,0)
static_torch = (-3,3)
elif (data & 0xC) == 8: # three ticks delay
moving_torch = (3,0)
static_torch = (-3,3)
elif (data & 0xC) == 12: # four ticks delay
moving_torch = (4,-1)
static_torch = (-3,3)
# this paste order it's ok for east and south orientation
# but it's wrong for north and west orientations. But using the
# default texture pack the torches are small enough to no overlap.
composite.alpha_over(img, torch, static_torch, torch)
composite.alpha_over(img, torch, moving_torch, torch)
return (img.convert("RGB"), img.split()[3]) return (img.convert("RGB"), img.split()[3])
@@ -1221,11 +1537,6 @@ def tintTexture(im, c):
i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA
return i return i
# generate biome (still grayscale) leaf, grass textures
biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2)
biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18)
currentBiomeFile = None currentBiomeFile = None
currentBiomeData = None currentBiomeData = None
grasscolor = None grasscolor = None
@@ -1290,9 +1601,9 @@ 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, 23, 27, 28, 35, 43, 44, 50, 51, special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 35, 43, 44, 50,
53, 54, 55, 58, 59, 61, 62, 64, 65, 66, 67, 71, 75, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67,
76, 85, 86, 91, 92]) 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94])
# 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.
@@ -1302,6 +1613,7 @@ 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(4) # wood: normal, birch and pine special_map[17] = range(4) # wood: normal, birch and pine
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
@@ -1317,17 +1629,22 @@ special_map[58] = (0,) # crafting table
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
special_map[63] = range(16) # signpost, orientation
special_map[64] = range(16) # wooden door, open/close and orientation special_map[64] = range(16) # wooden door, open/close and orientation
special_map[65] = (2,3,4,5) # ladder, orientation special_map[65] = (2,3,4,5) # ladder, orientation
special_map[66] = range(10) # minecrart tracks, orientation, slope special_map[66] = range(10) # minecrart tracks, orientation, slope
special_map[67] = range(4) # cobblestone stairs, orientation special_map[67] = range(4) # cobblestone stairs, orientation
special_map[68] = (2,3,4,5) # wall sing, orientation
special_map[71] = range(16) # iron door, open/close and 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[75] = (1,2,3,4,5) # off redstone torch, orientation
special_map[76] = (1,2,3,4,5) # on redstone torch, orientation special_map[76] = (1,2,3,4,5) # on redstone torch, orientation
special_map[85] = range(17) # fences, all the possible combination, 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[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[91] = range(5) # jack-o-lantern, orientation
special_map[92] = range(6) # cake! 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)
# grass and leaves are graysacle in terrain.png # grass and leaves are graysacle in terrain.png
# we treat them as special so we can manually tint them # we treat them as special so we can manually tint them
@@ -1339,9 +1656,34 @@ special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is
# and is harmless for normal maps # and is harmless for normal maps
special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemented) special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemented)
# placeholders that are generated in generate()
terrain_images = None
blockmap = None
biome_grass_texture = None
biome_leaf_texture = None
specialblockmap = None
specialblockmap = {} def generate(path=None):
global _find_file_local_path
for blockID in special_blocks: _find_file_local_path = path
for data in special_map[blockID]:
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) # This maps terainids to 16x16 images
global terrain_images
terrain_images = _split_terrain(_get_terrain_image())
# generate the normal blocks
global blockmap
blockmap = _build_blockimages()
load_water()
# generate biome (still grayscale) leaf, grass textures
global biome_grass_texture, biome_leaf_texture
biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2)
biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18)
# generate the special blocks
global specialblockmap, special_blocks
specialblockmap = {}
for blockID in special_blocks:
for data in special_map[blockID]:
specialblockmap[(blockID, data)] = generate_special_texture(blockID, data)

BIN
textures/portal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

View File

@@ -1,15 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="generator" content="Minecraft-Overviewer {version}" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="overviewer.css" type="text/css" /> <link rel="stylesheet" href="overviewer.css" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="overviewerConfig.js"></script> <script type="text/javascript" src="overviewerConfig.js"></script>
<script type="text/javascript" src="overviewer.js"></script> <script type="text/javascript" src="overviewer.js"></script>
<script type="text/javascript" src="markers.js"></script> <script type="text/javascript" src="markers.js"></script>
<script type="text/javascript" src="regions.js"></script> <script type="text/javascript" src="regions.js"></script>
</head> </head>
<!-- Generated at: {time} --> <!-- Generated at: {time} -->
<body onload="overviewer.util.initialize()"> <body onload="overviewer.util.initialize()">
<div id="mcmap"></div> <div id="mcmap"></div>

View File

@@ -145,7 +145,7 @@ var overviewer = {
var lng = defaultCenter.lng(); var lng = defaultCenter.lng();
var zoom = overviewerConfig.map.defaultZoom; var zoom = overviewerConfig.map.defaultZoom;
var mapcenter; var mapcenter;
queryParams = overviewer.util.parseQueryString(); var queryParams = overviewer.util.parseQueryString();
if (queryParams.lat) { if (queryParams.lat) {
lat = parseFloat(queryParams.lat); lat = parseFloat(queryParams.lat);
} }
@@ -194,6 +194,7 @@ var overviewer = {
}, },
mapTypeId: overviewer.util.getDefaultMapTypeId(), mapTypeId: overviewer.util.getDefaultMapTypeId(),
streetViewControl: false, streetViewControl: false,
overviewMapControl: true,
zoomControl: overviewerConfig.map.controls.zoom, zoomControl: overviewerConfig.map.controls.zoom,
backgroundColor: overviewer.util.getMapTypeBackgroundColor( backgroundColor: overviewer.util.getMapTypeBackgroundColor(
overviewer.util.getDefaultMapTypeId()) overviewer.util.getDefaultMapTypeId())
@@ -225,15 +226,28 @@ var overviewer = {
overviewer.collections.mapTypes[i].name, overviewer.collections.mapTypes[i].name,
overviewer.collections.mapTypes[i]); overviewer.collections.mapTypes[i]);
} }
// Make the link again whenever the map changes // Jump to the hash if given
overviewer.util.initHash();
// Add live hash update listeners
// Note: It is important to add them after jumping to the hash
google.maps.event.addListener(overviewer.map, 'dragend', function() {
overviewer.util.updateHash();
});
google.maps.event.addListener(overviewer.map, 'zoom_changed', function() {
overviewer.util.updateHash();
});
// Make the link again whenever the map changes
google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() { google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() {
$('#'+overviewerConfig.CONST.mapDivId).css( $('#'+overviewerConfig.CONST.mapDivId).css(
'background-color', overviewer.util.getMapTypeBackgroundColor( 'background-color', overviewer.util.getMapTypeBackgroundColor(
overviewer.map.getMapTypeId())); overviewer.map.getMapTypeId()));
//smuggled this one in here for maptypeid hash generation --CounterPillow
overviewer.util.updateHash();
}); });
// We can now set the map to use the 'coordinate' map type
overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId());
}, },
/** /**
* Read through overviewer.collections.markerDatas and create Marker * Read through overviewer.collections.markerDatas and create Marker
@@ -368,6 +382,14 @@ var overviewer = {
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) {
var strokeOpacity = region.opacity;
var fillOpacity = region.opacity * 0.25;
} else {
var strokeOpacity = region.strokeOpacity;
var fillOpacity = region.fillOpacity;
}
if (region.closed) { if (region.closed) {
var shape = new google.maps.Polygon({ var shape = new google.maps.Polygon({
'name': name, 'name': name,
@@ -375,10 +397,10 @@ var overviewer = {
'geodesic': false, 'geodesic': false,
'map': null, 'map': null,
'strokeColor': region.color, 'strokeColor': region.color,
'strokeOpacity': region.opacity, 'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight, 'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'fillColor': region.color, 'fillColor': region.color,
'fillOpacity': region.opacity * 0.25, 'fillOpacity': fillOpacity,
'zIndex': j, 'zIndex': j,
'paths': converted 'paths': converted
}); });
@@ -389,7 +411,7 @@ var overviewer = {
'geodesic': false, 'geodesic': false,
'map': null, 'map': null,
'strokeColor': region.color, 'strokeColor': region.color,
'strokeOpacity': region.opacity, 'strokeOpacity': strokeOpacity,
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight, 'strokeWeight': overviewerConfig.CONST.regionStrokeWeight,
'zIndex': j, 'zIndex': j,
'path': converted 'path': converted
@@ -449,29 +471,6 @@ var overviewer = {
} }
return results; return results;
}, },
/**
* Set the link (at the bottom of the screen) to the current view.
* TODO: make this preserve the mapTypeId as well
*/
'setViewUrl': function() {
var displayZoom = overviewer.map.getZoom();
if (displayZoom == overviewerConfig.map.maxZoom) {
displayZoom = 'max';
} else {
displayZoom -= overviewerConfig.map.maxZoom;
}
var point;
var point = overviewer.util.fromLatLngToWorld(
overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng());
var viewUrl = location.href.substring(0, location.href.lastIndexOf(
location.search))
+ '?x=' + Math.floor(point.x)
+ '&y=' + Math.floor(point.y)
+ '&z=' + Math.floor(point.z)
+ '&zoom=' + displayZoom;
document.getElementById('link').innerHTML = viewUrl;
},
'getDefaultMapTypeId': function() { 'getDefaultMapTypeId': function() {
return overviewer.collections.mapTypeIds[0]; return overviewer.collections.mapTypeIds[0];
}, },
@@ -571,21 +570,6 @@ var overviewer = {
* like the compass, current view link, etc. * like the compass, current view link, etc.
*/ */
'createMapControls': function() { 'createMapControls': function() {
// viewstate link (little link to where you're looking at the map,
// normally bottom left)
var viewStateDiv = document.createElement('DIV');
viewStateDiv.id='link';
// add it to the map, bottom left.
if (overviewerConfig.map.controls.link) {
google.maps.event.addListener(overviewer.map, 'zoom_changed', function() {
overviewer.util.setViewUrl();
});
google.maps.event.addListener(overviewer.map, 'center_changed', function() {
overviewer.util.setViewUrl();
});
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(viewStateDiv);
}
// compass rose, in the top right corner // compass rose, in the top right corner
var compassDiv = document.createElement('DIV'); var compassDiv = document.createElement('DIV');
compassDiv.style.padding = '5px'; compassDiv.style.padding = '5px';
@@ -767,9 +751,11 @@ var overviewer = {
overviewer.collections.infoWindow.close(); overviewer.collections.infoWindow.close();
} }
// Replace our Info Window's content and position // Replace our Info Window's content and position
var point = overviewer.util.fromLatLngToWorld(event.latLng.lat(),event.latLng.lng());
var contentString = '<b>Region: ' + shape.name + '</b><br />' + var contentString = '<b>Region: ' + shape.name + '</b><br />' +
'Clicked Location: <br />' + event.latLng.lat() + ', ' + 'Clicked Location: <br />' + Math.round(point.x,1) + ', ' + point.y
event.latLng.lng() + '<br />'; + ', ' + Math.round(point.z,1)
+ '<br />';
infowindow.setContent(contentString); infowindow.setContent(contentString);
infowindow.setPosition(event.latLng); infowindow.setPosition(event.latLng);
infowindow.open(overviewer.map); infowindow.open(overviewer.map);
@@ -794,7 +780,79 @@ var overviewer = {
infowindow.open(overviewer.map, marker); infowindow.open(overviewer.map, marker);
overviewer.collections.infoWindow = infowindow; overviewer.collections.infoWindow = infowindow;
}); });
} },
'initHash': function() {
if(window.location.hash.split("/").length > 1) {
overviewer.util.goToHash();
// Clean up the hash.
overviewer.util.updateHash();
// Add a marker indicating the user-supplied position
var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng());
overviewer.collections.markerDatas.push([{
'msg': 'Coordinates ' + Math.floor(coordinates.x) + ', ' + Math.floor(coordinates.y) + ', ' + Math.floor(coordinates.z),
'x': coordinates.x,
'y': coordinates.y,
'z': coordinates.z,
'type': 'querypos'}]);
}
},
'setHash': function(x, y, z, zoom, maptype) {
window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + maptype);
},
'updateHash': function() {
var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng());
var zoom = overviewer.map.getZoom();
var maptype = overviewer.map.getMapTypeId();
if (zoom == overviewerConfig.map.maxZoom) {
zoom = 'max';
} else if (zoom == overviewerConfig.map.minZoom) {
zoom = 'min';
} else {
// default to (map-update friendly) negative zooms
zoom -= overviewerConfig.map.maxZoom;
}
overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom, maptype);
},
'goToHash': function() {
// Note: the actual data begins at coords[1], coords[0] is empty.
var coords = window.location.hash.split("/");
var latlngcoords = overviewer.util.fromWorldToLatLng(parseInt(coords[1]), parseInt(coords[2]), parseInt(coords[3]));
var zoom;
var maptype = '';
// The if-statements try to prevent unexpected behaviour when using incomplete hashes, e.g. older links
if (coords.length > 4) {
zoom = coords[4];
}
if (coords.length > 5) {
maptype = coords[5];
}
if (zoom == 'max') {
zoom = overviewerConfig.map.maxZoom;
} else if (zoom == 'min') {
zoom = overviewerConfig.map.minZoom;
} else {
zoom = parseInt(zoom);
if (zoom < 0 && zoom + overviewerConfig.map.maxZoom >= 0) {
// if zoom is negative, treat it as a "zoom out from max"
zoom += overviewerConfig.map.maxZoom;
} else {
// fall back to default zoom
zoom = overviewerConfig.map.defaultZoom;
}
}
// If the maptype isn't set, set the default one.
if (maptype == '') {
// We can now set the map to use the 'coordinate' map type
overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId());
} else {
overviewer.map.setMapTypeId(maptype);
}
overviewer.map.setCenter(latlngcoords);
overviewer.map.setZoom(zoom);
},
}, },
/** /**
* The various classes needed in this file. * The various classes needed in this file.
@@ -827,6 +885,7 @@ var overviewer = {
overviewerConfig.map.center[0], overviewerConfig.map.center[0],
overviewerConfig.map.center[1], overviewerConfig.map.center[1],
overviewerConfig.map.center[2])); overviewerConfig.map.center[2]));
overviewer.util.updateHash();
}); });
}, },
/** /**

View File

@@ -79,7 +79,7 @@ var overviewerConfig = {
* Set to true to turn on debug mode, which adds a grid to the map along * Set to true to turn on debug mode, which adds a grid to the map along
* with co-ordinates and a bunch of console output. * with co-ordinates and a bunch of console output.
*/ */
'debug': false, 'debug': false
}, },
/** /**
* Group definitions for objects that are partially selectable (signs and * Group definitions for objects that are partially selectable (signs and

View File

@@ -78,7 +78,8 @@ class World(object):
logging.info("Scanning regions") logging.info("Scanning regions")
regionfiles = {} regionfiles = {}
self.regions = {} self.regions = {}
for x, y, regionfile in self._iterate_regionfiles(): self.regionlist = regionlist # a list of paths
for x, y, regionfile in self._iterate_regionfiles():
mcr = self.reload_region(regionfile) mcr = self.reload_region(regionfile)
mcr.get_chunk_info() mcr.get_chunk_info()
regionfiles[(x,y)] = (x,y,regionfile,mcr) regionfiles[(x,y)] = (x,y,regionfile,mcr)
@@ -277,16 +278,22 @@ class World(object):
"""Returns an iterator of all of the region files, along with their """Returns an iterator of all of the region files, along with their
coordinates coordinates
Note: the regionlist here will be used to determinte the size of the
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:
if path.endswith("\n"): path = path.strip()
path = path[:-1]
f = os.path.basename(path) f = os.path.basename(path)
if f.startswith("r.") and f.endswith(".mcr"): if f.startswith("r.") and f.endswith(".mcr"):
p = f.split(".") p = f.split(".")
logging.debug("Using path %s from regionlist", f)
yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f)) yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f))
else:
logging.warning("Ignore path '%s' in regionlist", f)
else: else:
for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"): for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"):
dirpath, f = os.path.split(path) dirpath, f = os.path.split(path)