Merge branch 'rewrite' into anvil
Conflicts: overviewer_core/src/overviewer.h overviewer_core/tileset.py overviewer_core/util.py overviewer_core/world.py
This commit is contained in:
@@ -6,4 +6,5 @@ include sample.settings.py
|
|||||||
recursive-include contrib/ *.py
|
recursive-include contrib/ *.py
|
||||||
recursive-include overviewer_core/ *.py
|
recursive-include overviewer_core/ *.py
|
||||||
recursive-include overviewer_core/src/ *.c *.h
|
recursive-include overviewer_core/src/ *.c *.h
|
||||||
|
recursive-include overviewer_core/src/primitives/ *.c *.h
|
||||||
recursive-include overviewer_core/data/ *
|
recursive-include overviewer_core/data/ *
|
||||||
|
|||||||
@@ -430,6 +430,26 @@ SmoothLighting
|
|||||||
|
|
||||||
(same as Lighting)
|
(same as Lighting)
|
||||||
|
|
||||||
|
ClearBase
|
||||||
|
Forces the background to be transparent. Use this in place of Base
|
||||||
|
for rendering pure overlays.
|
||||||
|
|
||||||
|
SpawnOverlay
|
||||||
|
Color the map red in areas where monsters can spawn. Either use
|
||||||
|
this on top of other modes, or on top of ClearBase to create a
|
||||||
|
pure overlay.
|
||||||
|
|
||||||
|
MineralOverlay
|
||||||
|
Color the map according to what minerals can be found
|
||||||
|
underneath. Either use this on top of other modes, or on top of
|
||||||
|
ClearBase to create a pure overlay.
|
||||||
|
|
||||||
|
**Options**
|
||||||
|
|
||||||
|
minerals
|
||||||
|
A list of (blockid, (r, g, b)) tuples to use as colors. If not
|
||||||
|
provided, a default list of common minerals is used.
|
||||||
|
|
||||||
Defining Custom Rendermodes
|
Defining Custom Rendermodes
|
||||||
---------------------------
|
---------------------------
|
||||||
Each rendermode primitive listed above is a Python *class* that is automatically
|
Each rendermode primitive listed above is a Python *class* that is automatically
|
||||||
|
|||||||
@@ -365,8 +365,11 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
if rset == None: # indicates no such dimension was found:
|
if rset == None: # indicates no such dimension was found:
|
||||||
logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'], render_name)
|
logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'], render_name)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# If this is to be a rotated regionset, wrap it in a RotatedRegionSet
|
||||||
|
# object
|
||||||
if (render['northdirection'] > 0):
|
if (render['northdirection'] > 0):
|
||||||
rset = rset.rotate(render['northdirection'])
|
rset = world.RotatedRegionSet(rset, render['northdirection'])
|
||||||
logging.debug("Using RegionSet %r", rset)
|
logging.debug("Using RegionSet %r", rset)
|
||||||
|
|
||||||
# create our TileSet from this RegionSet
|
# create our TileSet from this RegionSet
|
||||||
@@ -380,6 +383,12 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
||||||
tilesets.append(tset)
|
tilesets.append(tset)
|
||||||
|
|
||||||
|
# Do tileset preprocessing here, before we start dispatching jobs
|
||||||
|
for ts in tilesets:
|
||||||
|
ts.do_preprocessing()
|
||||||
|
|
||||||
|
# Output initial static data and configuration
|
||||||
|
assetMrg.initialize(tilesets)
|
||||||
|
|
||||||
# multiprocessing dispatcher
|
# multiprocessing dispatcher
|
||||||
if config['processes'] == 1:
|
if config['processes'] == 1:
|
||||||
|
|||||||
@@ -72,7 +72,23 @@ directory.
|
|||||||
# TODO based on the type, so something
|
# TODO based on the type, so something
|
||||||
POI[regionset.name].append
|
POI[regionset.name].append
|
||||||
|
|
||||||
def finalize(self, tilesets):
|
def initialize(self, tilesets):
|
||||||
|
"""Similar to finalize() but calls the tilesets' get_initial_data()
|
||||||
|
instead of get_persistent_data() to compile the generated javascript
|
||||||
|
config.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.finalize(tilesets, True)
|
||||||
|
|
||||||
|
def finalize(self, tilesets, initial=False):
|
||||||
|
"""Called to output the generated javascript and all static files to
|
||||||
|
the output directory
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not initial:
|
||||||
|
get_data = lambda tileset: tileset.get_persistent_data()
|
||||||
|
else:
|
||||||
|
get_data = lambda tileset: tileset.get_initial_data()
|
||||||
|
|
||||||
# dictionary to hold the overviewerConfig.js settings that we will dumps
|
# dictionary to hold the overviewerConfig.js settings that we will dumps
|
||||||
dump = dict()
|
dump = dict()
|
||||||
@@ -94,7 +110,7 @@ directory.
|
|||||||
# based on the tilesets we have, group them by worlds
|
# based on the tilesets we have, group them by worlds
|
||||||
worlds = []
|
worlds = []
|
||||||
for tileset in tilesets:
|
for tileset in tilesets:
|
||||||
full_name = tileset.get_persistent_data()['world']
|
full_name = get_data(tileset)['world']
|
||||||
if full_name not in worlds:
|
if full_name not in worlds:
|
||||||
worlds.append(full_name)
|
worlds.append(full_name)
|
||||||
|
|
||||||
@@ -120,7 +136,7 @@ directory.
|
|||||||
|
|
||||||
|
|
||||||
for tileset in tilesets:
|
for tileset in tilesets:
|
||||||
dump['tilesets'].append(tileset.get_persistent_data())
|
dump['tilesets'].append(get_data(tileset))
|
||||||
|
|
||||||
# write a blank image
|
# write a blank image
|
||||||
blank = Image.new("RGBA", (1,1), tileset.options.get('bgcolor'))
|
blank = Image.new("RGBA", (1,1), tileset.options.get('bgcolor'))
|
||||||
@@ -128,11 +144,10 @@ directory.
|
|||||||
|
|
||||||
|
|
||||||
jsondump = json.dumps(dump, indent=4)
|
jsondump = json.dumps(dump, indent=4)
|
||||||
with codecs.open(os.path.join(self.outputdir, 'overviewerConfig.js'), 'w', encoding='UTF-8') as f:
|
with util.FileReplacer(os.path.join(self.outputdir, "overviewerConfig.js")) as tmpfile:
|
||||||
f.write("var overviewerConfig = " + jsondump + ";\n")
|
with codecs.open(tmpfile, 'w', encoding='UTF-8') as f:
|
||||||
|
f.write("var overviewerConfig = " + jsondump + ";\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# copy web assets into destdir:
|
# copy web assets into destdir:
|
||||||
global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
|
global_assets = os.path.join(util.get_program_path(), "overviewer_core", "data", "web_assets")
|
||||||
if not os.path.isdir(global_assets):
|
if not os.path.isdir(global_assets):
|
||||||
@@ -143,23 +158,16 @@ directory.
|
|||||||
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
||||||
if not os.path.isdir(js_src):
|
if not os.path.isdir(js_src):
|
||||||
js_src = os.path.join(util.get_program_path(), "js_src")
|
js_src = os.path.join(util.get_program_path(), "js_src")
|
||||||
with open(os.path.join(self.outputdir, "overviewer.js"), "w") as fout:
|
with util.FileReplacer(os.path.join(self.outputdir, "overviewer.js")) as tmpfile:
|
||||||
# first copy in js_src/overviewer.js
|
with open(tmpfile, "w") as fout:
|
||||||
with open(os.path.join(js_src, "overviewer.js")) as f:
|
# first copy in js_src/overviewer.js
|
||||||
fout.write(f.read())
|
with open(os.path.join(js_src, "overviewer.js"), 'r') as f:
|
||||||
# now copy in the rest
|
fout.write(f.read())
|
||||||
for js in os.listdir(js_src):
|
# now copy in the rest
|
||||||
if not js.endswith("overviewer.js"):
|
for js in os.listdir(js_src):
|
||||||
with open(os.path.join(js_src,js)) as f:
|
if not js.endswith("overviewer.js") and js.endswith(".js"):
|
||||||
fout.write(f.read())
|
with open(os.path.join(js_src,js)) as f:
|
||||||
|
fout.write(f.read())
|
||||||
# do the same with the local copy, if we have it
|
|
||||||
# TODO
|
|
||||||
# if self.web_assets_path:
|
|
||||||
# util.mirror_dir(self.web_assets_path, self.outputdir)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# helper function to get a label for the given rendermode
|
# helper function to get a label for the given rendermode
|
||||||
def get_render_mode_label(rendermode):
|
def get_render_mode_label(rendermode):
|
||||||
info = get_render_mode_info(rendermode)
|
info = get_render_mode_info(rendermode)
|
||||||
@@ -177,7 +185,6 @@ directory.
|
|||||||
versionstr = "%s (%s)" % (overviewer_version.VERSION, overviewer_version.HASH[:7])
|
versionstr = "%s (%s)" % (overviewer_version.VERSION, overviewer_version.HASH[:7])
|
||||||
index = index.replace("{version}", versionstr)
|
index = index.replace("{version}", versionstr)
|
||||||
|
|
||||||
with codecs.open(os.path.join(self.outputdir, "index.html"), 'w', encoding='UTF-8') as output:
|
with util.FileReplacer(indexpath) as indexpath:
|
||||||
output.write(index)
|
with codecs.open(indexpath, 'w', encoding='UTF-8') as output:
|
||||||
|
output.write(index)
|
||||||
|
|
||||||
|
|||||||
@@ -36,21 +36,23 @@ overviewer.views.WorldView = Backbone.View.extend({
|
|||||||
|
|
||||||
overviewer.views.WorldSelectorView = Backbone.View.extend({
|
overviewer.views.WorldSelectorView = Backbone.View.extend({
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
// a div will have already been created for us, we just
|
if(overviewer.collections.worldViews.length > 1) {
|
||||||
// need to register it with the google maps control
|
// a div will have already been created for us, we just
|
||||||
var selectBox = document.createElement('select');
|
// need to register it with the google maps control
|
||||||
$.each(overviewer.collections.worldViews, function(index, elem) {
|
var selectBox = document.createElement('select');
|
||||||
var o = document.createElement("option");
|
$.each(overviewer.collections.worldViews, function(index, elem) {
|
||||||
o.value = elem.model.get("name");
|
var o = document.createElement("option");
|
||||||
o.innerHTML = elem.model.get("name");
|
o.value = elem.model.get("name");
|
||||||
$(o).data("viewObj", elem);
|
o.innerHTML = elem.model.get("name");
|
||||||
selectBox.appendChild(o);
|
$(o).data("viewObj", elem);
|
||||||
|
selectBox.appendChild(o);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.el.appendChild(selectBox);
|
this.el.appendChild(selectBox);
|
||||||
overviewer.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.el);
|
overviewer.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.el);
|
||||||
},
|
}
|
||||||
|
},
|
||||||
events: {
|
events: {
|
||||||
"change select": "changeWorld"
|
"change select": "changeWorld"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,14 +49,9 @@ class Dispatcher(object):
|
|||||||
"""
|
"""
|
||||||
# TODO use status callback
|
# TODO use status callback
|
||||||
|
|
||||||
# preprocessing
|
|
||||||
for tileset in tilesetlist:
|
|
||||||
tileset.do_preprocessing()
|
|
||||||
|
|
||||||
# setup tilesetlist
|
# setup tilesetlist
|
||||||
self.setup_tilesets(tilesetlist)
|
self.setup_tilesets(tilesetlist)
|
||||||
|
|
||||||
|
|
||||||
# iterate through all possible phases
|
# iterate through all possible phases
|
||||||
num_phases = [tileset.get_num_phases() for tileset in tilesetlist]
|
num_phases = [tileset.get_num_phases() for tileset in tilesetlist]
|
||||||
for phase in xrange(max(num_phases)):
|
for phase in xrange(max(num_phases)):
|
||||||
|
|||||||
@@ -146,6 +146,45 @@ class Lighting(RenderPrimitive):
|
|||||||
class SmoothLighting(Lighting):
|
class SmoothLighting(Lighting):
|
||||||
name = "smooth-lighting"
|
name = "smooth-lighting"
|
||||||
|
|
||||||
|
class ClearBase(RenderPrimitive):
|
||||||
|
name = "clear-base"
|
||||||
|
|
||||||
|
class Overlay(RenderPrimitive):
|
||||||
|
name = "overlay"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def whitecolor(self):
|
||||||
|
whitecolor = getattr(self, "_whitecolor", None)
|
||||||
|
if whitecolor:
|
||||||
|
return whitecolor
|
||||||
|
white = Image.new("RGBA", (24,24), (255, 255, 255, 255))
|
||||||
|
self._whitecolor = white
|
||||||
|
return white
|
||||||
|
|
||||||
|
@property
|
||||||
|
def facemask_top(self):
|
||||||
|
facemask_top = getattr(self, "_facemask_top", None)
|
||||||
|
if facemask_top:
|
||||||
|
return facemask_top
|
||||||
|
|
||||||
|
white = Image.new("L", (24,24), 255)
|
||||||
|
top = Image.new("L", (24,24), 0)
|
||||||
|
toppart = textures.Textures.transform_image_top(white)
|
||||||
|
top.paste(toppart, (0,0))
|
||||||
|
for x,y in [(3,4), (7,2), (11,0)]:
|
||||||
|
top.putpixel((x,y), 255)
|
||||||
|
self._facemask_top = top
|
||||||
|
return top
|
||||||
|
|
||||||
|
class SpawnOverlay(Overlay):
|
||||||
|
name = "overlay-spawn"
|
||||||
|
|
||||||
|
class MineralOverlay(Overlay):
|
||||||
|
name = "overlay-mineral"
|
||||||
|
options = {
|
||||||
|
'minerals' : ('a list of (blockid, (r, g, b)) tuples for coloring minerals', None),
|
||||||
|
}
|
||||||
|
|
||||||
# Built-in rendermodes for your convenience!
|
# Built-in rendermodes for your convenience!
|
||||||
normal = [Base(), EdgeLines()]
|
normal = [Base(), EdgeLines()]
|
||||||
lighting = [Base(), EdgeLines(), Lighting()]
|
lighting = [Base(), EdgeLines(), Lighting()]
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ renders = Setting(required=True, default={},
|
|||||||
"optimizeimg": Setting(required=True, validator=validateOptImg, default=0),
|
"optimizeimg": Setting(required=True, validator=validateOptImg, default=0),
|
||||||
"nomarkers": Setting(required=False, validator=validateBool, default=None),
|
"nomarkers": Setting(required=False, validator=validateBool, default=None),
|
||||||
"texturepath": Setting(required=False, validator=validateTexturePath, default=None),
|
"texturepath": Setting(required=False, validator=validateTexturePath, default=None),
|
||||||
"renderchecks": Setting(required=True, validator=validateInt, default=0),
|
"renderchecks": Setting(required=False, validator=validateInt, default=None),
|
||||||
"rerenderprob": Setting(required=True, validator=validateFloat, default=0),
|
"rerenderprob": Setting(required=True, validator=validateFloat, default=0),
|
||||||
|
|
||||||
# Remove this eventually (once people update their configs)
|
# Remove this eventually (once people update their configs)
|
||||||
|
|||||||
@@ -89,3 +89,12 @@ class Signal(object):
|
|||||||
# convenience
|
# convenience
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
self.emit(*args, **kwargs)
|
self.emit(*args, **kwargs)
|
||||||
|
|
||||||
|
# force pickled signals to redirect to existing signals
|
||||||
|
def __getstate__(self):
|
||||||
|
return self.fullname
|
||||||
|
def __setstate__(self, fullname):
|
||||||
|
for attr in dir(self.signals[fullname]):
|
||||||
|
if attr.startswith('_'):
|
||||||
|
continue
|
||||||
|
setattr(self, attr, getattr(self.signals[fullname], attr))
|
||||||
|
|||||||
@@ -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 25
|
#define OVERVIEWER_EXTENSION_VERSION 26
|
||||||
|
|
||||||
/* Python PIL, and numpy headers */
|
/* Python PIL, and numpy headers */
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|||||||
49
overviewer_core/src/primitives/clear-base.c
Normal file
49
overviewer_core/src/primitives/clear-base.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Minecraft Overviewer.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../overviewer.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
clear_base_occluded(void *data, RenderState *state, int x, int y, int z) {
|
||||||
|
if ( (x != 0) && (y != 15) && (z != 127) &&
|
||||||
|
!render_mode_hidden(state->rendermode, x-1, y, z) &&
|
||||||
|
!render_mode_hidden(state->rendermode, x, y, z+1) &&
|
||||||
|
!render_mode_hidden(state->rendermode, x, y+1, z) &&
|
||||||
|
!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
|
||||||
|
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) &&
|
||||||
|
!is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
||||||
|
/* clear the draw space -- set alpha to 0 within mask */
|
||||||
|
tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPrimitiveInterface primitive_clear_base = {
|
||||||
|
"clear-base",
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
clear_base_occluded,
|
||||||
|
NULL,
|
||||||
|
clear_base_draw,
|
||||||
|
};
|
||||||
@@ -15,7 +15,14 @@
|
|||||||
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "overviewer.h"
|
#include "overlay.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* inherits from overlay */
|
||||||
|
RenderPrimitiveOverlay parent;
|
||||||
|
|
||||||
|
void *minerals;
|
||||||
|
} RenderPrimitiveMineral;
|
||||||
|
|
||||||
struct MineralColor {
|
struct MineralColor {
|
||||||
unsigned char blockid;
|
unsigned char blockid;
|
||||||
@@ -43,15 +50,16 @@ static struct MineralColor default_minerals[] = {
|
|||||||
static void get_color(void *data, RenderState *state,
|
static void get_color(void *data, RenderState *state,
|
||||||
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
||||||
|
|
||||||
int x = state->x, y = state->y, z_max = state->z + 1, z;
|
int x = state->x, z = state->z, y_max, y;
|
||||||
int max_i = -1;
|
int max_i = -1;
|
||||||
RenderModeMineral* self = (RenderModeMineral *)data;
|
RenderPrimitiveMineral* self = (RenderPrimitiveMineral *)data;
|
||||||
struct MineralColor *minerals = (struct MineralColor *)(self->minerals);
|
struct MineralColor *minerals = (struct MineralColor *)(self->minerals);
|
||||||
*a = 0;
|
*a = 0;
|
||||||
|
|
||||||
for (z = 0; z <= z_max; z++) {
|
y_max = state->y + 1;
|
||||||
|
for (y = state->chunky * -16; y <= y_max; y++) {
|
||||||
int i, tmp;
|
int i, tmp;
|
||||||
unsigned char blockid = getArrayByte3D(state->blocks, x, y, z);
|
unsigned short blockid = get_data(state, BLOCKS, x, y, z);
|
||||||
|
|
||||||
for (i = 0; (max_i == -1 || i < max_i) && minerals[i].blockid != 0; i++) {
|
for (i = 0; (max_i == -1 || i < max_i) && minerals[i].blockid != 0; i++) {
|
||||||
if (minerals[i].blockid == blockid) {
|
if (minerals[i].blockid == blockid) {
|
||||||
@@ -59,7 +67,7 @@ static void get_color(void *data, RenderState *state,
|
|||||||
*g = minerals[i].g;
|
*g = minerals[i].g;
|
||||||
*b = minerals[i].b;
|
*b = minerals[i].b;
|
||||||
|
|
||||||
tmp = (128 - z_max + z) * 2 - 40;
|
tmp = (128 - y_max + y) * 2 - 40;
|
||||||
*a = MIN(MAX(0, tmp), 255);
|
*a = MIN(MAX(0, tmp), 255);
|
||||||
|
|
||||||
max_i = i;
|
max_i = i;
|
||||||
@@ -70,20 +78,21 @@ static void get_color(void *data, RenderState *state,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rendermode_mineral_start(void *data, RenderState *state, PyObject *options) {
|
overlay_mineral_start(void *data, RenderState *state, PyObject *support) {
|
||||||
PyObject *opt;
|
PyObject *opt;
|
||||||
RenderModeMineral* self;
|
RenderPrimitiveMineral* self;
|
||||||
|
|
||||||
/* first, chain up */
|
/* first, chain up */
|
||||||
int ret = rendermode_overlay.start(data, state, options);
|
int ret = primitive_overlay.start(data, state, support);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* now do custom initializations */
|
/* now do custom initializations */
|
||||||
self = (RenderModeMineral *)data;
|
self = (RenderPrimitiveMineral *)data;
|
||||||
|
|
||||||
opt = PyDict_GetItemString(options, "minerals");
|
if (!render_mode_parse_option(support, "minerals", "O", &(opt)))
|
||||||
if (opt) {
|
return 1;
|
||||||
|
if (opt && opt != Py_None) {
|
||||||
struct MineralColor *minerals = NULL;
|
struct MineralColor *minerals = NULL;
|
||||||
Py_ssize_t minerals_size = 0, i;
|
Py_ssize_t minerals_size = 0, i;
|
||||||
/* create custom minerals */
|
/* create custom minerals */
|
||||||
@@ -110,6 +119,7 @@ rendermode_mineral_start(void *data, RenderState *state, PyObject *options) {
|
|||||||
} else {
|
} else {
|
||||||
self->minerals = default_minerals;
|
self->minerals = default_minerals;
|
||||||
}
|
}
|
||||||
|
Py_XDECREF(opt);
|
||||||
|
|
||||||
/* setup custom color */
|
/* setup custom color */
|
||||||
self->parent.get_color = get_color;
|
self->parent.get_color = get_color;
|
||||||
@@ -118,50 +128,24 @@ rendermode_mineral_start(void *data, RenderState *state, PyObject *options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rendermode_mineral_finish(void *data, RenderState *state) {
|
overlay_mineral_finish(void *data, RenderState *state) {
|
||||||
/* first free all *our* stuff */
|
/* first free all *our* stuff */
|
||||||
RenderModeMineral* self = (RenderModeMineral *)data;
|
RenderPrimitiveMineral* self = (RenderPrimitiveMineral *)data;
|
||||||
|
|
||||||
if (self->minerals && self->minerals != default_minerals) {
|
if (self->minerals && self->minerals != default_minerals) {
|
||||||
free(self->minerals);
|
free(self->minerals);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now, chain up */
|
/* now, chain up */
|
||||||
rendermode_overlay.finish(data, state);
|
primitive_overlay.finish(data, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
RenderPrimitiveInterface primitive_overlay_mineral = {
|
||||||
rendermode_mineral_occluded(void *data, RenderState *state, int x, int y, int z) {
|
"overlay-mineral",
|
||||||
/* no special occlusion here */
|
sizeof(RenderPrimitiveMineral),
|
||||||
return rendermode_overlay.occluded(data, state, x, y, z);
|
overlay_mineral_start,
|
||||||
}
|
overlay_mineral_finish,
|
||||||
|
NULL,
|
||||||
static int
|
NULL,
|
||||||
rendermode_mineral_hidden(void *data, RenderState *state, int x, int y, int z) {
|
overlay_draw,
|
||||||
/* no special hiding here */
|
|
||||||
return rendermode_overlay.hidden(data, state, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rendermode_mineral_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
|
||||||
/* draw normally */
|
|
||||||
rendermode_overlay.draw(data, state, src, mask, mask_light);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RenderModeOption rendermode_mineral_options[] = {
|
|
||||||
{"minerals", "a list of (blockid, (r, g, b)) tuples for coloring minerals"},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
RenderModeInterface rendermode_mineral = {
|
|
||||||
"mineral", "Mineral",
|
|
||||||
"draws a colored overlay showing where ores are located",
|
|
||||||
rendermode_mineral_options,
|
|
||||||
&rendermode_overlay,
|
|
||||||
sizeof(RenderModeMineral),
|
|
||||||
rendermode_mineral_start,
|
|
||||||
rendermode_mineral_finish,
|
|
||||||
rendermode_mineral_occluded,
|
|
||||||
rendermode_mineral_hidden,
|
|
||||||
rendermode_mineral_draw,
|
|
||||||
};
|
};
|
||||||
92
overviewer_core/src/primitives/overlay-spawn.c
Normal file
92
overviewer_core/src/primitives/overlay-spawn.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Minecraft Overviewer.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "overlay.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* inherits from overlay */
|
||||||
|
RenderPrimitiveOverlay parent;
|
||||||
|
} RenderPrimitiveSpawn;
|
||||||
|
|
||||||
|
static void get_color(void *data, RenderState *state,
|
||||||
|
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
||||||
|
|
||||||
|
RenderPrimitiveSpawn* self = (RenderPrimitiveSpawn *)data;
|
||||||
|
int x = state->x, y = state->y, z = state->z;
|
||||||
|
int y_light = y + 1;
|
||||||
|
unsigned char blocklight, skylight;
|
||||||
|
PyObject *block_py;
|
||||||
|
|
||||||
|
/* set a nice, pretty red color */
|
||||||
|
*r = 229;
|
||||||
|
*g = 36;
|
||||||
|
*b = 38;
|
||||||
|
|
||||||
|
/* default to no overlay, until told otherwise */
|
||||||
|
*a = 0;
|
||||||
|
|
||||||
|
if (block_has_property(state->block, NOSPAWN)) {
|
||||||
|
/* nothing can spawn on this */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocklight = get_data(state, BLOCKLIGHT, x, y_light, z);
|
||||||
|
skylight = get_data(state, SKYLIGHT, x, y_light, z);
|
||||||
|
|
||||||
|
if (MAX(blocklight, skylight) <= 7) {
|
||||||
|
/* hostile mobs spawn in daylight */
|
||||||
|
*a = 240;
|
||||||
|
} else if (MAX(blocklight, skylight - 11) <= 7) {
|
||||||
|
/* hostile mobs spawn at night */
|
||||||
|
*a = 150;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
overlay_spawn_start(void *data, RenderState *state, PyObject *support) {
|
||||||
|
RenderPrimitiveSpawn* self;
|
||||||
|
|
||||||
|
/* first, chain up */
|
||||||
|
int ret = primitive_overlay.start(data, state, support);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* now do custom initializations */
|
||||||
|
self = (RenderPrimitiveSpawn *)data;
|
||||||
|
self->parent.get_color = get_color;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
overlay_spawn_finish(void *data, RenderState *state) {
|
||||||
|
RenderPrimitiveSpawn* self = (RenderPrimitiveSpawn *)data;
|
||||||
|
|
||||||
|
/* chain up */
|
||||||
|
primitive_overlay.finish(data, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPrimitiveInterface primitive_overlay_spawn = {
|
||||||
|
"overlay-spawn",
|
||||||
|
sizeof(RenderPrimitiveSpawn),
|
||||||
|
overlay_spawn_start,
|
||||||
|
overlay_spawn_finish,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
overlay_draw,
|
||||||
|
};
|
||||||
99
overviewer_core/src/primitives/overlay.c
Normal file
99
overviewer_core/src/primitives/overlay.c
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Minecraft Overviewer.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "overlay.h"
|
||||||
|
|
||||||
|
static void get_color(void *data, RenderState *state,
|
||||||
|
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
||||||
|
*r = 200;
|
||||||
|
*g = 200;
|
||||||
|
*b = 255;
|
||||||
|
*a = 155;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
overlay_start(void *data, RenderState *state, PyObject *support) {
|
||||||
|
PyObject *facemasks_py;
|
||||||
|
RenderPrimitiveOverlay *self = (RenderPrimitiveOverlay *)data;
|
||||||
|
|
||||||
|
self->facemask_top = PyObject_GetAttrString(support, "facemask_top");
|
||||||
|
self->white_color = PyObject_GetAttrString(support, "whitecolor");
|
||||||
|
self->get_color = get_color;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
overlay_finish(void *data, RenderState *state) {
|
||||||
|
RenderPrimitiveOverlay *self = (RenderPrimitiveOverlay *)data;
|
||||||
|
|
||||||
|
Py_DECREF(self->facemask_top);
|
||||||
|
Py_DECREF(self->white_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
||||||
|
RenderPrimitiveOverlay *self = (RenderPrimitiveOverlay *)data;
|
||||||
|
unsigned char r, g, b, a;
|
||||||
|
unsigned short top_block;
|
||||||
|
|
||||||
|
// exactly analogous to edge-line code for these special blocks
|
||||||
|
int increment=0;
|
||||||
|
if (state->block == 44) // half-step
|
||||||
|
increment=6;
|
||||||
|
else if (state->block == 78) // snow
|
||||||
|
increment=9;
|
||||||
|
|
||||||
|
/* skip rendering the overlay if we can't see it */
|
||||||
|
top_block = get_data(state, BLOCKS, state->x, state->y+1, state->z);
|
||||||
|
if (!is_transparent(top_block)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check to be sure this block is solid/fluid */
|
||||||
|
if (block_has_property(top_block, SOLID) || block_has_property(top_block, FLUID)) {
|
||||||
|
|
||||||
|
/* top block is fluid or solid, skip drawing */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check to be sure this block is solid/fluid */
|
||||||
|
if (!block_has_property(state->block, SOLID) && !block_has_property(state->block, FLUID)) {
|
||||||
|
|
||||||
|
/* not fluid or solid, skip drawing the overlay */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get our color info */
|
||||||
|
self->get_color(data, state, &r, &g, &b, &a);
|
||||||
|
|
||||||
|
/* do the overlay */
|
||||||
|
if (a > 0) {
|
||||||
|
alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
|
||||||
|
tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPrimitiveInterface primitive_overlay = {
|
||||||
|
"overlay",
|
||||||
|
sizeof(RenderPrimitiveOverlay),
|
||||||
|
overlay_start,
|
||||||
|
overlay_finish,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
overlay_draw,
|
||||||
|
};
|
||||||
31
overviewer_core/src/primitives/overlay.h
Normal file
31
overviewer_core/src/primitives/overlay.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Minecraft Overviewer.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../overviewer.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* top facemask and white color image, for drawing overlays */
|
||||||
|
PyObject *facemask_top, *white_color;
|
||||||
|
/* can be overridden in derived classes to control
|
||||||
|
overlay alpha and color
|
||||||
|
last four vars are r, g, b, a out */
|
||||||
|
void (*get_color)(void *, RenderState *,
|
||||||
|
unsigned char *, unsigned char *, unsigned char *, unsigned char *);
|
||||||
|
} RenderPrimitiveOverlay;
|
||||||
|
extern RenderPrimitiveInterface primitive_overlay;
|
||||||
|
|
||||||
|
void overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light);
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Minecraft Overviewer.
|
|
||||||
*
|
|
||||||
* Minecraft Overviewer is free software: you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "overviewer.h"
|
|
||||||
|
|
||||||
static void get_color(void *data, RenderState *state,
|
|
||||||
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
|
||||||
*r = 200;
|
|
||||||
*g = 200;
|
|
||||||
*b = 255;
|
|
||||||
*a = 155;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_overlay_start(void *data, RenderState *state, PyObject *options) {
|
|
||||||
PyObject *facemasks_py;
|
|
||||||
RenderModeOverlay *self = (RenderModeOverlay *)data;
|
|
||||||
|
|
||||||
facemasks_py = PyObject_GetAttrString(state->support, "facemasks");
|
|
||||||
/* borrowed reference, needs to be incref'd if we keep it */
|
|
||||||
self->facemask_top = PyTuple_GetItem(facemasks_py, 0);
|
|
||||||
Py_INCREF(self->facemask_top);
|
|
||||||
Py_DECREF(facemasks_py);
|
|
||||||
|
|
||||||
self->white_color = PyObject_GetAttrString(state->support, "white_color");
|
|
||||||
self->get_color = get_color;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rendermode_overlay_finish(void *data, RenderState *state) {
|
|
||||||
RenderModeOverlay *self = (RenderModeOverlay *)data;
|
|
||||||
|
|
||||||
Py_DECREF(self->facemask_top);
|
|
||||||
Py_DECREF(self->white_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_overlay_occluded(void *data, RenderState *state, int x, int y, int z) {
|
|
||||||
if ( (x != 0) && (y != 15) && (z != 127) &&
|
|
||||||
!render_mode_hidden(state->rendermode, x-1, y, z) &&
|
|
||||||
!render_mode_hidden(state->rendermode, x, y, z+1) &&
|
|
||||||
!render_mode_hidden(state->rendermode, x, y+1, z) &&
|
|
||||||
!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
|
|
||||||
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) &&
|
|
||||||
!is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_overlay_hidden(void *data, RenderState *state, int x, int y, int z) {
|
|
||||||
/* overlays hide nothing by default */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
|
||||||
RenderModeOverlay *self = (RenderModeOverlay *)data;
|
|
||||||
unsigned char r, g, b, a;
|
|
||||||
|
|
||||||
// exactly analogous to edge-line code for these special blocks
|
|
||||||
int increment=0;
|
|
||||||
if (state->block == 44) // half-step
|
|
||||||
increment=6;
|
|
||||||
else if (state->block == 78) // snow
|
|
||||||
increment=9;
|
|
||||||
|
|
||||||
/* clear the draw space -- set alpha to 0 within mask */
|
|
||||||
tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0);
|
|
||||||
|
|
||||||
/* skip rendering the overlay if we can't see it */
|
|
||||||
if (state->z != 127) {
|
|
||||||
unsigned char top_block = getArrayByte3D(state->blocks, state->x, state->y, state->z+1);
|
|
||||||
if (!is_transparent(top_block)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check to be sure this block is solid/fluid */
|
|
||||||
if (block_has_property(top_block, SOLID) || block_has_property(top_block, FLUID)) {
|
|
||||||
|
|
||||||
/* top block is fluid or solid, skip drawing */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check to be sure this block is solid/fluid */
|
|
||||||
if (!block_has_property(state->block, SOLID) && !block_has_property(state->block, FLUID)) {
|
|
||||||
|
|
||||||
/* not fluid or solid, skip drawing the overlay */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get our color info */
|
|
||||||
self->get_color(data, state, &r, &g, &b, &a);
|
|
||||||
|
|
||||||
/* do the overlay */
|
|
||||||
if (a > 0) {
|
|
||||||
alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
|
|
||||||
tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy + increment, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderModeInterface rendermode_overlay = {
|
|
||||||
"overlay", "Overlay",
|
|
||||||
"base rendermode for informational overlays",
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof(RenderModeOverlay),
|
|
||||||
rendermode_overlay_start,
|
|
||||||
rendermode_overlay_finish,
|
|
||||||
rendermode_overlay_occluded,
|
|
||||||
rendermode_overlay_hidden,
|
|
||||||
rendermode_overlay_draw,
|
|
||||||
};
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Minecraft Overviewer.
|
|
||||||
*
|
|
||||||
* Minecraft Overviewer is free software: you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Minecraft Overviewer is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
||||||
* Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "overviewer.h"
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
static void get_color(void *data, RenderState *state,
|
|
||||||
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
|
|
||||||
|
|
||||||
RenderModeSpawn* self = (RenderModeSpawn *)data;
|
|
||||||
int x = state->x, y = state->y, z = state->z;
|
|
||||||
int z_light = z + 1;
|
|
||||||
unsigned char blocklight, skylight;
|
|
||||||
PyObject *block_py;
|
|
||||||
|
|
||||||
/* set a nice, pretty red color */
|
|
||||||
*r = 229;
|
|
||||||
*g = 36;
|
|
||||||
*b = 38;
|
|
||||||
|
|
||||||
/* default to no overlay, until told otherwise */
|
|
||||||
*a = 0;
|
|
||||||
|
|
||||||
if (block_has_property(state->block, NOSPAWN)) {
|
|
||||||
/* nothing can spawn on this */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
blocklight = getArrayByte3D(self->blocklight, x, y, MIN(127, z_light));
|
|
||||||
|
|
||||||
/* if we're at the top, force 15 (brightest!) skylight */
|
|
||||||
if (z_light == 128) {
|
|
||||||
skylight = 15;
|
|
||||||
} else {
|
|
||||||
skylight = getArrayByte3D(self->skylight, x, y, z_light);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MAX(blocklight, skylight) <= 7) {
|
|
||||||
/* hostile mobs spawn in daylight */
|
|
||||||
*a = 240;
|
|
||||||
} else if (MAX(blocklight, skylight - 11) <= 7) {
|
|
||||||
/* hostile mobs spawn at night */
|
|
||||||
*a = 150;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_spawn_start(void *data, RenderState *state, PyObject *options) {
|
|
||||||
RenderModeSpawn* self;
|
|
||||||
|
|
||||||
/* first, chain up */
|
|
||||||
int ret = rendermode_overlay.start(data, state, options);
|
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* now do custom initializations */
|
|
||||||
self = (RenderModeSpawn *)data;
|
|
||||||
self->blocklight = get_chunk_data(state, CURRENT, BLOCKLIGHT, 1);
|
|
||||||
self->skylight = get_chunk_data(state, CURRENT, SKYLIGHT, 1);
|
|
||||||
|
|
||||||
/* setup custom color */
|
|
||||||
self->parent.get_color = get_color;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rendermode_spawn_finish(void *data, RenderState *state) {
|
|
||||||
/* first free all *our* stuff */
|
|
||||||
RenderModeSpawn* self = (RenderModeSpawn *)data;
|
|
||||||
|
|
||||||
Py_DECREF(self->blocklight);
|
|
||||||
Py_DECREF(self->skylight);
|
|
||||||
|
|
||||||
/* now, chain up */
|
|
||||||
rendermode_overlay.finish(data, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_spawn_occluded(void *data, RenderState *state, int x, int y, int z) {
|
|
||||||
/* no special occlusion here */
|
|
||||||
return rendermode_overlay.occluded(data, state, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
rendermode_spawn_hidden(void *data, RenderState *state, int x, int y, int z) {
|
|
||||||
/* no special hiding here */
|
|
||||||
return rendermode_overlay.hidden(data, state, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
|
|
||||||
/* draw normally */
|
|
||||||
rendermode_overlay.draw(data, state, src, mask, mask_light);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderModeInterface rendermode_spawn = {
|
|
||||||
"spawn", "Spawn",
|
|
||||||
"draws a red overlay where monsters can spawn at night",
|
|
||||||
NULL,
|
|
||||||
&rendermode_overlay,
|
|
||||||
sizeof(RenderModeSpawn),
|
|
||||||
rendermode_spawn_start,
|
|
||||||
rendermode_spawn_finish,
|
|
||||||
rendermode_spawn_occluded,
|
|
||||||
rendermode_spawn_hidden,
|
|
||||||
rendermode_spawn_draw,
|
|
||||||
};
|
|
||||||
@@ -102,38 +102,4 @@ void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject
|
|||||||
works like PyArg_ParseTuple on a support object */
|
works like PyArg_ParseTuple on a support object */
|
||||||
int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...);
|
int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...);
|
||||||
|
|
||||||
/* XXX individual rendermode interface declarations follow */
|
|
||||||
#ifdef OLD_MODES
|
|
||||||
|
|
||||||
/* OVERLAY */
|
|
||||||
typedef struct {
|
|
||||||
/* top facemask and white color image, for drawing overlays */
|
|
||||||
PyObject *facemask_top, *white_color;
|
|
||||||
/* can be overridden in derived classes to control
|
|
||||||
overlay alpha and color
|
|
||||||
last four vars are r, g, b, a out */
|
|
||||||
void (*get_color)(void *, RenderState *,
|
|
||||||
unsigned char *, unsigned char *, unsigned char *, unsigned char *);
|
|
||||||
} RenderModeOverlay;
|
|
||||||
extern RenderModeInterface rendermode_overlay;
|
|
||||||
|
|
||||||
/* SPAWN */
|
|
||||||
typedef struct {
|
|
||||||
/* inherits from overlay */
|
|
||||||
RenderModeOverlay parent;
|
|
||||||
|
|
||||||
PyObject *skylight, *blocklight;
|
|
||||||
} RenderModeSpawn;
|
|
||||||
extern RenderModeInterface rendermode_spawn;
|
|
||||||
|
|
||||||
/* MINERAL */
|
|
||||||
typedef struct {
|
|
||||||
/* inherits from overlay */
|
|
||||||
RenderModeOverlay parent;
|
|
||||||
|
|
||||||
void *minerals;
|
|
||||||
} RenderModeMineral;
|
|
||||||
extern RenderModeInterface rendermode_mineral;
|
|
||||||
#endif /* OLD_MODES */
|
|
||||||
|
|
||||||
#endif /* __RENDERMODES_H_INCLUDED__ */
|
#endif /* __RENDERMODES_H_INCLUDED__ */
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from itertools import product, izip
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .util import iterate_base4, convert_coords, unconvert_coords, roundrobin
|
from .util import iterate_base4, convert_coords, unconvert_coords, roundrobin
|
||||||
|
from .util import FileReplacer
|
||||||
from .optimizeimages import optimize_image
|
from .optimizeimages import optimize_image
|
||||||
import c_overviewer
|
import c_overviewer
|
||||||
|
|
||||||
@@ -189,7 +190,9 @@ class TileSet(object):
|
|||||||
|
|
||||||
renderchecks
|
renderchecks
|
||||||
An integer indicating how to determine which tiles need updating
|
An integer indicating how to determine which tiles need updating
|
||||||
and which don't. This is one of three levels:
|
and which don't. This key is optional; if not specified, an
|
||||||
|
appropriate mode is determined from the persistent config obtained
|
||||||
|
from the asset manager. This is one of three levels:
|
||||||
|
|
||||||
0
|
0
|
||||||
Only render tiles that have chunks with a greater mtime than
|
Only render tiles that have chunks with a greater mtime than
|
||||||
@@ -258,8 +261,38 @@ class TileSet(object):
|
|||||||
self.am = assetmanagerobj
|
self.am = assetmanagerobj
|
||||||
self.textures = texturesobj
|
self.textures = texturesobj
|
||||||
|
|
||||||
self.last_rendertime = self.am.get_tileset_config(self.options.get("name")).get('last_rendertime', 0)
|
config = self.am.get_tileset_config(self.options.get("name"))
|
||||||
self.this_rendertime = time.time()
|
self.config = config
|
||||||
|
|
||||||
|
self.last_rendertime = config.get('last_rendertime', 0)
|
||||||
|
|
||||||
|
if "renderchecks" not in self.options:
|
||||||
|
if not config:
|
||||||
|
# No persistent config? This is a full render then.
|
||||||
|
self.options['renderchecks'] = 2
|
||||||
|
logging.debug("This is the first time rendering %s. Doing" +
|
||||||
|
" a full-render",
|
||||||
|
self.options['name'])
|
||||||
|
elif config.get("render_in_progress", False):
|
||||||
|
# The last render must have been interrupted. The default should be
|
||||||
|
# 1 then, not 0
|
||||||
|
logging.warning(
|
||||||
|
"The last render for %s didn't finish. I'll be " +
|
||||||
|
"scanning all the tiles to make sure everything's up "+
|
||||||
|
"to date.",
|
||||||
|
self.options['name'],
|
||||||
|
)
|
||||||
|
logging.warning("You won't get percentage progress for "+
|
||||||
|
"this run only, because I don't know how many tiles "+
|
||||||
|
"need rendering. I'll be checking them as I go")
|
||||||
|
self.options['renderchecks'] = 1
|
||||||
|
else:
|
||||||
|
logging.debug("No rendercheck mode specified for %s. "+
|
||||||
|
"Rendering tile whose chunks have changed since %s",
|
||||||
|
self.options['name'],
|
||||||
|
time.strftime("%x %X", time.localtime(self.last_rendertime)),
|
||||||
|
)
|
||||||
|
self.options['renderchecks'] = 0
|
||||||
|
|
||||||
# Throughout the class, self.outputdir is an absolute path to the
|
# Throughout the class, self.outputdir is an absolute path to the
|
||||||
# directory where we output tiles. It is assumed to exist.
|
# directory where we output tiles. It is assumed to exist.
|
||||||
@@ -298,8 +331,11 @@ class TileSet(object):
|
|||||||
logging.warning("Just letting you know, your map requries %s zoom levels. This is REALLY big!",
|
logging.warning("Just letting you know, your map requries %s zoom levels. This is REALLY big!",
|
||||||
self.treedepth)
|
self.treedepth)
|
||||||
|
|
||||||
# Do any tile re-arranging if necessary
|
# Do any tile re-arranging if necessary. Skip if there was no config
|
||||||
self._rearrange_tiles()
|
# from the asset-manager, which typically indicates this is a new
|
||||||
|
# render
|
||||||
|
if self.config:
|
||||||
|
self._rearrange_tiles()
|
||||||
|
|
||||||
# Do the chunk scan here
|
# Do the chunk scan here
|
||||||
self.dirtytree = self._chunk_scan()
|
self.dirtytree = self._chunk_scan()
|
||||||
@@ -383,6 +419,20 @@ class TileSet(object):
|
|||||||
name = str(tilepath[-1])
|
name = str(tilepath[-1])
|
||||||
self._render_compositetile(dest, name)
|
self._render_compositetile(dest, name)
|
||||||
|
|
||||||
|
def get_initial_data(self):
|
||||||
|
"""This is called similarly to get_persistent_data, but is called after
|
||||||
|
do_preprocessing but before any work is acutally done.
|
||||||
|
|
||||||
|
"""
|
||||||
|
d = self.get_persistent_data()
|
||||||
|
# This is basically the same as get_persistent_data() with the
|
||||||
|
# following exceptions:
|
||||||
|
# * last_rendertime is not changed
|
||||||
|
# * A key "render_in_progress" is set to True
|
||||||
|
d['last_rendertime'] = self.last_rendertime
|
||||||
|
d['render_in_progress'] = True
|
||||||
|
return d
|
||||||
|
|
||||||
def get_persistent_data(self):
|
def get_persistent_data(self):
|
||||||
"""Returns a dictionary representing the persistent data of this
|
"""Returns a dictionary representing the persistent data of this
|
||||||
TileSet. Typically this is called by AssetManager
|
TileSet. Typically this is called by AssetManager
|
||||||
@@ -400,7 +450,7 @@ class TileSet(object):
|
|||||||
bgcolor = bgcolorformat(self.options.get('bgcolor')),
|
bgcolor = bgcolorformat(self.options.get('bgcolor')),
|
||||||
world = self.options.get('worldname_orig') +
|
world = self.options.get('worldname_orig') +
|
||||||
(" - " + self.options.get('dimension') if self.options.get('dimension') != 'default' else ''),
|
(" - " + self.options.get('dimension') if self.options.get('dimension') != 'default' else ''),
|
||||||
last_rendertime = self.this_rendertime,
|
last_rendertime = self.max_chunk_mtime,
|
||||||
imgextension = self.imgextension,
|
imgextension = self.imgextension,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
@@ -463,16 +513,17 @@ class TileSet(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
curdepth = get_dirdepth(self.outputdir)
|
curdepth = self.config['zoomLevels']
|
||||||
except Exception:
|
except KeyError:
|
||||||
logging.critical("Could not determine existing tile tree depth. Does it exist?")
|
return
|
||||||
raise
|
|
||||||
|
|
||||||
if curdepth == 1:
|
if curdepth == 1:
|
||||||
# Skip a depth 1 tree. A depth 1 tree pretty much can't happen, so
|
# Skip a depth 1 tree. A depth 1 tree pretty much can't happen, so
|
||||||
# when we detect this it usually means the tree is actually empty
|
# when we detect this it usually means the tree is actually empty
|
||||||
return
|
return
|
||||||
logging.debug("Current tree depth was detected to be %s. Target tree depth is %s", curdepth, self.treedepth)
|
logging.debug("Current tree depth for %s is reportedly %s. Target tree depth is %s",
|
||||||
|
self.options['name'],
|
||||||
|
curdepth, self.treedepth)
|
||||||
if self.treedepth != curdepth:
|
if self.treedepth != curdepth:
|
||||||
if self.treedepth > curdepth:
|
if self.treedepth > curdepth:
|
||||||
logging.warning("Your map seems to have expanded beyond its previous bounds.")
|
logging.warning("Your map seems to have expanded beyond its previous bounds.")
|
||||||
@@ -648,8 +699,8 @@ class TileSet(object):
|
|||||||
dirty.add(tile.path)
|
dirty.add(tile.path)
|
||||||
|
|
||||||
t = int(time.time()-stime)
|
t = int(time.time()-stime)
|
||||||
logging.debug("%s finished chunk scan. %s chunks scanned in %s second%s",
|
logging.debug("Finished chunk scan for %s. %s chunks scanned in %s second%s",
|
||||||
self, chunkcount, t,
|
self.options['name'], chunkcount, t,
|
||||||
"s" if t != 1 else "")
|
"s" if t != 1 else "")
|
||||||
|
|
||||||
self.max_chunk_mtime = max_chunk_mtime
|
self.max_chunk_mtime = max_chunk_mtime
|
||||||
@@ -728,22 +779,23 @@ class TileSet(object):
|
|||||||
img.paste(quad, path[0])
|
img.paste(quad, path[0])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'", path[1], e)
|
logging.warning("Couldn't open %s. It may be corrupt. Error was '%s'", path[1], e)
|
||||||
logging.warning("I'm going to try and delete it. You will need to run the render again")
|
logging.warning("I'm going to try and delete it. You will need to run the render again and with --check-tiles")
|
||||||
try:
|
try:
|
||||||
os.unlink(path[1])
|
os.unlink(path[1])
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.error("While attempting to delete corrupt image %s, an error was encountered. You will need to delete it yourself. Error was '%s'", path[1], e)
|
logging.error("While attempting to delete corrupt image %s, an error was encountered. You will need to delete it yourself. Error was '%s'", path[1], e)
|
||||||
|
|
||||||
# Save it
|
# Save it
|
||||||
if imgformat == 'jpg':
|
with FileReplacer(imgpath) as tmppath:
|
||||||
img.save(imgpath, quality=self.options['imgquality'], subsampling=0)
|
if imgformat == 'jpg':
|
||||||
else: # png
|
img.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
||||||
img.save(imgpath)
|
else: # png
|
||||||
|
img.save(tmppath, "png")
|
||||||
if self.options['optimizeimg']:
|
|
||||||
optimize_image(imgpath, imgformat, self.options['optimizeimg'])
|
if self.options['optimizeimg']:
|
||||||
|
optimize_image(tmppath, imgformat, self.options['optimizeimg'])
|
||||||
|
|
||||||
os.utime(imgpath, (max_mtime, max_mtime))
|
os.utime(tmppath, (max_mtime, max_mtime))
|
||||||
|
|
||||||
def _render_rendertile(self, tile):
|
def _render_rendertile(self, tile):
|
||||||
"""Renders the given render-tile.
|
"""Renders the given render-tile.
|
||||||
@@ -829,15 +881,16 @@ class TileSet(object):
|
|||||||
#draw.text((96,96), "c,r: %s,%s" % (col, row), fill='red')
|
#draw.text((96,96), "c,r: %s,%s" % (col, row), fill='red')
|
||||||
|
|
||||||
# Save them
|
# Save them
|
||||||
if self.imgextension == 'jpg':
|
with FileReplacer(imgpath) as tmppath:
|
||||||
tileimg.save(imgpath, quality=self.options['imgquality'], subsampling=0)
|
if self.imgextension == 'jpg':
|
||||||
else: # png
|
tileimg.save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0)
|
||||||
tileimg.save(imgpath)
|
else: # png
|
||||||
|
tileimg.save(tmppath, "png")
|
||||||
|
|
||||||
if self.options['optimizeimg']:
|
if self.options['optimizeimg']:
|
||||||
optimize_image(imgpath, self.imgextension, self.options['optimizeimg'])
|
optimize_image(tmppath, self.imgextension, self.options['optimizeimg'])
|
||||||
|
|
||||||
os.utime(imgpath, (max_chunk_mtime, max_chunk_mtime))
|
os.utime(tmppath, (max_chunk_mtime, max_chunk_mtime))
|
||||||
|
|
||||||
def _iterate_and_check_tiles(self, path):
|
def _iterate_and_check_tiles(self, path):
|
||||||
"""A generator function over all tiles that need rendering in the
|
"""A generator function over all tiles that need rendering in the
|
||||||
@@ -946,34 +999,6 @@ class TileSet(object):
|
|||||||
# Nope.
|
# Nope.
|
||||||
yield path, max_child_mtime, False
|
yield path, max_child_mtime, False
|
||||||
|
|
||||||
|
|
||||||
def get_dirdepth(outputdir):
|
|
||||||
"""Returns the current depth of the tree on disk
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Traverses down the first directory until it reaches one with no
|
|
||||||
# subdirectories. While all paths of the tree may not exist, all paths
|
|
||||||
# of the tree should and are assumed to be the same depth
|
|
||||||
|
|
||||||
# This function returns a list of all subdirectories of the given
|
|
||||||
# directory. It's slightly more complicated than you'd think it should be
|
|
||||||
# because one must turn directory names returned by os.listdir into
|
|
||||||
# relative/absolute paths before they can be passed to os.path.isdir()
|
|
||||||
getsubdirs = lambda directory: [
|
|
||||||
abssubdir
|
|
||||||
for abssubdir in
|
|
||||||
(os.path.join(directory,subdir) for subdir in os.listdir(directory))
|
|
||||||
if os.path.isdir(abssubdir)
|
|
||||||
]
|
|
||||||
|
|
||||||
depth = 1
|
|
||||||
subdirs = getsubdirs(outputdir)
|
|
||||||
while subdirs:
|
|
||||||
subdirs = getsubdirs(subdirs[0])
|
|
||||||
depth += 1
|
|
||||||
|
|
||||||
return depth
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# The following two functions define the mapping from chunks to tiles and back.
|
# The following two functions define the mapping from chunks to tiles and back.
|
||||||
# The mapping from chunks to tiles (get_tiles_by_chunk()) is used during the
|
# The mapping from chunks to tiles (get_tiles_by_chunk()) is used during the
|
||||||
|
|||||||
@@ -152,6 +152,74 @@ def unconvert_coords(col, row):
|
|||||||
# col - row = chunkx + chunkx => (col - row)/2 = chunkx
|
# col - row = chunkx + chunkx => (col - row)/2 = chunkx
|
||||||
return ((col - row) / 2, (col + row) / 2)
|
return ((col - row) / 2, (col + row) / 2)
|
||||||
|
|
||||||
|
# Define a context manager to handle atomic renaming or "just forget it write
|
||||||
|
# straight to the file" depending on whether os.rename provides atomic
|
||||||
|
# overwrites.
|
||||||
|
# Detect whether os.rename will overwrite files
|
||||||
|
import tempfile
|
||||||
|
with tempfile.NamedTemporaryFile() as f1:
|
||||||
|
with tempfile.NamedTemporaryFile() as f2:
|
||||||
|
try:
|
||||||
|
os.rename(f1.name,f2.name)
|
||||||
|
except OSError:
|
||||||
|
renameworks = False
|
||||||
|
else:
|
||||||
|
renameworks = True
|
||||||
|
# re-make this file so it can be deleted without error
|
||||||
|
open(f1.name, 'w').close()
|
||||||
|
del tempfile,f1,f2
|
||||||
|
doc = """This class acts as a context manager for files that are to be written
|
||||||
|
out overwriting an existing file.
|
||||||
|
|
||||||
|
The parameter is the destination filename. The value returned into the context
|
||||||
|
is the filename that should be used. On systems that support an atomic
|
||||||
|
os.rename(), the filename will actually be a temporary file, and it will be
|
||||||
|
atomically replaced over the destination file on exit.
|
||||||
|
|
||||||
|
On systems that don't support an atomic rename, the filename returned is the
|
||||||
|
filename given.
|
||||||
|
|
||||||
|
If an error is encountered, the file is attempted to be removed, and the error
|
||||||
|
is propagated.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
with FileReplacer("config") as configname:
|
||||||
|
with open(configout, 'w') as configout:
|
||||||
|
configout.write(newconfig)
|
||||||
|
"""
|
||||||
|
if renameworks:
|
||||||
|
class FileReplacer(object):
|
||||||
|
__doc__ = doc
|
||||||
|
def __init__(self, destname):
|
||||||
|
self.destname = destname
|
||||||
|
self.tmpname = destname + ".tmp"
|
||||||
|
def __enter__(self):
|
||||||
|
# rename works here. Return a temporary filename
|
||||||
|
return self.tmpname
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if exc_type:
|
||||||
|
# error
|
||||||
|
try:
|
||||||
|
os.remove(self.tmpname)
|
||||||
|
except Exception, e:
|
||||||
|
logging.warning("An error was raised, so I was doing "
|
||||||
|
"some cleanup first, but I couldn't remove "
|
||||||
|
"'%s'!", self.tmpname)
|
||||||
|
else:
|
||||||
|
# atomic rename into place
|
||||||
|
os.rename(self.tmpname, self.destname)
|
||||||
|
else:
|
||||||
|
class FileReplacer(object):
|
||||||
|
__doc__ = doc
|
||||||
|
def __init__(self, destname):
|
||||||
|
self.destname = destname
|
||||||
|
def __enter__(self):
|
||||||
|
return self.destname
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
return
|
||||||
|
del renameworks
|
||||||
|
|
||||||
# Logging related classes are below
|
# Logging related classes are below
|
||||||
|
|
||||||
# Some cool code for colored logging:
|
# Some cool code for colored logging:
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ class ChunkDoesntExist(Exception):
|
|||||||
|
|
||||||
def log_other_exceptions(func):
|
def log_other_exceptions(func):
|
||||||
"""A decorator that prints out any errors that are not
|
"""A decorator that prints out any errors that are not
|
||||||
ChunkDoesntExist errors. This decorates get_chunk because the C
|
ChunkDoesntExist errors. This should decorate any functions or
|
||||||
code is likely to swallow exceptions, so this will at least make
|
methods called by the C code, such as get_chunk(), because the C
|
||||||
them visible.
|
code is likely to swallow exceptions. This will at least make them
|
||||||
|
visible.
|
||||||
"""
|
"""
|
||||||
functools.wraps(func)
|
functools.wraps(func)
|
||||||
def newfunc(*args):
|
def newfunc(*args):
|
||||||
@@ -359,9 +359,6 @@ class RegionSet(object):
|
|||||||
return chunk_data
|
return chunk_data
|
||||||
|
|
||||||
|
|
||||||
def rotate(self, north_direction):
|
|
||||||
return RotatedRegionSet(self.regiondir, north_direction)
|
|
||||||
|
|
||||||
def iterate_chunks(self):
|
def iterate_chunks(self):
|
||||||
"""Returns an iterator over all chunk metadata in this world. Iterates
|
"""Returns an iterator over all chunk metadata in this world. Iterates
|
||||||
over tuples of integers (x,z,mtime) for each chunk. Other chunk data
|
over tuples of integers (x,z,mtime) for each chunk. Other chunk data
|
||||||
@@ -412,6 +409,33 @@ class RegionSet(object):
|
|||||||
x = int(p[1])
|
x = int(p[1])
|
||||||
y = int(p[2])
|
y = int(p[2])
|
||||||
yield (x, y, path)
|
yield (x, y, path)
|
||||||
|
|
||||||
|
class RegionSetWrapper(object):
|
||||||
|
"""This is the base class for all "wrappers" of RegionSet objects. A
|
||||||
|
wrapper is an object that acts similarly to a subclass: some methods are
|
||||||
|
overridden and functionality is changed, others may not be. The difference
|
||||||
|
here is that these wrappers may wrap each other, forming chains.
|
||||||
|
|
||||||
|
In fact, subclasses of this object may act exactly as if they've subclassed
|
||||||
|
the original RegionSet object, except the first parameter of the
|
||||||
|
constructor is a regionset object, not a regiondir.
|
||||||
|
|
||||||
|
This class must implement the full public interface of RegionSet objects
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, rsetobj):
|
||||||
|
self._r = rsetobj
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self._r.get_type()
|
||||||
|
def get_biome_data(self, x, z):
|
||||||
|
return self._r.get_biome_data(x,z)
|
||||||
|
def get_chunk(self, x, z):
|
||||||
|
return self._r.get_chunk(x,z)
|
||||||
|
def iterate_chunks(self):
|
||||||
|
return self._r.iterate_chunks()
|
||||||
|
def get_chunk_mtime(self, x, z):
|
||||||
|
return self._r.get_chunk_mtime(x,z)
|
||||||
|
|
||||||
# see RegionSet.rotate. These values are chosen so that they can be
|
# see RegionSet.rotate. These values are chosen so that they can be
|
||||||
# passed directly to rot90; this means that they're the number of
|
# passed directly to rot90; this means that they're the number of
|
||||||
@@ -421,7 +445,7 @@ UPPER_RIGHT = 1 ## - Return the world such that north is down the +X axis (rotat
|
|||||||
LOWER_RIGHT = 2 ## - Return the world such that north is down the +Z axis (rotate 180 degrees)
|
LOWER_RIGHT = 2 ## - Return the world such that north is down the +Z axis (rotate 180 degrees)
|
||||||
LOWER_LEFT = 3 ## - Return the world such that north is down the -X axis (rotate 90 degrees clockwise)
|
LOWER_LEFT = 3 ## - Return the world such that north is down the -X axis (rotate 90 degrees clockwise)
|
||||||
|
|
||||||
class RotatedRegionSet(RegionSet):
|
class RotatedRegionSet(RegionSetWrapper):
|
||||||
"""A regionset, only rotated such that north points in the given direction
|
"""A regionset, only rotated such that north points in the given direction
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -433,39 +457,41 @@ class RotatedRegionSet(RegionSet):
|
|||||||
_ROTATE_180 = lambda x,z: (-x,-z)
|
_ROTATE_180 = lambda x,z: (-x,-z)
|
||||||
|
|
||||||
# These take rotated coords and translate into un-rotated coords
|
# These take rotated coords and translate into un-rotated coords
|
||||||
_unrotation_funcs = {
|
_unrotation_funcs = [
|
||||||
0: _NO_ROTATION,
|
_NO_ROTATION,
|
||||||
1: _ROTATE_COUNTERCLOCKWISE,
|
_ROTATE_COUNTERCLOCKWISE,
|
||||||
2: _ROTATE_180,
|
_ROTATE_180,
|
||||||
3: _ROTATE_CLOCKWISE,
|
_ROTATE_CLOCKWISE,
|
||||||
}
|
]
|
||||||
|
|
||||||
# These translate un-rotated coordinates into rotated coordinates
|
# These translate un-rotated coordinates into rotated coordinates
|
||||||
_rotation_funcs = {
|
_rotation_funcs = [
|
||||||
0: _NO_ROTATION,
|
_NO_ROTATION,
|
||||||
1: _ROTATE_CLOCKWISE,
|
_ROTATE_CLOCKWISE,
|
||||||
2: _ROTATE_180,
|
_ROTATE_180,
|
||||||
3: _ROTATE_COUNTERCLOCKWISE,
|
_ROTATE_COUNTERCLOCKWISE,
|
||||||
}
|
]
|
||||||
|
|
||||||
def __init__(self, regiondir, north_dir):
|
def __init__(self, rsetobj, north_dir):
|
||||||
self.north_dir = north_dir
|
self.north_dir = north_dir
|
||||||
self.unrotate = self._unrotation_funcs[north_dir]
|
self.unrotate = self._unrotation_funcs[north_dir]
|
||||||
self.rotate = self._rotation_funcs[north_dir]
|
self.rotate = self._rotation_funcs[north_dir]
|
||||||
|
|
||||||
super(RotatedRegionSet, self).__init__(regiondir)
|
super(RotatedRegionSet, self).__init__(rsetobj)
|
||||||
|
|
||||||
|
|
||||||
# Re-initialize upon unpickling
|
# Re-initialize upon unpickling. This is needed because we store a couple
|
||||||
|
# lambda functions as instance variables
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return (self.regiondir, self.north_dir)
|
return (self._r, self.north_dir)
|
||||||
def __setstate__(self, args):
|
def __setstate__(self, args):
|
||||||
self.__init__(args[0], args[1])
|
self.__init__(args[0], args[1])
|
||||||
|
|
||||||
def get_chunk(self, x, z):
|
def get_chunk(self, x, z):
|
||||||
x,z = self.unrotate(x,z)
|
x,z = self.unrotate(x,z)
|
||||||
chunk_data = super(RotatedRegionSet, self).get_chunk(x,z)
|
chunk_data = dict(super(RotatedRegionSet, self).get_chunk(x,z))
|
||||||
for section in chunk_data['Sections']:
|
for section in chunk_data['Sections']:
|
||||||
|
section = dict(section)
|
||||||
for arrayname in ['Blocks', 'Data', 'SkyLight', 'BlockLight']:
|
for arrayname in ['Blocks', 'Data', 'SkyLight', 'BlockLight']:
|
||||||
array = section[arrayname]
|
array = section[arrayname]
|
||||||
# Since the anvil change, arrays are arranged with axes Y,Z,X
|
# Since the anvil change, arrays are arranged with axes Y,Z,X
|
||||||
|
|||||||
Reference in New Issue
Block a user