diff --git a/.gitignore b/.gitignore index 1918894..ffd3ba6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,8 +22,9 @@ overviewer_core/c_overviewer.pyd overviewer_core/c_overviewer_d.pyd overviewer_core/c_overviewer.dylib -# generated version file +# generated files overviewer_core/overviewer_version.py +overviewer_core/src/primitives.h # Mac OS X noise .DS_Store diff --git a/overviewer.py b/overviewer.py index 60fc7f1..7f02289 100755 --- a/overviewer.py +++ b/overviewer.py @@ -18,25 +18,13 @@ import platform import sys +# quick version check if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6): print "Sorry, the Overviewer requires at least Python 2.6 to run" if sys.version_info[0] >= 3: print "and will not run on Python 3.0 or later" sys.exit(1) -isBareConsole = False - -if platform.system() == 'Windows': - try: - import ctypes - GetConsoleProcessList = ctypes.windll.kernel32.GetConsoleProcessList - num = GetConsoleProcessList(ctypes.byref(ctypes.c_int(0)), ctypes.c_int(1)) - if (num == 1): - isBareConsole = True - - except Exception: - pass - import os import os.path import re @@ -44,110 +32,13 @@ import subprocess import multiprocessing import time import logging -from overviewer_core import util - -def doExit(msg=None, code=1, wait=None, consoleMsg=True): - '''Exits Overviewer. If `wait` is None, the default - will be true is 'isBareConsole' is true''' - global isBareConsole - if msg: - print msg - - if wait == None: - if isBareConsole: - if consoleMsg: - print "\n" - print "The Overviewer is a console program. Please open a Windows command prompt" - print "first and run Overviewer from there. Further documentation is available at" - print "http://docs.overviewer.org/\n" - print "Press [Enter] to close this window." - raw_input() - else: - if wait: - if consoleMsg: - print "\n" - print "The Overviewer is a console program. Please open a Windows command prompt" - print "first and run Overviewer from there. Further documentation is available at" - print "http://docs.overviewer.org/\n" - print "Press [Enter] to close this window." - raw_input() - - sys.exit(code) - - -this_dir = util.get_program_path() - -# make sure the c_overviewer extension is available -try: - from overviewer_core import c_overviewer -except ImportError: - ## if this is a frozen windows package, the following error messages about - ## building the c_overviewer extension are not appropriate - if hasattr(sys, "frozen"): - print "Something has gone wrong importing the c_overviewer extension. Please" - print "make sure the 2008 and 2010 redistributable packages from Microsoft" - print "are installed." - doExit() - - - ## try to find the build extension - ext = os.path.join(this_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so")) - if os.path.exists(ext): - print "Something has gone wrong importing the c_overviewer extension. Please" - print "make sure it is up-to-date (clean and rebuild)" - doExit() - - import traceback - traceback.print_exc() - - print "" - print "You need to compile the c_overviewer module to run Minecraft Overviewer." - print "Run `python setup.py build`, or see the README for details." - doExit() - -from overviewer_core import textures - -if hasattr(sys, "frozen"): - pass # we don't bother with a compat test since it should always be in sync -elif "extension_version" in dir(c_overviewer): - # check to make sure the binary matches the headers - if os.path.exists(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")): - with open(os.path.join(this_dir, "overviewer_core", "src", "overviewer.h")) as f: - lines = f.readlines() - lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines) - if lines: - l = lines[0] - if int(l.split()[2].strip()) != c_overviewer.extension_version(): - print "Please rebuild your c_overviewer module. It is out of date!" - doExit(code=1, consoleMsg=True) -else: - print "Please rebuild your c_overviewer module. It is out of date!" - doExit() - from optparse import OptionParser + +from overviewer_core import util +from overviewer_core import textures from overviewer_core import optimizeimages, world -from overviewer_core import googlemap from overviewer_core import configParser, tileset, assetmanager, dispatcher -# definitions of built-in custom modes -# usually because what used to be a mode became an option -# for example, night mode -builtin_custom_rendermodes = {} -# 'night' : { -# 'parent' : 'lighting', -# 'label' : 'Night', -# 'description' : 'like "lighting", except at night', -# 'options' : {'night' : True} -# }, - -# 'smooth-night' : { -# 'parent' : 'smooth-lighting', -# 'label' : 'Smooth Night', -# 'description' : 'like "lighting", except smooth and at night', -# 'options' : {'night' : True} -# }, -# } - helptext = """ %prog [OPTIONS] """ @@ -195,7 +86,6 @@ def configure_logger(loglevel=logging.INFO, verbose=False): logger.setLevel(loglevel) def main(): - # bootstrap the logger with defaults configure_logger() @@ -204,7 +94,7 @@ def main(): except NotImplementedError: cpus = 1 - avail_rendermodes = c_overviewer.get_render_modes() + #avail_rendermodes = c_overviewer.get_render_modes() avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right', 'auto'] # revert to a vanilla OptionParser for right now @@ -216,7 +106,6 @@ def main(): #parser.add_option("--forcerender", dest="forcerender", help="Force re-rendering the entire map (or the given regionlist). Useful for re-rendering without deleting it.", action="store_true") #parser.add_option("--stochastic-render", dest="stochastic_render", help="Rerender a non-updated tile randomly, with the given probability (between 0 and 1). Useful for incrementally updating a map with a new mode.", type="float", advanced=True, default=0.0, metavar="PROBABILITY") #parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by ',', ':', or '/'. Use --list-rendermodes to list them all.", type="choice", 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.") #parser.add_option("--rendermode-options", dest="rendermode_options", default={}, advanced=True, help="Used to specify options for different rendermodes. Only useful in a settings.py file") #parser.add_option("--custom-rendermodes", dest="custom_rendermodes", default={}, advanced=True, help="Used to define custom rendermodes. Only useful in a settings.py file") #parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.", advanced=True ) @@ -253,59 +142,54 @@ def main(): print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS) except ImportError: print "(build info not found)" - pass - doExit(code=0, consoleMsg=False) - - # setup c_overviewer rendermode customs / options - for mode in builtin_custom_rendermodes: - c_overviewer.add_custom_render_mode(mode, builtin_custom_rendermodes[mode]) - # TODO allow custom rendermodes to be specified in a settings.py file - #for mode in options.custom_rendermodes: - # c_overviewer.add_custom_render_mode(mode, options.custom_rendermodes[mode]) - #for mode in options.rendermode_options: - # c_overviewer.set_render_mode_options(mode, options.rendermode_options[mode]) - - - - if options.list_rendermodes: - list_rendermodes() - doExit(code=0, consoleMsg=False) + return 0 if options.check_terrain: import hashlib - from overviewer_core.textures import _find_file - if options.textures_path: - textures._find_file_local_path = options.textures_path + from overviewer_core.textures import Textures + # TODO custom textures path? + tex = Textures() try: - f = _find_file("terrain.png", verbose=True) + f = tex.find_file("terrain.png", verbose=True) except IOError: logging.error("Could not find the file terrain.png") - doExit(code=1, consoleMsg=False) + return 1 h = hashlib.sha1() h.update(f.read()) logging.info("Hash of terrain.png file is: `%s`", h.hexdigest()) - doExit(code=0, consoleMsg=False) + return 0 + + if options.display_config: + # just display the config file and exit + parser.display_config() + return 0 # TODO remove advanced help? needs discussion - #if options.advanced_help: - # parser.advanced_help() - # doExit(code=0, consoleMsg=False) - # TODO right now, we will not let users specify worlds to render on the command line. # TODO in the future, we need to also let worlds be specified on the command line + # if no arguments are provided, print out a helpful message + if len(args) == 0: + # first provide an appropriate error for bare-console users + # that don't provide any options + if util.is_bare_console(): + print "\n" + print "The Overviewer is a console program. Please open a Windows command prompt" + print "first and run Overviewer from there. Further documentation is available at" + print "http://docs.overviewer.org/\n" + else: + # more helpful message for users who know what they're doing + logging.error("You need to give me your world number or directory") + parser.print_help() + list_worlds() + return 1 + # for multiworld, we must specify the *outputdir* on the command line - if len(args) == 1: + elif len(args) == 1: logging.debug("Using %r as the output_directory", args[0]) destdir = os.path.expanduser(args[0]) - - elif len(args) < 1: - logging.error("You need to give me your world number or directory") - parser.print_help() - list_worlds() - doExit(code=1, consoleMsg=True) elif len(args) == 2: # TODO support this usecase worlddir = os.path.expanduser(args[0]) destdir = os.path.expanduser(args[1]) @@ -319,14 +203,7 @@ def main(): if os.path.exists(" ".join(args[start:end])): logging.warning("It looks like you meant to specify \"%s\" as your world dir or your output\n\ dir but you forgot to put quotes around the directory, since it contains spaces." % " ".join(args[start:end])) - doExit(code=1, consoleMsg=False) - - - if options.display_config: - # just display the config file and exit - parser.display_config() - doExit(code=0, consoleMsg=False) - + return 1 # TODO regionlists are per-world #if options.regionlist: @@ -363,11 +240,11 @@ dir but you forgot to put quotes around the directory, since it contains spaces. # except IOError as e: # logging.error("Unable to open file %s to use for changelist." % options.changelist) # logging.error("I/O Error: %s" % e.strerror) - # doExit(code=1, consoleMsg=False) + # return 1 #if options.changelist_format != "auto" and not options.changelist: # logging.error("changelist_format specified without changelist.") - # doExit(code=1, consoleMsg=False) + # return 1 #if options.changelist_format == "auto": # options.changelist_format = "relative" @@ -382,7 +259,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces. tex.generate() except IOError, e: logging.error(str(e)) - doExit(code=1, consoleMsg=False) + return 1 # look at our settings.py file mw_parser = configParser.MultiWorldParser("settings.py") @@ -429,127 +306,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces. dispatch.close() assetMrg.finalize(tilesets) - - sys.exit("early abort") - - # First do world-level preprocessing. This scans the world hierarchy, reads - # in the region files and caches chunk modified times, and determines the - # chunk bounds (max and min in both dimensions) - w = world.World(worlddir) - rset = w.get_regionsets()[0] # use the default - if north_direction == 'auto': - # TODO get this from the asset manager # north_direction = w.persistentData['north_direction'] - options.north_direction = north_direction - - # TODO deal with changed north direction - - # A couple other things we need to figure out about the world: - w.get_regionsets()[0].determine_bounds() - # w.find_true_spawn() - - logging.info("Rendering the following tilesets: %s", ",".join(options.rendermode)) - - bgcolor = (int(options.bg_color[1:3],16), - int(options.bg_color[3:5],16), - int(options.bg_color[5:7],16), - 0) - - # Create the quadtrees. There is one quadtree per rendermode requested, and - # therefore, per output directory hierarchy of tiles. Each quadtree - # individually computes its depth and size. The constructor computes the - # depth of the tree, while the go() method re-arranges tiles if the current - # depth differs from the computed depth. - q = [] - qtree_args = {'depth' : options.zoom, - 'imgformat' : imgformat, - 'imgquality' : options.imgquality, - 'optimizeimg' : optimizeimg, - 'bgcolor' : bgcolor, - 'forcerender' : options.forcerender, - 'rerender_prob' : options.stochastic_render - } - for rendermode in options.rendermode: - if rendermode == 'normal': - qtree = quadtree.QuadtreeGen(rset, destdir, rendermode=rendermode, tiledir='tiles', **qtree_args) - else: - qtree = quadtree.QuadtreeGen(rset, destdir, rendermode=rendermode, **qtree_args) - q.append(qtree) - - # Make sure the quadtrees are the correct depth - for qtree in q: - qtree.check_depth() - - # create the distributed render - r = rendernode.RenderNode(q, options) - # for the pool_initializer - r.builtin_custom_rendermodes = builtin_custom_rendermodes - - # write out the map and web assets - m = googlemap.MapGen(q, configInfo=options) - m.go(options.procs) - - # render the tiles! - r.go(options.procs) - - # finish up the map - m.finalize() - - if options.changelist: - changed=[] - for tile in r.rendered_tiles: - if options.changelist_format=="absolute": - tile=os.path.abspath(tile) - changed.append(tile) - for zl in range(q[0].p - 1): - tile=os.path.dirname(tile) - changed.append("%s.%s" % (tile, imgformat)) - #Quick and nasty way to remove duplicate entries - changed=list(set(changed)) - changed.sort() - for path in changed: - changefile.write("%s\n" % path) - changefile.close() - -def list_rendermodes(): - "Prints out a pretty list of supported rendermodes" - - def print_mode_tree(line_max, mode, prefix='', last=False): - "Prints out a mode tree for the given mode, with an indent." - - try: - info = c_overviewer.get_render_mode_info(mode) - except ValueError: - info = {} - - print prefix + '+-', mode, - - if 'description' in info: - print " " * (line_max - len(prefix) - len(mode) - 2), - print info['description'] - else: - print - - children = c_overviewer.get_render_mode_children(mode) - for child in children: - child_last = (child == children[-1]) - if last: - child_prefix = ' ' - else: - child_prefix = '| ' - print_mode_tree(line_max, child, prefix=prefix + child_prefix, last=child_last) - - avail_rendermodes = c_overviewer.get_render_modes() - line_lengths = {} - parent_modes = [] - for mode in avail_rendermodes: - inherit = c_overviewer.get_render_mode_inheritance(mode) - if not inherit[0] in parent_modes: - parent_modes.append(inherit[0]) - line_lengths[mode] = 2 * len(inherit) + 1 + len(mode) - - line_length = max(line_lengths.values()) - for mode in parent_modes: - print_mode_tree(line_length, mode, last=(mode == parent_modes[-1])) + return 0 def list_worlds(): "Prints out a brief summary of saves found in the default directory" @@ -582,18 +339,14 @@ def list_worlds(): size = "%.2fMB" % (info['SizeOnDisk'] / 1024. / 1024.) print formatString % (name, size, playstamp, timestamp) - if __name__ == "__main__": multiprocessing.freeze_support() try: - main() + ret = main() + util.exit(ret) except Exception, e: - print e - if e.args[0] == "Exiting": - logging.info("Exiting...") - doExit(code=0, wait=False) logging.exception("""An error has occurred. This may be a bug. Please let us know! See http://docs.overviewer.org/en/latest/index.html#help This is the error that occurred:""") - doExit(code=1, consoleMsg=False) + util.exit(1) diff --git a/overviewer_core/__init__.py b/overviewer_core/__init__.py index c3d3680..ec4081e 100644 --- a/overviewer_core/__init__.py +++ b/overviewer_core/__init__.py @@ -1,7 +1,71 @@ -# c_overviewer must be imported first, because it imports other -# modules; leaving this out can lead to bad dependency loops +# +# Code to check to make sure c_overviewer is built and working +# -try: - import c_overviewer -except ImportError: - pass +import os.path +import platform +import traceback +import sys + +import util + +def check_c_overviewer(): + """Check to make sure c_overviewer works and is up-to-date. Prints + out a helpful error and returns 1 if something's wrong, returns 0 + otherwise. + """ + root_dir = util.get_program_path() + # make sure the c_overviewer extension is available + try: + import c_overviewer + except ImportError: + ## if this is a frozen windows package, the following error messages about + ## building the c_overviewer extension are not appropriate + if hasattr(sys, "frozen") and platform.system() == 'Windows': + print "Something has gone wrong importing the c_overviewer extension. Please" + print "make sure the 2008 and 2010 redistributable packages from Microsoft" + print "are installed." + return 1 + + ## try to find the build extension + ext = os.path.join(root_dir, "overviewer_core", "c_overviewer.%s" % ("pyd" if platform.system() == "Windows" else "so")) + if os.path.exists(ext): + traceback.print_exc() + print "" + print "Something has gone wrong importing the c_overviewer extension. Please" + print "make sure it is up-to-date (clean and rebuild)" + return 1 + + print "You need to compile the c_overviewer module to run Minecraft Overviewer." + print "Run `python setup.py build`, or see the README for details." + return 1 + + # + # make sure it's up-to-date + # + + if hasattr(sys, "frozen"): + pass # we don't bother with a compat test since it should always be in sync + elif "extension_version" in dir(c_overviewer): + # check to make sure the binary matches the headers + if os.path.exists(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")): + with open(os.path.join(root_dir, "overviewer_core", "src", "overviewer.h")) as f: + lines = f.readlines() + lines = filter(lambda x: x.startswith("#define OVERVIEWER_EXTENSION_VERSION"), lines) + if lines: + l = lines[0] + if int(l.split()[2].strip()) != c_overviewer.extension_version(): + print "Please rebuild your c_overviewer module. It is out of date!" + return 1 + else: + print "Please rebuild your c_overviewer module. It is out of date!" + return 1 + + # all good! + return 0 + +# only check the module if we're not setup.py +if not sys.argv[0].endswith("setup.py"): + ret = check_c_overviewer() + if ret > 0: + util.exit(ret) diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index e3386a3..ffa2e43 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -16,59 +16,125 @@ from PIL import Image import textures -# Render 3 blending masks for lighting -# first is top (+Z), second is left (-X), third is right (+Y) -def generate_facemasks(): - white = Image.new("L", (24,24), 255) - - top = Image.new("L", (24,24), 0) - left = Image.new("L", (24,24), 0) - whole = Image.new("L", (24,24), 0) - - toppart = textures.Textures.transform_image_top(white) - leftpart = textures.Textures.transform_image_side(white) - - # using the real PIL paste here (not alpha_over) because there is - # no alpha channel (and it's mode "L") - top.paste(toppart, (0,0)) - left.paste(leftpart, (0,6)) - right = left.transpose(Image.FLIP_LEFT_RIGHT) - - # Manually touch up 6 pixels that leave a gap, like in - # textures._build_block() - for x,y in [(13,23), (17,21), (21,19)]: - right.putpixel((x,y), 255) - for x,y in [(3,4), (7,2), (11,0)]: - top.putpixel((x,y), 255) - - # special fix for chunk boundary stipple - for x,y in [(13,11), (17,9), (21,7)]: - right.putpixel((x,y), 0) - - return (top, left, right) -facemasks = generate_facemasks() -black_color = Image.new("RGB", (24,24), (0,0,0)) -white_color = Image.new("RGB", (24,24), (255,255,255)) - -# Render 128 different color images for color coded depth blending in cave mode -def generate_depthcolors(): - depth_colors = [] - r = 255 - g = 0 - b = 0 - for z in range(128): - depth_colors.append(r) - depth_colors.append(g) - depth_colors.append(b) +class RenderPrimitive(object): + options = {} + name = None + def __init__(self, **kwargs): + if self.name is None: + raise RuntimeError("RenderPrimitive cannot be used directly") - if z < 32: - g += 7 - elif z < 64: - r -= 7 - elif z < 96: - b += 7 - else: - g -= 7 + self.option_values = {} + for key, val in kwargs.iteritems(): + if not key in self.options: + raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key)) + self.option_values[key] = val + + # set up defaults + for name, (description, default) in self.options.iteritems(): + if not name in self.option_values: + self.option_values[name] = default - return depth_colors -depth_colors = generate_depthcolors() +class Base(RenderPrimitive): + name = "base" + +class Nether(RenderPrimitive): + name = "nether" + +class HeightFading(RenderPrimitive): + name = "height-fading" + + black_color = Image.new("RGB", (24,24), (0,0,0)) + white_color = Image.new("RGB", (24,24), (255,255,255)) + +class Depth(RenderPrimitive): + name = "depth" + options = { + "min": ("lowest level of blocks to render", 0), + "max": ("highest level of blocks to render", 127), + } + +class EdgeLines(RenderPrimitive): + name = "edge-lines" + options = { + "opacity": ("darkness of the edge lines, from 0.0 to 1.0", 0.15), + } + +class Cave(RenderPrimitive): + name = "cave" + options = { + "only_lit": ("only render lit caves", False), + } + +class DepthTinting(RenderPrimitive): + name = "depth-tinting" + + @property + def depth_colors(self): + depth_colors = getattr(self, "_depth_colors", []) + if depth_colors: + return depth_colors + r = 255 + g = 0 + b = 0 + for z in range(128): + depth_colors.append(r) + depth_colors.append(g) + depth_colors.append(b) + + if z < 32: + g += 7 + elif z < 64: + r -= 7 + elif z < 96: + b += 7 + else: + g -= 7 + + self._depth_colors = depth_colors + return depth_colors + +class Lighting(RenderPrimitive): + name = "lighting" + options = { + "strength": ("how dark to make the shadows, from 0.0 to 1.0", 1.0), + "night": ("whether to use nighttime skylight settings", False), + "color": ("whether to use colored light", False), + } + + @property + def facemasks(self): + facemasks = getattr(self, "_facemasks", None) + if facemasks: + return facemasks + + white = Image.new("L", (24,24), 255) + + top = Image.new("L", (24,24), 0) + left = Image.new("L", (24,24), 0) + whole = Image.new("L", (24,24), 0) + + toppart = textures.Textures.transform_image_top(white) + leftpart = textures.Textures.transform_image_side(white) + + # using the real PIL paste here (not alpha_over) because there is + # no alpha channel (and it's mode "L") + top.paste(toppart, (0,0)) + left.paste(leftpart, (0,6)) + right = left.transpose(Image.FLIP_LEFT_RIGHT) + + # Manually touch up 6 pixels that leave a gap, like in + # textures._build_block() + for x,y in [(13,23), (17,21), (21,19)]: + right.putpixel((x,y), 255) + for x,y in [(3,4), (7,2), (11,0)]: + top.putpixel((x,y), 255) + + # special fix for chunk boundary stipple + for x,y in [(13,11), (17,9), (21,7)]: + right.putpixel((x,y), 0) + + self._facemasks = (top, left, right) + return self._facemasks + +class SmoothLighting(Lighting): + name = "smooth-lighting" diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 22cfabd..f1e7bc2 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -18,7 +18,6 @@ #include "overviewer.h" static PyObject *textures = NULL; -static PyObject *support = NULL; unsigned int max_blockid = 0; unsigned int max_data = 0; @@ -47,11 +46,6 @@ PyObject *init_chunk_render(void) { return NULL; } - support = PyImport_ImportModule("overviewer_core.rendermodes"); - if (!support) { - return NULL; - } - tmp = PyObject_GetAttrString(textures, "max_blockid"); if (!tmp) return NULL; @@ -388,7 +382,7 @@ chunk_render(PyObject *self, PyObject *args) { RenderState state; PyObject *regionset; int chunkx, chunkz; - const char* rendermode_name = NULL; + PyObject *modeobj; PyObject *blockmap; int xoff, yoff; @@ -406,14 +400,11 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *t = NULL; - if (!PyArg_ParseTuple(args, "OiiOiisO", &state.regionset, &state.chunkx, &state.chunkz, &state.img, &xoff, &yoff, &rendermode_name, &state.textures)) + if (!PyArg_ParseTuple(args, "OiiOiiOO", &state.regionset, &state.chunkx, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures)) return NULL; - /* rendermode support */ - state.support = support; - /* set up the render mode */ - state.rendermode = rendermode = render_mode_create(rendermode_name, &state); + state.rendermode = rendermode = render_mode_create(modeobj, &state); if (rendermode == NULL) { return NULL; // note that render_mode_create will // set PyErr. No need to set it here diff --git a/overviewer_core/src/main.c b/overviewer_core/src/main.c index ca14065..11a12e2 100644 --- a/overviewer_core/src/main.c +++ b/overviewer_core/src/main.c @@ -17,10 +17,6 @@ #include "overviewer.h" -/* global variables from rendermodes.c -- both are dictionaries */ -extern PyObject *render_mode_options; -extern PyObject *custom_render_modes; - PyObject *get_extension_version(PyObject *self, PyObject *args) { return Py_BuildValue("i", OVERVIEWER_EXTENSION_VERSION); @@ -33,20 +29,6 @@ static PyMethodDef COverviewerMethods[] = { {"render_loop", chunk_render, METH_VARARGS, "Renders stuffs"}, - {"get_render_modes", get_render_modes, METH_VARARGS, - "returns available render modes"}, - {"get_render_mode_info", get_render_mode_info, METH_VARARGS, - "returns info for a particular render mode"}, - {"get_render_mode_inheritance", get_render_mode_inheritance, METH_VARARGS, - "returns inheritance chain for a particular render mode"}, - {"get_render_mode_children", get_render_mode_children, METH_VARARGS, - "returns (direct) children for a particular render mode"}, - - {"set_render_mode_options", set_render_mode_options, METH_VARARGS, - "sets the default options for a given render mode"}, - {"add_custom_render_mode", add_custom_render_mode, METH_VARARGS, - "add a new rendermode derived from an existing mode"}, - {"extension_version", get_extension_version, METH_VARARGS, "Returns the extension version"}, @@ -69,17 +51,5 @@ initc_overviewer(void) return; } - /* create the render mode data structures, and attatch them to the module - * so that the Python garbage collector doesn't freak out - */ - - render_mode_options = PyDict_New(); - PyObject_SetAttrString(mod, "_render_mode_options", render_mode_options); - Py_DECREF(render_mode_options); - - custom_render_modes = PyDict_New(); - PyObject_SetAttrString(mod, "_custom_render_modes", custom_render_modes); - Py_DECREF(custom_render_modes); - init_endian(); } diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 1155724..517bb5b 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -79,9 +79,6 @@ typedef struct { /* the Texture object */ PyObject *textures; - /* the rendermode support module (rendermodes.py) */ - PyObject *support; - /* the block position and type, and the block array */ int x, y, z; unsigned char block; diff --git a/overviewer_core/src/rendermode-normal.c b/overviewer_core/src/primitives/base.c similarity index 56% rename from overviewer_core/src/rendermode-normal.c rename to overviewer_core/src/primitives/base.c index 39bc679..4f13c47 100644 --- a/overviewer_core/src/rendermode-normal.c +++ b/overviewer_core/src/primitives/base.c @@ -15,39 +15,22 @@ * with the Overviewer. If not, see . */ -#include "overviewer.h" +#include "../overviewer.h" + +typedef struct { + /* coordinates of the chunk, inside its region file */ + int chunk_x, chunk_y; + /* biome data for the region */ + PyObject *biome_data; + /* grasscolor and foliagecolor lookup tables */ + PyObject *grasscolor, *foliagecolor, *watercolor; + /* biome-compatible grass/leaf textures */ + PyObject *grass_texture; +} PrimitiveBase; static int -rendermode_normal_start(void *data, RenderState *state, PyObject *options) { - RenderModeNormal *self = (RenderModeNormal *)data; - - /* load up the given options, first */ - - self->edge_opacity = 0.15; - if (!render_mode_parse_option(options, "edge_opacity", "f", &(self->edge_opacity))) - return 1; - - self->min_depth = 0; - if (!render_mode_parse_option(options, "min_depth", "I", &(self->min_depth))) - return 1; - - self->max_depth = 127; - if (!render_mode_parse_option(options, "max_depth", "I", &(self->max_depth))) - return 1; - - self->height_fading = 0; - /* XXX skip height fading */ - /*if (!render_mode_parse_option(options, "height_fading", "i", &(self->height_fading))) - return 1;*/ - - self->nether = 0; - if (!render_mode_parse_option(options, "nether", "i", &(self->nether))) - return 1; - - /*if (self->height_fading) { - self->black_color = PyObject_GetAttrString(state->chunk, "black_color"); - self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - }*/ +base_start(void *data, RenderState *state, PyObject *support) { + PrimitiveBase *self = (PrimitiveBase *)data; /* biome-compliant grass mask (includes sides!) */ self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); @@ -95,20 +78,18 @@ rendermode_normal_start(void *data, RenderState *state, PyObject *options) { } static void -rendermode_normal_finish(void *data, RenderState *state) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_finish(void *data, RenderState *state) { + PrimitiveBase *self = (PrimitiveBase *)data; Py_XDECREF(self->biome_data); Py_XDECREF(self->foliagecolor); Py_XDECREF(self->grasscolor); Py_XDECREF(self->watercolor); Py_XDECREF(self->grass_texture); - Py_XDECREF(self->black_color); - Py_XDECREF(self->white_color); } static int -rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) { +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) && @@ -123,39 +104,15 @@ rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) } static int -rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_hidden(void *data, RenderState *state, int x, int y, int z) { + PrimitiveBase *self = (PrimitiveBase *)data; - if (z > self->max_depth || z < self->min_depth) { - return 1; - } - - if (self->nether) - { - - /* hide all blocks above all air blocks */ - int below_air = 0; - - while (z < 128) - { - if (getArrayByte3D(state->blocks, x, y, z) == 0) - { - below_air = 1; - break; - } - z++; - } - - if (!below_air) - return 1; - } - return 0; } static void -rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveBase *self = (PrimitiveBase *)data; /* draw the block! */ alpha_over(state->img, src, mask, state->imgx, state->imgy, 0, 0); @@ -299,85 +256,13 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * if (facemask) tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0); } - - if (self->height_fading) { - /* do some height fading */ - PyObject *height_color = self->white_color; - /* negative alpha => darkness, positive => light */ - float alpha = (1.0 / (1 + expf((70 - state->z) / 11.0))) * 0.6 - 0.55; - - if (alpha < 0.0) { - alpha *= -1; - height_color = self->black_color; - } - - alpha_over_full(state->img, height_color, mask_light, alpha, state->imgx, state->imgy, 0, 0); - } - - - /* Draw some edge lines! */ - // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) - if (state->block == 44 || state->block == 78 || !is_transparent(state->block)) { - Imaging img_i = imaging_python_to_c(state->img); - unsigned char ink[] = {0, 0, 0, 255 * self->edge_opacity}; - - int increment=0; - if (state->block == 44) // half-step - increment=6; - else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) - increment=9; - - if ((state->x == 15) && (state->up_right_blocks != Py_None)) { - unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); - } - } else if (state->x != 15) { - unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); - } - } - // if y != 0 and blocks[x,y-1,z] == 0 - - // chunk boundries are annoying - if ((state->y == 0) && (state->up_left_blocks != Py_None)) { - unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); - } - } else if (state->y != 0) { - unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); - if (side_block != state->block && is_transparent(side_block)) { - // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); - } - } - } } -const RenderModeOption rendermode_normal_options[] = { - {"edge_opacity", "darkness of the edge lines, from 0.0 to 1.0 (default: 0.15)"}, - {"min_depth", "lowest level of blocks to render (default: 0)"}, - {"max_depth", "highest level of blocks to render (default: 127)"}, - {"height_fading", "darken or lighten blocks based on height (default: False)"}, - {"nether", "if True, remove the roof of the map. Useful on nether maps. (default: False)"}, - {NULL, NULL} -}; - -RenderModeInterface rendermode_normal = { - "normal", "Normal", - "nothing special, just render the blocks", - rendermode_normal_options, - NULL, - sizeof(RenderModeNormal), - rendermode_normal_start, - rendermode_normal_finish, - rendermode_normal_occluded, - rendermode_normal_hidden, - rendermode_normal_draw, +RenderPrimitiveInterface primitive_base = { + "base", sizeof(PrimitiveBase), + base_start, + base_finish, + base_occluded, + base_hidden, + base_draw, }; diff --git a/overviewer_core/src/rendermode-cave.c b/overviewer_core/src/primitives/cave.c similarity index 64% rename from overviewer_core/src/rendermode-cave.c rename to overviewer_core/src/primitives/cave.c index 9807ed2..f4d6605 100644 --- a/overviewer_core/src/rendermode-cave.c +++ b/overviewer_core/src/primitives/cave.c @@ -15,15 +15,32 @@ * with the Overviewer. If not, see . */ -#include "overviewer.h" +#include "../overviewer.h" #include +typedef struct { + /* data used to know where the surface is */ + PyObject *skylight; + PyObject *left_skylight; + PyObject *right_skylight; + PyObject *up_left_skylight; + PyObject *up_right_skylight; + + /* data used to know where the lit caves are */ + PyObject *blocklight; + PyObject *left_blocklight; + PyObject *right_blocklight; + PyObject *up_left_blocklight; + PyObject *up_right_blocklight; + + int only_lit; +} RenderPrimitiveCave; + static inline int touches_light(unsigned int x, unsigned int y, unsigned int z, PyObject *light, PyObject *left_light, PyObject *right_light, PyObject *up_left_light, PyObject *up_right_light) { - if (getArrayByte3D(light, x, y, z+1) != 0) { return 1; } @@ -79,10 +96,11 @@ touches_light(unsigned int x, unsigned int y, unsigned int z, return 0; } -static inline int -rendermode_cave_adjacent_occluded(void *data, RenderState *state, int x, int y, int z) { - /* check for occlusion of edge blocks, using adjacent block data */ - +static int +cave_occluded(void *data, RenderState *state, int x, int y, int z) { + /* check for normal occlusion */ + /* use ajacent chunks, if not you get blocks spreaded in chunk edges */ + if (z != 127) { if ( (x == 0) && (y != 15) ) { if (state->left_blocks != Py_None) { @@ -133,26 +151,11 @@ rendermode_cave_adjacent_occluded(void *data, RenderState *state, int x, int y, } static int -rendermode_cave_occluded(void *data, RenderState *state, int x, int y, int z) { - /* first, check to see if it's "normally" occluded */ - if (rendermode_lighting.occluded(data, state, x, y, z)) - return 1; - - /* check for normal occlusion */ - /* use ajacent chunks, if not you get blocks spreaded in chunk edges */ - return rendermode_cave_adjacent_occluded(data, state, x, y, z); -} - -static int -rendermode_cave_hidden(void *data, RenderState *state, int x, int y, int z) { - RenderModeCave* self; +cave_hidden(void *data, RenderState *state, int x, int y, int z) { + RenderPrimitiveCave* self; int dz = 0; - self = (RenderModeCave *)data; + self = (RenderPrimitiveCave *)data; - /* first, check to see if it's "normally" hidden */ - if (rendermode_lighting.hidden(data, state, x, y, z)) - return 1; - /* check if the block is touching skylight */ if (z != 127) { @@ -190,51 +193,25 @@ rendermode_cave_hidden(void *data, RenderState *state, int x, int y, int z) { /* unfortunate side-effect of lit cave mode: we need to count occluded * blocks as hidden for the lighting to look right, since technically our * hiding depends on occlusion as well - * - * We leave out this check otherwise because it's fairly expensive. */ - if (self->lighting) { - if ( (x != 0) && (y != 15) && (z != 127) && - !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 rendermode_cave_adjacent_occluded(data, state, x, y, z); + if ( (x != 0) && (y != 15) && (z != 127) && + !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; + return cave_occluded(data, state, x, y, z); } static int -rendermode_cave_start(void *data, RenderState *state, PyObject *options) { - RenderModeCave* self; +cave_start(void *data, RenderState *state, PyObject *support) { + RenderPrimitiveCave* self; int ret; - self = (RenderModeCave *)data; + self = (RenderPrimitiveCave *)data; - /* first, chain up */ - ret = rendermode_lighting.start(data, state, options); - if (ret != 0) - return ret; - - self->depth_tinting = 1; - if (!render_mode_parse_option(options, "depth_tinting", "i", &(self->depth_tinting))) + if (!render_mode_parse_option(support, "only_lit", "i", &(self->only_lit))) return 1; - - self->only_lit = 0; - if (!render_mode_parse_option(options, "only_lit", "i", &(self->only_lit))) - return 1; - - self->lighting = 0; - if (!render_mode_parse_option(options, "lighting", "i", &(self->lighting))) - return 1; - - if (self->lighting) - { - /* we can't skip lighting the sides in cave mode, it looks too weird */ - self->parent.skip_sides = 0; - } /* if there's skylight we are in the surface! */ self->skylight = get_chunk_data(state, CURRENT, SKYLIGHT); @@ -251,16 +228,13 @@ rendermode_cave_start(void *data, RenderState *state, PyObject *options) { self->up_right_blocklight = get_chunk_data(state, UP_RIGHT, BLOCKLIGHT); } - /* colors for tinting */ - self->depth_colors = PyObject_GetAttrString(state->support, "depth_colors"); - return 0; } static void -rendermode_cave_finish(void *data, RenderState *state) { - RenderModeCave* self; - self = (RenderModeCave *)data; +cave_finish(void *data, RenderState *state) { + RenderPrimitiveCave* self; + self = (RenderPrimitiveCave *)data; Py_DECREF(self->skylight); Py_DECREF(self->left_skylight); @@ -275,55 +249,13 @@ rendermode_cave_finish(void *data, RenderState *state) { Py_DECREF(self->up_left_blocklight); Py_DECREF(self->up_right_blocklight); } - - Py_DECREF(self->depth_colors); - - rendermode_lighting.finish(data, state); } -static void -rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { - RenderModeCave* self; - int z, r, g, b; - self = (RenderModeCave *)data; - - z = state->z; - r = 0, g = 0, b = 0; - - /* draw the normal block */ - if (self->lighting) { - rendermode_lighting.draw(data, state, src, mask, mask_light); - } else { - rendermode_normal.draw(data, state, src, mask, mask_light); - } - - if (self->depth_tinting) { - /* get the colors and tint and tint */ - r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + z*3)); - g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + z*3)); - b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + z*3)); - - tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); - } - -} - -const RenderModeOption rendermode_cave_options[] = { - {"depth_tinting", "tint caves based on how deep they are (default: True)"}, - {"only_lit", "only render lit caves (default: False)"}, - {"lighting", "render caves with lighting enabled (default: False)"}, - {NULL, NULL} -}; - -RenderModeInterface rendermode_cave = { - "cave", "Cave", - "render only caves", - rendermode_cave_options, - &rendermode_lighting, - sizeof(RenderModeCave), - rendermode_cave_start, - rendermode_cave_finish, - rendermode_cave_occluded, - rendermode_cave_hidden, - rendermode_cave_draw, +RenderPrimitiveInterface primitive_cave = { + "cave", sizeof(RenderPrimitiveCave), + cave_start, + cave_finish, + cave_occluded, + cave_hidden, + NULL, }; diff --git a/overviewer_core/src/primitives/depth-tinting.c b/overviewer_core/src/primitives/depth-tinting.c new file mode 100644 index 0000000..80d5226 --- /dev/null +++ b/overviewer_core/src/primitives/depth-tinting.c @@ -0,0 +1,70 @@ +/* + * 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 . + */ + +#include "../overviewer.h" +#include + +typedef struct { + /* list of colors used for tinting */ + PyObject *depth_colors; +} RenderPrimitiveDepthTinting; + +static int +depth_tinting_start(void *data, RenderState *state, PyObject *support) { + RenderPrimitiveDepthTinting* self; + self = (RenderPrimitiveDepthTinting *)data; + + self->depth_colors = PyObject_GetAttrString(support, "depth_colors"); + if (self->depth_colors == NULL) + return 1; + + return 0; +} + +static void +depth_tinting_finish(void *data, RenderState *state) { + RenderPrimitiveDepthTinting* self; + self = (RenderPrimitiveDepthTinting *)data; + + Py_DECREF(self->depth_colors); +} + +static void +depth_tinting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + RenderPrimitiveDepthTinting* self; + int z, r, g, b; + self = (RenderPrimitiveDepthTinting *)data; + + z = state->z; + r = 0, g = 0, b = 0; + + /* get the colors and tint and tint */ + r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + z*3)); + g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + z*3)); + b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + z*3)); + + tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); +} + +RenderPrimitiveInterface primitive_depth_tinting = { + "depth-tinting", sizeof(RenderPrimitiveDepthTinting), + depth_tinting_start, + depth_tinting_finish, + NULL, + NULL, + depth_tinting_draw, +}; diff --git a/overviewer_core/src/primitives/depth.c b/overviewer_core/src/primitives/depth.c new file mode 100644 index 0000000..87a14a6 --- /dev/null +++ b/overviewer_core/src/primitives/depth.c @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +#include "../overviewer.h" + +typedef struct { + unsigned int min; + unsigned int max; +} PrimitiveDepth; + +static int +depth_start(void *data, RenderState *state, PyObject *support) { + PrimitiveDepth *self = (PrimitiveDepth *)data; + + if (!render_mode_parse_option(support, "min", "I", &(self->min))) + return 1; + if (!render_mode_parse_option(support, "max", "I", &(self->max))) + return 1; + + return 0; +} + +static int +depth_hidden(void *data, RenderState *state, int x, int y, int z) { + PrimitiveDepth *self = (PrimitiveDepth *)data; + if (z > self->max || z < self->min) { + return 1; + } + return 0; +} + +RenderPrimitiveInterface primitive_depth = { + "depth", sizeof(PrimitiveDepth), + depth_start, + NULL, + NULL, + depth_hidden, + NULL, +}; diff --git a/overviewer_core/src/primitives/edge-lines.c b/overviewer_core/src/primitives/edge-lines.c new file mode 100644 index 0000000..f1e4e42 --- /dev/null +++ b/overviewer_core/src/primitives/edge-lines.c @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +#include "../overviewer.h" + +typedef struct { + float opacity; +} PrimitiveEdgeLines; + +static int +edge_lines_start(void *data, RenderState *state, PyObject *support) { + PrimitiveEdgeLines *self = (PrimitiveEdgeLines *)data; + if (!render_mode_parse_option(support, "opacity", "f", &(self->opacity))) + return 1; + return 0; +} + +static void +edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveEdgeLines *self = (PrimitiveEdgeLines *)data; + + /* Draw some edge lines! */ + // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) + if (state->block == 44 || state->block == 78 || !is_transparent(state->block)) { + Imaging img_i = imaging_python_to_c(state->img); + unsigned char ink[] = {0, 0, 0, 255 * self->opacity}; + + int increment=0; + if (state->block == 44) // half-step + increment=6; + else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) + increment=9; + + if ((state->x == 15) && (state->up_right_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + } else if (state->x != 15) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + } + // if y != 0 and blocks[x,y-1,z] == 0 + + // chunk boundries are annoying + if ((state->y == 0) && (state->up_left_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } else if (state->y != 0) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); + if (side_block != state->block && is_transparent(side_block)) { + // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } + } +} + +RenderPrimitiveInterface primitive_edge_lines = { + "edge-lines", sizeof(PrimitiveEdgeLines), + edge_lines_start, + NULL, + NULL, + NULL, + edge_lines_draw, +}; diff --git a/overviewer_core/src/primitives/height-fading.c b/overviewer_core/src/primitives/height-fading.c new file mode 100644 index 0000000..589111e --- /dev/null +++ b/overviewer_core/src/primitives/height-fading.c @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +#include "../overviewer.h" + +typedef struct { + PyObject *black_color; + PyObject *white_color; +} PrimitiveHeightFading; + +static int +height_fading_start(void *data, RenderState *state, PyObject *support) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + self->black_color = PyObject_GetAttrString(support, "black_color"); + self->white_color = PyObject_GetAttrString(support, "white_color"); + + return 0; +} + +static void +height_fading_finish(void *data, RenderState *state) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + Py_DECREF(self->black_color); + Py_DECREF(self->white_color); +} + +static void +height_fading_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + /* do some height fading */ + PyObject *height_color = self->white_color; + + /* negative alpha => darkness, positive => light */ + float alpha = (1.0 / (1 + expf((70 - state->z) / 11.0))) * 0.6 - 0.55; + + if (alpha < 0.0) { + alpha *= -1; + height_color = self->black_color; + } + + alpha_over_full(state->img, height_color, mask_light, alpha, state->imgx, state->imgy, 0, 0); +} + +RenderPrimitiveInterface primitive_height_fading = { + "height-fading", sizeof(PrimitiveHeightFading), + height_fading_start, + height_fading_finish, + NULL, + NULL, + height_fading_draw, +}; diff --git a/overviewer_core/src/rendermode-lighting.c b/overviewer_core/src/primitives/lighting.c similarity index 83% rename from overviewer_core/src/rendermode-lighting.c rename to overviewer_core/src/primitives/lighting.c index a6eca00..1eea2be 100644 --- a/overviewer_core/src/rendermode-lighting.c +++ b/overviewer_core/src/primitives/lighting.c @@ -15,7 +15,8 @@ * with the Overviewer. If not, see . */ -#include "overviewer.h" +#include "../overviewer.h" +#include "lighting.h" #include /* figures out the color from a given skylight and blocklight, @@ -35,7 +36,7 @@ static void calculate_light_color_fancy(void *data, unsigned char skylight, unsigned char blocklight, unsigned char *r, unsigned char *g, unsigned char *b) { - RenderModeLighting *mode = (RenderModeLighting *)(data); + RenderPrimitiveLighting *mode = (RenderPrimitiveLighting *)(data); unsigned int index; PyObject *color; @@ -70,7 +71,7 @@ static void calculate_light_color_fancy_night(void *data, unsigned char skylight, unsigned char blocklight, unsigned char *r, unsigned char *g, unsigned char *b) { - RenderModeLighting *mode = (RenderModeLighting *)(data); + RenderPrimitiveLighting *mode = (RenderPrimitiveLighting *)(data); unsigned int index; PyObject *color; @@ -95,7 +96,7 @@ calculate_light_color_fancy_night(void *data, */ inline unsigned char -estimate_blocklevel(RenderModeLighting *self, RenderState *state, +estimate_blocklevel(RenderPrimitiveLighting *self, RenderState *state, int x, int y, int z, int *authoratative) { /* placeholders for later data arrays, coordinates */ @@ -185,7 +186,7 @@ estimate_blocklevel(RenderModeLighting *self, RenderState *state, } inline void -get_lighting_color(RenderModeLighting *self, RenderState *state, +get_lighting_color(RenderPrimitiveLighting *self, RenderState *state, int x, int y, int z, unsigned char *r, unsigned char *g, unsigned char *b) { @@ -273,7 +274,10 @@ get_lighting_color(RenderModeLighting *self, RenderState *state, if (block == 10 || block == 11) { /* lava blocks should always be lit! */ - return 0.0f; + *r = 255; + *g = 255; + *b = 255; + return; } self->calculate_light_color(self, MIN(skylevel, 15), MIN(blocklevel, 15), r, g, b); @@ -281,7 +285,7 @@ get_lighting_color(RenderModeLighting *self, RenderState *state, /* does per-face occlusion checking for do_shading_with_mask */ inline int -rendermode_lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int z) { +lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int z) { /* first, check for occlusion if the block is in the local chunk */ if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) { unsigned char block = getArrayByte3D(state->blocks, x, y, z); @@ -315,52 +319,41 @@ rendermode_lighting_is_face_occluded(RenderState *state, int skip_sides, int x, /* shades the drawn block with the given facemask, based on the lighting results from (x, y, z) */ static inline void -do_shading_with_mask(RenderModeLighting *self, RenderState *state, +do_shading_with_mask(RenderPrimitiveLighting *self, RenderState *state, int x, int y, int z, PyObject *mask) { unsigned char r, g, b; - float comp_shade_strength; + float comp_strength; /* check occlusion */ - if (rendermode_lighting_is_face_occluded(state, self->skip_sides, x, y, z)) + if (lighting_is_face_occluded(state, self->skip_sides, x, y, z)) return; get_lighting_color(self, state, x, y, z, &r, &g, &b); - comp_shade_strength = 1.0 - self->shade_strength; + comp_strength = 1.0 - self->strength; - r += (255 - r) * comp_shade_strength; - g += (255 - g) * comp_shade_strength; - b += (255 - b) * comp_shade_strength; + r += (255 - r) * comp_strength; + g += (255 - g) * comp_strength; + b += (255 - b) * comp_strength; tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); } static int -rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { - RenderModeLighting* self; - - /* first, chain up */ - int ret = rendermode_normal.start(data, state, options); - if (ret != 0) - return ret; +lighting_start(void *data, RenderState *state, PyObject *support) { + RenderPrimitiveLighting* self; + self = (RenderPrimitiveLighting *)data; - self = (RenderModeLighting *)data; - - /* skip sides by default */ - self->skip_sides = 1; + /* don't skip sides by default */ + self->skip_sides = 0; - self->shade_strength = 1.0; - if (!render_mode_parse_option(options, "shade_strength", "f", &(self->shade_strength))) + if (!render_mode_parse_option(support, "strength", "f", &(self->strength))) return 1; - - self->night = 0; - if (!render_mode_parse_option(options, "night", "i", &(self->night))) + if (!render_mode_parse_option(support, "night", "i", &(self->night))) return 1; - - self->color_light = 0; - if (!render_mode_parse_option(options, "color_light", "i", &(self->color_light))) + if (!render_mode_parse_option(support, "color", "i", &(self->color))) return 1; - self->facemasks_py = PyObject_GetAttrString(state->support, "facemasks"); + self->facemasks_py = PyObject_GetAttrString(support, "facemasks"); // borrowed references, don't need to be decref'd self->facemasks[0] = PyTuple_GetItem(self->facemasks_py, 0); self->facemasks[1] = PyTuple_GetItem(self->facemasks_py, 1); @@ -383,12 +376,12 @@ rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { self->calculate_light_color = calculate_light_color; } - if (self->color_light) { - self->lightcolor = PyObject_CallMethod(state->textures, "loadLightColor", ""); + if (self->color) { + self->lightcolor = PyObject_CallMethod(state->textures, "load_light_color", ""); if (self->lightcolor == Py_None) { Py_DECREF(self->lightcolor); self->lightcolor = NULL; - self->color_light = 0; + self->color = 0; } else { if (self->night) { self->calculate_light_color = calculate_light_color_fancy_night; @@ -404,8 +397,8 @@ rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { } static void -rendermode_lighting_finish(void *data, RenderState *state) { - RenderModeLighting *self = (RenderModeLighting *)data; +lighting_finish(void *data, RenderState *state) { + RenderPrimitiveLighting *self = (RenderPrimitiveLighting *)data; Py_DECREF(self->facemasks_py); @@ -419,32 +412,14 @@ rendermode_lighting_finish(void *data, RenderState *state) { Py_DECREF(self->up_left_blocklight); Py_DECREF(self->up_right_skylight); Py_DECREF(self->up_right_blocklight); - - /* now chain up */ - rendermode_normal.finish(data, state); -} - -static int -rendermode_lighting_occluded(void *data, RenderState *state, int x, int y, int z) { - /* no special occlusion here */ - return rendermode_normal.occluded(data, state, x, y, z); -} - -static int -rendermode_lighting_hidden(void *data, RenderState *state, int x, int y, int z) { - /* no special hiding here */ - return rendermode_normal.hidden(data, state, x, y, z); } static void -rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { - RenderModeLighting* self; +lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + RenderPrimitiveLighting* self; int x, y, z; - /* first, chain up */ - rendermode_normal.draw(data, state, src, mask, mask_light); - - self = (RenderModeLighting *)data; + self = (RenderPrimitiveLighting *)data; x = state->x, y = state->y, z = state->z; if ((state->block == 9) || (state->block == 79)) { /* special case for water and ice */ @@ -473,22 +448,11 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject } } -const RenderModeOption rendermode_lighting_options[] = { - {"shade_strength", "how dark to make the shadows, from 0.0 to 1.0 (default: 1.0)"}, - {"night", "whether to use nighttime skylight settings (default: False)"}, - {"color_light", "whether to use colored light (default: False)"}, - {NULL, NULL} -}; - -RenderModeInterface rendermode_lighting = { - "lighting", "Lighting", - "draw shadows from the lighting data", - rendermode_lighting_options, - &rendermode_normal, - sizeof(RenderModeLighting), - rendermode_lighting_start, - rendermode_lighting_finish, - rendermode_lighting_occluded, - rendermode_lighting_hidden, - rendermode_lighting_draw, +RenderPrimitiveInterface primitive_lighting = { + "lighting", sizeof(RenderPrimitiveLighting), + lighting_start, + lighting_finish, + NULL, + NULL, + lighting_draw, }; diff --git a/overviewer_core/src/primitives/lighting.h b/overviewer_core/src/primitives/lighting.h new file mode 100644 index 0000000..0742167 --- /dev/null +++ b/overviewer_core/src/primitives/lighting.h @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +#include "../overviewer.h" + +typedef struct { + PyObject *facemasks_py; + PyObject *facemasks[3]; + + /* extra data, loaded off the chunk class */ + PyObject *skylight, *blocklight; + PyObject *left_skylight, *left_blocklight; + PyObject *right_skylight, *right_blocklight; + PyObject *up_left_skylight, *up_left_blocklight; + PyObject *up_right_skylight, *up_right_blocklight; + + /* light color image, loaded if color_light is True */ + PyObject *lightcolor; + + /* can be overridden in derived rendermodes to control lighting + arguments are data, skylight, blocklight, return RGB */ + void (*calculate_light_color)(void *, unsigned char, unsigned char, unsigned char *, unsigned char *, unsigned char *); + + /* can be set to 0 in derived modes to indicate that lighting the chunk + * sides is actually important. Right now, this is used in cave mode + */ + int skip_sides; + + float strength; + int color; + int night; +} RenderPrimitiveLighting; + +/* exposed so that smooth-lighting can use them */ +extern RenderPrimitiveInterface primitive_lighting; +int lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int z); +void get_lighting_color(RenderPrimitiveLighting *self, RenderState *state, + int x, int y, int z, + unsigned char *r, unsigned char *g, unsigned char *b); diff --git a/overviewer_core/src/primitives/nether.c b/overviewer_core/src/primitives/nether.c new file mode 100644 index 0000000..3844d1c --- /dev/null +++ b/overviewer_core/src/primitives/nether.c @@ -0,0 +1,42 @@ +/* + * 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 . + */ + +#include "../overviewer.h" + +static int +nether_hidden(void *data, RenderState *state, int x, int y, int z) { + /* hide all blocks above all air blocks */ + while (z < 128) + { + if (getArrayByte3D(state->blocks, x, y, z) == 0) + { + return 0; + break; + } + z++; + } + return 1; +} + +RenderPrimitiveInterface primitive_nether = { + "nether", 0, + NULL, + NULL, + NULL, + nether_hidden, + NULL, +}; diff --git a/overviewer_core/src/rendermode-smooth-lighting.c b/overviewer_core/src/primitives/smooth-lighting.c similarity index 79% rename from overviewer_core/src/rendermode-smooth-lighting.c rename to overviewer_core/src/primitives/smooth-lighting.c index 28767ea..6036a52 100644 --- a/overviewer_core/src/rendermode-smooth-lighting.c +++ b/overviewer_core/src/primitives/smooth-lighting.c @@ -15,9 +15,15 @@ * with the Overviewer. If not, see . */ -#include "overviewer.h" +#include "../overviewer.h" +#include "lighting.h" #include +typedef struct { + /* inherits from lighting */ + RenderPrimitiveLighting parent; +} RenderPrimitiveSmoothLighting; + /* structure representing one corner of a face (see below) */ struct SmoothLightingCorner { /* where this corner shows up on each block texture */ @@ -123,12 +129,12 @@ enum }; static void -do_shading_with_rule(RenderModeSmoothLighting *self, RenderState *state, struct SmoothLightingFace face) { +do_shading_with_rule(RenderPrimitiveSmoothLighting *self, RenderState *state, struct SmoothLightingFace face) { int i; - RenderModeLighting *lighting = (RenderModeLighting *)self; + RenderPrimitiveLighting *lighting = (RenderPrimitiveLighting *)self; int x = state->imgx, y = state->imgy; struct SmoothLightingCorner *pts = face.corners; - float comp_shade_strength = 1.0 - lighting->shade_strength; + float comp_shade_strength = 1.0 - lighting->strength; unsigned char pts_r[4] = {0, 0, 0, 0}; unsigned char pts_g[4] = {0, 0, 0, 0}; unsigned char pts_b[4] = {0, 0, 0, 0}; @@ -137,7 +143,7 @@ do_shading_with_rule(RenderModeSmoothLighting *self, RenderState *state, struct int cz = state->z + face.dz; /* first, check for occlusion if the block is in the local chunk */ - if (rendermode_lighting_is_face_occluded(state, 0, cx, cy, cz)) + if (lighting_is_face_occluded(state, 0, cx, cy, cz)) return; /* calculate the lighting colors for each point */ @@ -189,55 +195,38 @@ do_shading_with_rule(RenderModeSmoothLighting *self, RenderState *state, struct } static int -rendermode_smooth_lighting_start(void *data, RenderState *state, PyObject *options) { +smooth_lighting_start(void *data, RenderState *state, PyObject *support) { /* first, chain up */ - int ret = rendermode_lighting.start(data, state, options); + int ret = primitive_lighting.start(data, state, support); if (ret != 0) - return ret; - + return ret; return 0; } static void -rendermode_smooth_lighting_finish(void *data, RenderState *state) { +smooth_lighting_finish(void *data, RenderState *state) { /* nothing special to do */ - rendermode_lighting.finish(data, state); -} - -static int -rendermode_smooth_lighting_occluded(void *data, RenderState *state, int x, int y, int z) { - /* no special occlusion here */ - return rendermode_lighting.occluded(data, state, x, y, z); -} - -static int -rendermode_smooth_lighting_hidden(void *data, RenderState *state, int x, int y, int z) { - /* no special hiding here */ - return rendermode_lighting.hidden(data, state, x, y, z); + primitive_lighting.finish(data, state); } static void -rendermode_smooth_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { +smooth_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { int light_top = 1; int light_left = 1; int light_right = 1; - RenderModeSmoothLighting *self = (RenderModeSmoothLighting *)data; + RenderPrimitiveSmoothLighting *self = (RenderPrimitiveSmoothLighting *)data; /* special case for leaves, water 8, water 9 -- these are also smooth-lit! */ if (state->block != 18 && state->block != 8 && state->block != 9 && is_transparent(state->block)) { /* transparent blocks are rendered as usual, with flat lighting */ - rendermode_lighting.draw(data, state, src, mask, mask_light); + primitive_lighting.draw(data, state, src, mask, mask_light); return; } /* non-transparent blocks get the special smooth treatment */ - /* nothing special to do, but we do want to avoid vanilla - * lighting mode draws */ - rendermode_normal.draw(data, state, src, mask, mask_light); - /* special code for water */ if (state->block == 9) { @@ -257,15 +246,11 @@ rendermode_smooth_lighting_draw(void *data, RenderState *state, PyObject *src, P do_shading_with_rule(self, state, lighting_rules[FACE_RIGHT]); } -RenderModeInterface rendermode_smooth_lighting = { - "smooth-lighting", "Smooth Lighting", - "like \"lighting\", except smooth", +RenderPrimitiveInterface primitive_smooth_lighting = { + "smooth-lighting", sizeof(RenderPrimitiveSmoothLighting), + smooth_lighting_start, + smooth_lighting_finish, NULL, - &rendermode_lighting, - sizeof(RenderModeSmoothLighting), - rendermode_smooth_lighting_start, - rendermode_smooth_lighting_finish, - rendermode_smooth_lighting_occluded, - rendermode_smooth_lighting_hidden, - rendermode_smooth_lighting_draw, + NULL, + smooth_lighting_draw, }; diff --git a/overviewer_core/src/rendermodes.c b/overviewer_core/src/rendermodes.c index 545aad2..6ff99de 100644 --- a/overviewer_core/src/rendermodes.c +++ b/overviewer_core/src/rendermodes.c @@ -19,175 +19,174 @@ #include #include -/* list of all render modes, ending in NULL - all of these will be available to the user, so DON'T include modes - that are only useful as a base for other modes. */ -static RenderModeInterface *render_modes[] = { - &rendermode_normal, - &rendermode_lighting, - &rendermode_smooth_lighting, - &rendermode_cave, - - &rendermode_spawn, - &rendermode_mineral, - NULL -}; - -PyObject *render_mode_options = NULL; -PyObject *custom_render_modes = NULL; +/* this file defines render_primitives, + a list of all render primitives, ending in NULL + all of these will be available to the user, so DON'T include primitives + that are only useful as a base for other primitives. + + this file is auto-generated by setup.py */ +#include "primitives.h" /* rendermode encapsulation */ -/* helper to recursively find options for a given mode */ -static inline PyObject * -render_mode_create_options(const char *mode) { - const char *parent = NULL; - PyObject *base_options, *ret, *parent_options; - unsigned int i, found_concrete; +/* helper to create a single primitive */ +RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) { + RenderPrimitive *ret = NULL; + RenderPrimitiveInterface *iface = NULL; + unsigned int i; + PyObject *pyname; + const char* name; + + pyname = PyObject_GetAttrString(prim, "name"); + if (!pyname) + return NULL; + name = PyString_AsString(pyname); - base_options = PyDict_GetItemString(render_mode_options, mode); - if (base_options) { - ret = PyDict_Copy(base_options); - } else { - ret = PyDict_New(); - } - - /* figure out the parent mode name */ - found_concrete = 0; - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, mode) == 0) { - found_concrete = 1; - if (render_modes[i]->parent) { - parent = render_modes[i]->parent->name; - } + for (i = 0; render_primitives[i] != NULL; i++) { + if (strcmp(render_primitives[i]->name, name) == 0) { + iface = render_primitives[i]; break; } } + Py_DECREF(pyname); + + if (iface == NULL) + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "invalid primitive name: %s", name); - /* check custom mode info if needed */ - if (found_concrete == 0) { - PyObject *custom = PyDict_GetItemString(custom_render_modes, mode); - if (custom) { - custom = PyDict_GetItemString(custom, "parent"); - if (custom) { - parent = PyString_AsString(custom); - } + ret = calloc(1, sizeof(RenderPrimitive)); + if (ret == NULL) { + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc a render primitive"); + } + + if (iface->data_size > 0) { + ret->primitive = calloc(1, iface->data_size); + if (ret->primitive == NULL) { + free(ret); + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc render primitive data"); } } - /* merge parent options, if the parent was found */ - if (parent) { - parent_options = render_mode_create_options(parent); - if (parent_options) { - if (PyDict_Merge(ret, parent_options, 0) == -1) { - Py_DECREF(ret); - Py_DECREF(parent_options); - return NULL; - } - Py_DECREF(parent_options); + ret->iface = iface; + + if (iface->start) { + if (iface->start(ret->primitive, state, prim)) { + free(ret->primitive); + free(ret); + return NULL; } } return ret; } -/* helper to find the first concrete, C interface for a given mode */ -inline static RenderModeInterface * -render_mode_find_interface(const char *mode) { - PyObject *custom; - const char *custom_parent; +RenderMode *render_mode_create(PyObject *mode, RenderState *state) { + RenderMode *ret = NULL; + PyObject *mode_fast = NULL; unsigned int i; - /* if it is *itself* concrete, we're done */ - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, mode) == 0) - return render_modes[i]; - } - - /* check for custom modes */ - custom = PyDict_GetItemString(custom_render_modes, mode); - if (custom == NULL) - return PyErr_Format(PyExc_RuntimeError, "Failed to find rendermode interface (custom not found)"); - custom = PyDict_GetItemString(custom, "parent"); - if (custom == NULL) - return PyErr_Format(PyExc_RuntimeError, "Failed to find rendermode interface (parent not found)"); - custom_parent = PyString_AsString(custom); - if (custom_parent == NULL) - return NULL; // PyString_AsString sets an exception - - return render_mode_find_interface(custom_parent); -} + mode_fast = PySequence_Fast(mode, "Mode is not a sequence type"); + if (!mode_fast) + return NULL; -RenderMode *render_mode_create(const char *mode, RenderState *state) { - PyObject *options; - RenderMode *ret = NULL; - RenderModeInterface *iface = NULL; - - iface = render_mode_find_interface(mode); - if (iface == NULL) - return NULL; - - options = render_mode_create_options(mode); - if (options == NULL) - return NULL; - ret = calloc(1, sizeof(RenderMode)); - if (ret == NULL) { - Py_DECREF(options); - return PyErr_Format(PyExc_RuntimeError, "Failed to alloc a rendermode"); - } - - ret->mode = calloc(1, iface->data_size); - if (ret->mode == NULL) { - Py_DECREF(options); - free(ret); - return PyErr_Format(PyExc_RuntimeError, "Failed to alloc rendermode data"); - } - - ret->iface = iface; ret->state = state; - - if (iface->start(ret->mode, state, options)) { - Py_DECREF(options); - free(ret->mode); - free(ret); - return NULL; + ret->num_primitives = PySequence_Length(mode); + ret->primitives = calloc(ret->num_primitives, sizeof(RenderPrimitive*)); + for (i = 0; i < ret->num_primitives; i++) { + PyObject *pyprim = PySequence_Fast_GET_ITEM(mode_fast, i); + RenderPrimitive *prim = render_primitive_create(pyprim, state); + + if (!prim) { + render_mode_destroy(ret); + Py_DECREF(mode_fast); + return NULL; + } + + ret->primitives[i] = prim; } - Py_DECREF(options); return ret; } void render_mode_destroy(RenderMode *self) { - self->iface->finish(self->mode, self->state); - free(self->mode); + unsigned int i; + + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + /* we may be destroying a half-constructed mode, so we need this + check */ + if (prim) { + if (prim->iface->finish) { + prim->iface->finish(prim->primitive, self->state); + } + if (prim->primitive) { + free(prim->primitive); + } + free(prim); + } + } + free(self->primitives); free(self); } int render_mode_occluded(RenderMode *self, int x, int y, int z) { - return self->iface->occluded(self->mode, self->state, x, y, z); + unsigned int i; + int occluded = 0; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + if (prim->iface->occluded) { + occluded |= prim->iface->occluded(prim->primitive, self->state, x, y, z); + } + + if (occluded) + return occluded; + } + return occluded; } int render_mode_hidden(RenderMode *self, int x, int y, int z) { - return self->iface->hidden(self->mode, self->state, x, y, z); + unsigned int i; + int hidden = 0; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + if (prim->iface->hidden) { + hidden |= prim->iface->hidden(prim->primitive, self->state, x, y, z); + } + + if (hidden) + return hidden; + } + return hidden; } void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light) { - self->iface->draw(self->mode, self->state, img, mask, mask_light); + unsigned int i; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + if (prim->iface->draw) { + prim->iface->draw(prim->primitive, self->state, img, mask, mask_light); + } + } } /* options parse helper */ -int render_mode_parse_option(PyObject *dict, const char *name, const char *format, ...) { +int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...) { va_list ap; - PyObject *item; + PyObject *item, *dict; int ret; - if (dict == NULL || name == NULL) - return 1; + if (support == NULL || name == NULL) + return 0; + + dict = PyObject_GetAttrString(support, "option_values"); + if (!dict) + return 0; item = PyDict_GetItemString(dict, name); - if (item == NULL) - return 1; + if (item == NULL) { + Py_DECREF(dict); + return 0; + }; /* make sure the item we're parsing is a tuple for VaParse to work correctly */ @@ -202,6 +201,7 @@ int render_mode_parse_option(PyObject *dict, const char *name, const char *forma va_end(ap); Py_DECREF(item); + Py_DECREF(dict); if (!ret) { PyObject *errtype, *errvalue, *errtraceback; @@ -219,330 +219,3 @@ int render_mode_parse_option(PyObject *dict, const char *name, const char *forma return ret; } - -/* bindings for python -- get all the rendermode names */ -PyObject *get_render_modes(PyObject *self, PyObject *args) { - PyObject *modes; - unsigned int i; - PyObject *key, *value; - Py_ssize_t pos = 0; - - if (!PyArg_ParseTuple(args, "")) - return NULL; - - modes = PyList_New(0); - if (modes == NULL) - return NULL; - - for (i = 0; render_modes[i] != NULL; i++) { - PyObject *name = PyString_FromString(render_modes[i]->name); - PyList_Append(modes, name); - Py_DECREF(name); - } - - - while (PyDict_Next(custom_render_modes, &pos, &key, &value)) { - PyList_Append(modes, key); - } - - return modes; -} - -/* helper, get the list of options for a render mode */ -static inline PyObject * -get_render_mode_options(const char *rendermode) -{ - PyObject *options; - unsigned int i, j; - - options = PyList_New(0); - if (!options) - return NULL; - - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, rendermode) == 0) { - if (render_modes[i]->options == NULL) - break; - - for (j = 0; render_modes[i]->options[j].name != NULL; j++) { - RenderModeOption opt = render_modes[i]->options[j]; - PyObject *name = PyString_FromString(opt.name); - PyObject *description = PyString_FromString(opt.description); - PyObject *option = PyDict_New(); - if (!name || !description || !option) { - Py_XDECREF(name); - Py_XDECREF(description); - Py_XDECREF(option); - Py_DECREF(options); - return NULL; - } - - PyDict_SetItemString(option, "name", name); - PyDict_SetItemString(option, "description", description); - PyList_Append(options, option); - Py_DECREF(name); - Py_DECREF(description); - Py_DECREF(option); - } - break; - } - } - - return options; -} - -/* more bindings -- return info for a given rendermode name */ -PyObject *get_render_mode_info(PyObject *self, PyObject *args) { - const char* rendermode; - PyObject *info; - unsigned int i; - PyObject *custom; - - if (!PyArg_ParseTuple(args, "s", &rendermode)) - return NULL; - - info = PyDict_New(); - if (info == NULL) - return NULL; - - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, rendermode) == 0) { - PyObject *tmp; - - tmp = PyString_FromString(render_modes[i]->name); - PyDict_SetItemString(info, "name", tmp); - Py_DECREF(tmp); - - tmp = PyString_FromString(render_modes[i]->label); - PyDict_SetItemString(info, "label", tmp); - Py_DECREF(tmp); - - tmp = PyString_FromString(render_modes[i]->description); - PyDict_SetItemString(info, "description", tmp); - Py_DECREF(tmp); - - tmp = get_render_mode_options(rendermode); - PyDict_SetItemString(info, "options", tmp); - Py_DECREF(tmp); - - if (render_modes[i]->parent != NULL) { - tmp = PyString_FromString(render_modes[i]->parent->name); - PyDict_SetItemString(info, "parent", tmp); - Py_DECREF(tmp); - } - - return info; - } - } - - custom = PyDict_GetItemString(custom_render_modes, rendermode); - if (custom) { - PyObject *tmp, *copy = PyDict_Copy(custom); - Py_DECREF(info); - - tmp = PyString_FromString(rendermode); - PyDict_SetItemString(copy, "name", tmp); - Py_DECREF(tmp); - - tmp = PyList_New(0); - PyDict_SetItemString(copy, "options", tmp); - Py_DECREF(tmp); - - return copy; - } - - Py_DECREF(info); - return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode); -} - -/* bindings -- get list of inherited parents */ -PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) { - const char *rendermode; - PyObject *parents; - unsigned int i; - RenderModeInterface *iface = NULL; - PyObject *custom; - - if (!PyArg_ParseTuple(args, "s", &rendermode)) - return NULL; - - parents = PyList_New(0); - if (!parents) - return NULL; - - /* take care of the chain of custom modes, if there are any */ - custom = PyDict_GetItemString(custom_render_modes, rendermode); - while (custom != NULL) { - PyObject *name = PyString_FromString(rendermode); - PyList_Append(parents, name); - Py_DECREF(name); - - custom = PyDict_GetItemString(custom, "parent"); - rendermode = PyString_AsString(custom); - custom = PyDict_GetItem(custom_render_modes, custom); - } - - /* now handle concrete modes */ - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, rendermode) == 0) { - iface = render_modes[i]; - break; - } - } - - if (!iface) { - Py_DECREF(parents); - return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode); - } - - while (iface) { - PyObject *name = PyString_FromString(iface->name); - PyList_Append(parents, name); - Py_DECREF(name); - - iface = iface->parent; - } - - PyList_Reverse(parents); - return parents; -} - -/* bindings -- get list of (direct) children */ -PyObject *get_render_mode_children(PyObject *self, PyObject *args) { - const char *rendermode; - PyObject *children; - unsigned int i; - PyObject *key, *value; - Py_ssize_t pos = 0; - - if (!PyArg_ParseTuple(args, "s", &rendermode)) - return NULL; - - children = PyList_New(0); - if (!children) - return NULL; - - for (i = 0; render_modes[i] != NULL; i++) { - if (render_modes[i]->parent && strcmp(render_modes[i]->parent->name, rendermode) == 0) { - PyObject *child_name = PyString_FromString(render_modes[i]->name); - PyList_Append(children, child_name); - Py_DECREF(child_name); - } - } - - - while (PyDict_Next(custom_render_modes, &pos, &key, &value)) { - PyObject *pyparent = PyDict_GetItemString(value, "parent"); - const char *parent = PyString_AsString(pyparent); - - if (strcmp(parent, rendermode) == 0) { - PyList_Append(children, key); - } - } - - return children; -} - -/* helper to decide if a rendermode supports a given option */ -static inline int -render_mode_supports_option(RenderModeInterface *iface, const char *name) { - unsigned int i; - - if (iface->options != NULL) { - for (i = 0; iface->options[i].name != NULL; i++) { - if (strcmp(iface->options[i].name, name) == 0) { - return 1; - } - } - } - - if (iface->parent != NULL) - return render_mode_supports_option(iface->parent, name); - - return 0; -} - -/* python rendermode options bindings */ -PyObject *set_render_mode_options(PyObject *self, PyObject *args) { - const char *rendermode; - PyObject *opts, *key, *value; - Py_ssize_t pos = 0; - RenderModeInterface *iface = NULL; - if (!PyArg_ParseTuple(args, "sO!", &rendermode, &PyDict_Type, &opts)) - return NULL; - - iface = render_mode_find_interface(rendermode); - - if (iface == NULL) { - return PyErr_Format(PyExc_ValueError, "'%s' is not a valid rendermode name", rendermode); - } - - /* check options to make sure they're available */ - while (PyDict_Next(opts, &pos, &key, &value)) { - const char *name = PyString_AsString(key); - if (name == NULL) - return NULL; - - if (!render_mode_supports_option(iface, name)) { - return PyErr_Format(PyExc_ValueError, "'%s' is not a valid option for rendermode '%s'", name, rendermode); - } - } - - PyDict_SetItemString(render_mode_options, rendermode, opts); - Py_RETURN_NONE; -} - -PyObject *add_custom_render_mode(PyObject *self, PyObject *args) { - const char *rendermode, *parentmode; - PyObject *opts, *options, *pyparent; - if (!PyArg_ParseTuple(args, "sO!", &rendermode, &PyDict_Type, &opts)) - return NULL; - - /* first, make sure the parent is set correctly */ - pyparent = PyDict_GetItemString(opts, "parent"); - if (pyparent == NULL) - return PyErr_Format(PyExc_ValueError, "'%s' does not have a parent mode", rendermode); - parentmode = PyString_AsString(pyparent); - if (parentmode == NULL) - return PyErr_Format(PyExc_ValueError, "'%s' does not have a valid parent", rendermode); - - /* check that parentmode exists */ - if (PyDict_GetItemString(custom_render_modes, parentmode) == NULL) { - unsigned int parent_valid = 0, i; - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, parentmode) == 0) { - parent_valid = 1; - } - } - - if (parent_valid == 0) - return PyErr_Format(PyExc_ValueError, "'%s' parent '%s' is not valid", rendermode, parentmode); - } - - /* remove and handle options seperately, if needed */ - options = PyDict_GetItemString(opts, "options"); - if (options != NULL) { - PyObject *opts_copy, *set_opts_args; - - opts_copy = PyDict_Copy(opts); - if (opts_copy == NULL) - return NULL; - - PyDict_DelItemString(opts_copy, "options"); - PyDict_SetItemString(custom_render_modes, rendermode, opts_copy); - Py_DECREF(opts_copy); - - /* call set_render_mode_options */ - set_opts_args = Py_BuildValue("sO", rendermode, options); - if (set_opts_args == NULL) - return NULL; - if (set_render_mode_options(NULL, set_opts_args) == NULL) { - Py_DECREF(set_opts_args); - return NULL; - } - Py_DECREF(set_opts_args); - } else { - PyDict_SetItemString(custom_render_modes, rendermode, opts); - } - Py_RETURN_NONE; -} diff --git a/overviewer_core/src/rendermodes.h b/overviewer_core/src/rendermodes.h index 1d94df9..23df5e6 100644 --- a/overviewer_core/src/rendermodes.h +++ b/overviewer_core/src/rendermodes.h @@ -15,21 +15,27 @@ * with the Overviewer. If not, see . */ -/* - * To make a new render mode (the C part, at least): +/* To make a new render primitive: * - * * add a data struct and extern'd interface declaration below + * * add a new class to rendermodes.py + * there are a ton of examples there, the syntax is pretty simple. If + * you need any extra objects that are easy to create in python, this + * is where you put them. * - * * fill in this interface struct in rendermode-(yourmode).c - * (see rendermodes-normal.c for an example: the "normal" mode) + * * create a file in src/primitives with the same name + * so, Nether (named "nether") goes in `nether.c`. * - * * if you want to derive from (say) the "normal" mode, put - * a RenderModeNormal entry at the top of your data struct, and - * be sure to call your parent's functions in your own! - * (see rendermode-night.c for a simple example derived from - * the "lighting" mode) + * * declare a RenderPrimitiveInterface with the name primitive_name + * if you have an underscore in the name, replace it with a + * hyphen. height-fading uses primitive_height_fading. * - * * add your mode to the list in rendermodes.c + * * fill in the entries of this struct + * the name should match, and you should declare an 'instance' struct + * to use as the self argument to each function. See nether.c and + * height-fading.c for simple examples. + * + * setup.py will pick up your primitive, add it to the global list, and build + * it for you if you follow these conventions. */ #ifndef __RENDERMODES_H_INCLUDED__ @@ -38,30 +44,14 @@ #include #include "overviewer.h" +/* render primitive interface */ typedef struct { - const char *name; - const char *description; -} RenderModeOption; - -/* rendermode interface */ -typedef struct _RenderModeInterface RenderModeInterface; -struct _RenderModeInterface { /* the name of this mode */ - const char *name; - /* the label to use in the map */ - const char *label; - /* the short description of this render mode */ - const char *description; - - /* a NULL-terminated list of render mode options, or NULL */ - RenderModeOption *options; - - /* the rendermode this is derived from, or NULL */ - RenderModeInterface *parent; + const char *name; /* the size of the local storage for this rendermode */ unsigned int data_size; - /* may return non-zero on error, last arg is options */ + /* may return non-zero on error, last arg is the python support object */ int (*start)(void *, RenderState *, PyObject *); void (*finish)(void *, RenderState *); /* returns non-zero to skip rendering this block because it's not visible */ @@ -71,7 +61,7 @@ struct _RenderModeInterface { int (*hidden)(void *, RenderState *, int, int, int); /* last two arguments are img and mask, from texture lookup */ void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *); -}; +} RenderPrimitiveInterface; /* A quick note about the difference between occluded and hidden: * @@ -88,57 +78,32 @@ struct _RenderModeInterface { * example, in lighting mode it is called at most 4 times per block. */ +/* convenience wrapper for a single primitive + interface */ +typedef struct { + void *primitive; + RenderPrimitiveInterface *iface; +} RenderPrimitive; + /* wrapper for passing around rendermodes */ struct _RenderMode { - void *mode; - RenderModeInterface *iface; + unsigned int num_primitives; + RenderPrimitive **primitives; RenderState *state; }; /* functions for creating / using rendermodes */ -RenderMode *render_mode_create(const char *mode, RenderState *state); +RenderMode *render_mode_create(PyObject *mode, RenderState *state); void render_mode_destroy(RenderMode *self); int render_mode_occluded(RenderMode *self, int x, int y, int z); int render_mode_hidden(RenderMode *self, int x, int y, int z); void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light); /* helper function for reading in rendermode options - works like PyArg_ParseTuple on a dictionary item */ -int render_mode_parse_option(PyObject *dict, const char *name, const char *format, ...); + works like PyArg_ParseTuple on a support object */ +int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...); -/* python metadata bindings */ -PyObject *get_render_modes(PyObject *self, PyObject *args); -PyObject *get_render_mode_info(PyObject *self, PyObject *args); -PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args); -PyObject *get_render_mode_children(PyObject *self, PyObject *args); - -/* python rendermode options bindings */ -PyObject *set_render_mode_options(PyObject *self, PyObject *args); -PyObject *add_custom_render_mode(PyObject *self, PyObject *args); - -/* individual rendermode interface declarations follow */ - -/* NORMAL */ -typedef struct { - /* coordinates of the chunk, inside its region file */ - int chunk_x, chunk_y; - /* biome data for the region */ - PyObject *biome_data; - /* grasscolor and foliagecolor lookup tables */ - PyObject *grasscolor, *foliagecolor, *watercolor; - /* biome-compatible grass/leaf textures */ - PyObject *grass_texture; - - /* black and white colors for height fading */ - PyObject *black_color, *white_color; - - float edge_opacity; - unsigned int min_depth; - unsigned int max_depth; - int height_fading; - int nether; -} RenderModeNormal; -extern RenderModeInterface rendermode_normal; +/* XXX individual rendermode interface declarations follow */ +#ifdef OLD_MODES /* OVERLAY */ typedef struct { @@ -152,54 +117,6 @@ typedef struct { } RenderModeOverlay; extern RenderModeInterface rendermode_overlay; -/* LIGHTING */ -typedef struct { - /* inherits from normal render mode */ - RenderModeNormal parent; - - PyObject *facemasks_py; - PyObject *facemasks[3]; - - /* extra data, loaded off the chunk class */ - PyObject *skylight, *blocklight; - PyObject *left_skylight, *left_blocklight; - PyObject *right_skylight, *right_blocklight; - PyObject *up_left_skylight, *up_left_blocklight; - PyObject *up_right_skylight, *up_right_blocklight; - - /* light color image, loaded if color_light is True */ - PyObject *lightcolor; - - /* can be overridden in derived rendermodes to control lighting - arguments are data, skylight, blocklight, return RGB */ - void (*calculate_light_color)(void *, unsigned char, unsigned char, unsigned char *, unsigned char *, unsigned char *); - - /* can be set to 0 in derived modes to indicate that lighting the chunk - * sides is actually important. Right now, this is used in cave mode - */ - int skip_sides; - - float shade_strength; - int color_light; - int night; -} RenderModeLighting; -extern RenderModeInterface rendermode_lighting; - -/* exposed so it can be used in other per-face occlusion checks */ -int rendermode_lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int z); - -/* exposed so sub-modes can look at colors directly */ -void get_lighting_color(RenderModeLighting *self, RenderState *state, - int x, int y, int z, - unsigned char *r, unsigned char *g, unsigned char *b); - -/* SMOOTH LIGHTING */ -typedef struct { - /* inherits from lighting */ - RenderModeLighting parent; -} RenderModeSmoothLighting; -extern RenderModeInterface rendermode_smooth_lighting; - /* SPAWN */ typedef struct { /* inherits from overlay */ @@ -209,34 +126,6 @@ typedef struct { } RenderModeSpawn; extern RenderModeInterface rendermode_spawn; -/* CAVE */ -typedef struct { - /* render blocks with lighting mode */ - RenderModeLighting parent; - - /* data used to know where the surface is */ - PyObject *skylight; - PyObject *left_skylight; - PyObject *right_skylight; - PyObject *up_left_skylight; - PyObject *up_right_skylight; - - /* data used to know where the surface is */ - PyObject *blocklight; - PyObject *left_blocklight; - PyObject *right_blocklight; - PyObject *up_left_blocklight; - PyObject *up_right_blocklight; - - /* colors used for tinting */ - PyObject *depth_colors; - - int depth_tinting; - int only_lit; - int lighting; -} RenderModeCave; -extern RenderModeInterface rendermode_cave; - /* MINERAL */ typedef struct { /* inherits from overlay */ @@ -245,5 +134,6 @@ typedef struct { void *minerals; } RenderModeMineral; extern RenderModeInterface rendermode_mineral; +#endif /* OLD_MODES */ #endif /* __RENDERMODES_H_INCLUDED__ */ diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index d9b5739..4f628e2 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -56,8 +56,11 @@ class Textures(object): def __getstate__(self): # we must get rid of the huge image lists, and other images attributes = self.__dict__.copy() - for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture']: - del attributes[attr] + for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture', 'lightcolor']: + try: + del attributes[attr] + except KeyError: + pass return attributes def __setstate__(self, attrs): # regenerate textures, if needed @@ -245,6 +248,18 @@ class Textures(object): lavatexture = self.load_image("lava.png") self.lavatexture = lavatexture return lavatexture + + def load_light_color(self): + """Helper function to load the light color texture.""" + if hasattr(self, "lightcolor"): + return self.lightcolor + try: + lightcolor = list(_load_image("light_normal.png").getdata()) + except Exception: + logging.warning("Light color image could not be found.") + lightcolor = None + self.lightcolor = lightcolor + return lightcolor def _split_terrain(self, terrain): """Builds and returns a length 256 array of each 16x16 chunk @@ -650,24 +665,6 @@ def getBiomeData(worlddir, chunkX, chunkY): currentBiomeData = data return data -## -## Color Light -## - -lightcolor = None -lightcolor_checked = False -def loadLightColor(): - global lightcolor, lightcolor_checked - - if not lightcolor_checked: - lightcolor_checked = True - try: - lightcolor = list(_load_image("light_normal.png").getdata()) - except Exception: - logging.warning("Light color image could not be found.") - lightcolor = None - return lightcolor - ## ## The other big one: @material and associated framework ## diff --git a/overviewer_core/util.py b/overviewer_core/util.py index 3be41ad..48485e6 100644 --- a/overviewer_core/util.py +++ b/overviewer_core/util.py @@ -21,6 +21,7 @@ import imp import os import os.path import sys +import platform from subprocess import Popen, PIPE import logging from cStringIO import StringIO @@ -88,6 +89,33 @@ def findGitVersion(): except Exception: return "unknown" +def is_bare_console(): + """Returns true if Overviewer is running in a bare console in + Windows, that is, if overviewer wasn't started in a cmd.exe + session. + """ + if platform.system() == 'Windows': + try: + import ctypes + GetConsoleProcessList = ctypes.windll.kernel32.GetConsoleProcessList + num = GetConsoleProcessList(ctypes.byref(ctypes.c_int(0)), ctypes.c_int(1)) + if (num == 1): + return True + + except Exception: + pass + return False + +def exit(ret=0): + """Drop-in replacement for sys.exit that will automatically detect + bare consoles and wait for user input before closing. + """ + if ret and is_bare_console(): + print + print "Press [Enter] to close this window." + raw_input() + sys.exit(ret) + # http://docs.python.org/library/itertools.html def roundrobin(iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" diff --git a/setup.py b/setup.py index db2c3d9..7a20a3d 100755 --- a/setup.py +++ b/setup.py @@ -15,6 +15,7 @@ import glob import platform import time import overviewer_core.util as util +import numpy try: import py2exe @@ -133,7 +134,6 @@ if py2exe is None: # # Third-party modules - we depend on numpy for everything -import numpy # Obtain the numpy include directory. This logic works across numpy versions. try: numpy_include = numpy.get_include() @@ -149,11 +149,16 @@ except Exception: # used to figure out what files to compile -render_modes = ['normal', 'lighting', 'smooth-lighting', 'cave'] -render_modes += ['overlay', 'spawn', 'mineral'] +# auto-created from files in primitives/, but we need the raw names so +# we can use them later. +primitives = [] +for name in glob.glob("overviewer_core/src/primitives/*.c"): + name = os.path.split(name)[-1] + name = os.path.splitext(name)[0] + primitives.append(name) c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c'] -c_overviewer_files += map(lambda mode: 'rendermode-%s.c' % (mode,), render_modes) +c_overviewer_files += map(lambda mode: 'primitives/%s.c' % (mode,), primitives) c_overviewer_files += ['Draw.c'] c_overviewer_includes = ['overviewer.h', 'rendermodes.h'] @@ -168,7 +173,7 @@ setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_o setup_kwargs['options']['build_ext'] = {'inplace' : 1} # custom clean command to remove in-place extension -# and the version file +# and the version file, primitives header class CustomClean(clean): def run(self): # do the normal cleanup @@ -177,31 +182,24 @@ class CustomClean(clean): # try to remove '_composite.{so,pyd,...}' extension, # regardless of the current system's extension name convention build_ext = self.get_finalized_command('build_ext') - pretty_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer') - fname = pretty_fname - if os.path.exists(fname): - try: - if not self.dry_run: - os.remove(fname) - log.info("removing '%s'", pretty_fname) - except OSError: - log.warn("'%s' could not be cleaned -- permission denied", - pretty_fname) - else: - log.debug("'%s' does not exist -- can't clean it", - pretty_fname) - + ext_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer') versionpath = os.path.join("overviewer_core", "overviewer_version.py") - if os.path.exists(versionpath): - try: - if not self.dry_run: - os.remove(versionpath) - log.info("removing '%s'", versionpath) - except OSError: - log.warn("'%s' could not be cleaned -- permission denied", versionpath) - else: - log.debug("'%s' does not exist -- can't clean it", versionpath) - + primspath = os.path.join("overviewer_core", "src", "primitives.h") + + for fname in [ext_fname, versionpath, primspath]: + if os.path.exists(fname): + try: + log.info("removing '%s'", fname) + if not self.dry_run: + os.remove(fname) + + except OSError: + log.warn("'%s' could not be cleaned -- permission denied", + fname) + else: + log.debug("'%s' does not exist -- can't clean it", + fname) + # now try to purge all *.pyc files for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")): for f in files: @@ -225,16 +223,34 @@ def generate_version_py(): except Exception: print "WARNING: failed to build overviewer_version file" +def generate_primitives_h(): + global primitives + prims = [p.lower().replace('-', '_') for p in primitives] + + outstr = "/* this file is auto-generated by setup.py */\n" + for p in prims: + outstr += "extern RenderPrimitiveInterface primitive_{0};\n".format(p) + outstr += "static RenderPrimitiveInterface *render_primitives[] = {\n" + for p in prims: + outstr += " &primitive_{0},\n".format(p) + outstr += " NULL\n" + outstr += "};\n" + + with open("overviewer_core/src/primitives.h", "w") as f: + f.write(outstr) + class CustomSDist(sdist): def run(self): # generate the version file generate_version_py() + generate_primitives_h() sdist.run(self) class CustomBuild(build): def run(self): # generate the version file generate_version_py() + generate_primitives_h() build.run(self) print "\nBuild Complete"