0

Merge remote-tracking branch 'origin/rewrite-rendermodes' into rewrite

This commit is contained in:
Andrew Brown
2012-01-16 00:50:57 -05:00
22 changed files with 1001 additions and 1415 deletions

3
.gitignore vendored
View File

@@ -22,8 +22,9 @@ overviewer_core/c_overviewer.pyd
overviewer_core/c_overviewer_d.pyd overviewer_core/c_overviewer_d.pyd
overviewer_core/c_overviewer.dylib overviewer_core/c_overviewer.dylib
# generated version file # generated files
overviewer_core/overviewer_version.py overviewer_core/overviewer_version.py
overviewer_core/src/primitives.h
# Mac OS X noise # Mac OS X noise
.DS_Store .DS_Store

View File

@@ -18,25 +18,13 @@
import platform import platform
import sys import sys
# quick version check
if not (sys.version_info[0] == 2 and sys.version_info[1] >= 6): 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" print "Sorry, the Overviewer requires at least Python 2.6 to run"
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
print "and will not run on Python 3.0 or later" print "and will not run on Python 3.0 or later"
sys.exit(1) 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
import os.path import os.path
import re import re
@@ -44,110 +32,13 @@ import subprocess
import multiprocessing import multiprocessing
import time import time
import logging 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 optparse import OptionParser
from overviewer_core import util
from overviewer_core import textures
from overviewer_core import optimizeimages, world from overviewer_core import optimizeimages, world
from overviewer_core import googlemap
from overviewer_core import configParser, tileset, assetmanager, dispatcher 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 = """ helptext = """
%prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir>""" %prog [OPTIONS] <World # / Name / Path to World> <tiles dest dir>"""
@@ -195,7 +86,6 @@ def configure_logger(loglevel=logging.INFO, verbose=False):
logger.setLevel(loglevel) logger.setLevel(loglevel)
def main(): def main():
# bootstrap the logger with defaults # bootstrap the logger with defaults
configure_logger() configure_logger()
@@ -204,7 +94,7 @@ def main():
except NotImplementedError: except NotImplementedError:
cpus = 1 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'] avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right', 'auto']
# revert to a vanilla OptionParser for right now # 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("--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("--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("--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("--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("--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 ) #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) print "Build machine: %s %s" % (overviewer_version.BUILD_PLATFORM, overviewer_version.BUILD_OS)
except ImportError: except ImportError:
print "(build info not found)" print "(build info not found)"
pass return 0
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)
if options.check_terrain: if options.check_terrain:
import hashlib import hashlib
from overviewer_core.textures import _find_file from overviewer_core.textures import Textures
if options.textures_path: # TODO custom textures path?
textures._find_file_local_path = options.textures_path tex = Textures()
try: try:
f = _find_file("terrain.png", verbose=True) f = tex.find_file("terrain.png", verbose=True)
except IOError: except IOError:
logging.error("Could not find the file terrain.png") logging.error("Could not find the file terrain.png")
doExit(code=1, consoleMsg=False) return 1
h = hashlib.sha1() h = hashlib.sha1()
h.update(f.read()) h.update(f.read())
logging.info("Hash of terrain.png file is: `%s`", h.hexdigest()) 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 # 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 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 # 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 # 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]) logging.debug("Using %r as the output_directory", args[0])
destdir = os.path.expanduser(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 elif len(args) == 2: # TODO support this usecase
worlddir = os.path.expanduser(args[0]) worlddir = os.path.expanduser(args[0])
destdir = os.path.expanduser(args[1]) destdir = os.path.expanduser(args[1])
@@ -319,14 +203,7 @@ def main():
if os.path.exists(" ".join(args[start:end])): 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\ 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])) dir but you forgot to put quotes around the directory, since it contains spaces." % " ".join(args[start:end]))
doExit(code=1, consoleMsg=False) return 1
if options.display_config:
# just display the config file and exit
parser.display_config()
doExit(code=0, consoleMsg=False)
# TODO regionlists are per-world # TODO regionlists are per-world
#if options.regionlist: #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: # except IOError as e:
# logging.error("Unable to open file %s to use for changelist." % options.changelist) # logging.error("Unable to open file %s to use for changelist." % options.changelist)
# logging.error("I/O Error: %s" % e.strerror) # logging.error("I/O Error: %s" % e.strerror)
# doExit(code=1, consoleMsg=False) # return 1
#if options.changelist_format != "auto" and not options.changelist: #if options.changelist_format != "auto" and not options.changelist:
# logging.error("changelist_format specified without changelist.") # logging.error("changelist_format specified without changelist.")
# doExit(code=1, consoleMsg=False) # return 1
#if options.changelist_format == "auto": #if options.changelist_format == "auto":
# options.changelist_format = "relative" # options.changelist_format = "relative"
@@ -382,7 +259,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
tex.generate() tex.generate()
except IOError, e: except IOError, e:
logging.error(str(e)) logging.error(str(e))
doExit(code=1, consoleMsg=False) return 1
# look at our settings.py file # look at our settings.py file
mw_parser = configParser.MultiWorldParser("settings.py") 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() dispatch.close()
assetMrg.finalize(tilesets) assetMrg.finalize(tilesets)
return 0
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]))
def list_worlds(): def list_worlds():
"Prints out a brief summary of saves found in the default directory" "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.) size = "%.2fMB" % (info['SizeOnDisk'] / 1024. / 1024.)
print formatString % (name, size, playstamp, timestamp) print formatString % (name, size, playstamp, timestamp)
if __name__ == "__main__": if __name__ == "__main__":
multiprocessing.freeze_support() multiprocessing.freeze_support()
try: try:
main() ret = main()
util.exit(ret)
except Exception, e: 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! 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 See http://docs.overviewer.org/en/latest/index.html#help
This is the error that occurred:""") This is the error that occurred:""")
doExit(code=1, consoleMsg=False) util.exit(1)

View File

@@ -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 os.path
import c_overviewer import platform
except ImportError: import traceback
pass 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)

View File

@@ -16,59 +16,125 @@
from PIL import Image from PIL import Image
import textures import textures
# Render 3 blending masks for lighting class RenderPrimitive(object):
# first is top (+Z), second is left (-X), third is right (+Y) options = {}
def generate_facemasks(): name = None
white = Image.new("L", (24,24), 255) def __init__(self, **kwargs):
if self.name is None:
top = Image.new("L", (24,24), 0) raise RuntimeError("RenderPrimitive cannot be used directly")
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)
if z < 32: self.option_values = {}
g += 7 for key, val in kwargs.iteritems():
elif z < 64: if not key in self.options:
r -= 7 raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key))
elif z < 96: self.option_values[key] = val
b += 7
else: # set up defaults
g -= 7 for name, (description, default) in self.options.iteritems():
if not name in self.option_values:
self.option_values[name] = default
return depth_colors class Base(RenderPrimitive):
depth_colors = generate_depthcolors() 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"

View File

@@ -18,7 +18,6 @@
#include "overviewer.h" #include "overviewer.h"
static PyObject *textures = NULL; static PyObject *textures = NULL;
static PyObject *support = NULL;
unsigned int max_blockid = 0; unsigned int max_blockid = 0;
unsigned int max_data = 0; unsigned int max_data = 0;
@@ -47,11 +46,6 @@ PyObject *init_chunk_render(void) {
return NULL; return NULL;
} }
support = PyImport_ImportModule("overviewer_core.rendermodes");
if (!support) {
return NULL;
}
tmp = PyObject_GetAttrString(textures, "max_blockid"); tmp = PyObject_GetAttrString(textures, "max_blockid");
if (!tmp) if (!tmp)
return NULL; return NULL;
@@ -388,7 +382,7 @@ chunk_render(PyObject *self, PyObject *args) {
RenderState state; RenderState state;
PyObject *regionset; PyObject *regionset;
int chunkx, chunkz; int chunkx, chunkz;
const char* rendermode_name = NULL; PyObject *modeobj;
PyObject *blockmap; PyObject *blockmap;
int xoff, yoff; int xoff, yoff;
@@ -406,14 +400,11 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *t = NULL; 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; return NULL;
/* rendermode support */
state.support = support;
/* set up the render mode */ /* 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) { if (rendermode == NULL) {
return NULL; // note that render_mode_create will return NULL; // note that render_mode_create will
// set PyErr. No need to set it here // set PyErr. No need to set it here

View File

@@ -17,10 +17,6 @@
#include "overviewer.h" #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) { PyObject *get_extension_version(PyObject *self, PyObject *args) {
return Py_BuildValue("i", OVERVIEWER_EXTENSION_VERSION); return Py_BuildValue("i", OVERVIEWER_EXTENSION_VERSION);
@@ -33,20 +29,6 @@ static PyMethodDef COverviewerMethods[] = {
{"render_loop", chunk_render, METH_VARARGS, {"render_loop", chunk_render, METH_VARARGS,
"Renders stuffs"}, "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, {"extension_version", get_extension_version, METH_VARARGS,
"Returns the extension version"}, "Returns the extension version"},
@@ -69,17 +51,5 @@ initc_overviewer(void)
return; 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(); init_endian();
} }

View File

@@ -79,9 +79,6 @@ typedef struct {
/* the Texture object */ /* the Texture object */
PyObject *textures; PyObject *textures;
/* the rendermode support module (rendermodes.py) */
PyObject *support;
/* the block position and type, and the block array */ /* the block position and type, and the block array */
int x, y, z; int x, y, z;
unsigned char block; unsigned char block;

View File

@@ -15,39 +15,22 @@
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>. * with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "overviewer.h" #include "../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 static int
rendermode_normal_start(void *data, RenderState *state, PyObject *options) { base_start(void *data, RenderState *state, PyObject *support) {
RenderModeNormal *self = (RenderModeNormal *)data; PrimitiveBase *self = (PrimitiveBase *)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");
}*/
/* biome-compliant grass mask (includes sides!) */ /* biome-compliant grass mask (includes sides!) */
self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); 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 static void
rendermode_normal_finish(void *data, RenderState *state) { base_finish(void *data, RenderState *state) {
RenderModeNormal *self = (RenderModeNormal *)data; PrimitiveBase *self = (PrimitiveBase *)data;
Py_XDECREF(self->biome_data); Py_XDECREF(self->biome_data);
Py_XDECREF(self->foliagecolor); Py_XDECREF(self->foliagecolor);
Py_XDECREF(self->grasscolor); Py_XDECREF(self->grasscolor);
Py_XDECREF(self->watercolor); Py_XDECREF(self->watercolor);
Py_XDECREF(self->grass_texture); Py_XDECREF(self->grass_texture);
Py_XDECREF(self->black_color);
Py_XDECREF(self->white_color);
} }
static int 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) && if ( (x != 0) && (y != 15) && (z != 127) &&
!render_mode_hidden(state->rendermode, x-1, y, z) && !render_mode_hidden(state->rendermode, x-1, y, z) &&
!render_mode_hidden(state->rendermode, x, y, z+1) && !render_mode_hidden(state->rendermode, x, y, z+1) &&
@@ -123,39 +104,15 @@ rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z)
} }
static int static int
rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) { base_hidden(void *data, RenderState *state, int x, int y, int z) {
RenderModeNormal *self = (RenderModeNormal *)data; 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; return 0;
} }
static void static void
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeNormal *self = (RenderModeNormal *)data; PrimitiveBase *self = (PrimitiveBase *)data;
/* draw the block! */ /* draw the block! */
alpha_over(state->img, src, mask, state->imgx, state->imgy, 0, 0); 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) if (facemask)
tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0); 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[] = { RenderPrimitiveInterface primitive_base = {
{"edge_opacity", "darkness of the edge lines, from 0.0 to 1.0 (default: 0.15)"}, "base", sizeof(PrimitiveBase),
{"min_depth", "lowest level of blocks to render (default: 0)"}, base_start,
{"max_depth", "highest level of blocks to render (default: 127)"}, base_finish,
{"height_fading", "darken or lighten blocks based on height (default: False)"}, base_occluded,
{"nether", "if True, remove the roof of the map. Useful on nether maps. (default: False)"}, base_hidden,
{NULL, NULL} base_draw,
};
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,
}; };

View File

@@ -15,15 +15,32 @@
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>. * with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "overviewer.h" #include "../overviewer.h"
#include <math.h> #include <math.h>
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 static inline int
touches_light(unsigned int x, unsigned int y, unsigned int z, touches_light(unsigned int x, unsigned int y, unsigned int z,
PyObject *light, PyObject *left_light, PyObject *right_light, PyObject *light, PyObject *left_light, PyObject *right_light,
PyObject *up_left_light, PyObject *up_right_light) { PyObject *up_left_light, PyObject *up_right_light) {
if (getArrayByte3D(light, x, y, z+1) != 0) { if (getArrayByte3D(light, x, y, z+1) != 0) {
return 1; return 1;
} }
@@ -79,10 +96,11 @@ touches_light(unsigned int x, unsigned int y, unsigned int z,
return 0; return 0;
} }
static inline int static int
rendermode_cave_adjacent_occluded(void *data, RenderState *state, int x, int y, int z) { cave_occluded(void *data, RenderState *state, int x, int y, int z) {
/* check for occlusion of edge blocks, using adjacent block data */ /* check for normal occlusion */
/* use ajacent chunks, if not you get blocks spreaded in chunk edges */
if (z != 127) { if (z != 127) {
if ( (x == 0) && (y != 15) ) { if ( (x == 0) && (y != 15) ) {
if (state->left_blocks != Py_None) { if (state->left_blocks != Py_None) {
@@ -133,26 +151,11 @@ rendermode_cave_adjacent_occluded(void *data, RenderState *state, int x, int y,
} }
static int static int
rendermode_cave_occluded(void *data, RenderState *state, int x, int y, int z) { cave_hidden(void *data, RenderState *state, int x, int y, int z) {
/* first, check to see if it's "normally" occluded */ RenderPrimitiveCave* self;
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;
int dz = 0; 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 */ /* check if the block is touching skylight */
if (z != 127) { 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 /* unfortunate side-effect of lit cave mode: we need to count occluded
* blocks as hidden for the lighting to look right, since technically our * blocks as hidden for the lighting to look right, since technically our
* hiding depends on occlusion as well * 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) &&
if ( (x != 0) && (y != 15) && (z != 127) && !is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) && !is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) && !is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) {
!is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) { return 1;
return 1;
}
return rendermode_cave_adjacent_occluded(data, state, x, y, z);
} }
return 0; return cave_occluded(data, state, x, y, z);
} }
static int static int
rendermode_cave_start(void *data, RenderState *state, PyObject *options) { cave_start(void *data, RenderState *state, PyObject *support) {
RenderModeCave* self; RenderPrimitiveCave* self;
int ret; int ret;
self = (RenderModeCave *)data; self = (RenderPrimitiveCave *)data;
/* first, chain up */ if (!render_mode_parse_option(support, "only_lit", "i", &(self->only_lit)))
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)))
return 1; 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! */ /* if there's skylight we are in the surface! */
self->skylight = get_chunk_data(state, CURRENT, SKYLIGHT); 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); 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; return 0;
} }
static void static void
rendermode_cave_finish(void *data, RenderState *state) { cave_finish(void *data, RenderState *state) {
RenderModeCave* self; RenderPrimitiveCave* self;
self = (RenderModeCave *)data; self = (RenderPrimitiveCave *)data;
Py_DECREF(self->skylight); Py_DECREF(self->skylight);
Py_DECREF(self->left_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_left_blocklight);
Py_DECREF(self->up_right_blocklight); Py_DECREF(self->up_right_blocklight);
} }
Py_DECREF(self->depth_colors);
rendermode_lighting.finish(data, state);
} }
static void RenderPrimitiveInterface primitive_cave = {
rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { "cave", sizeof(RenderPrimitiveCave),
RenderModeCave* self; cave_start,
int z, r, g, b; cave_finish,
self = (RenderModeCave *)data; cave_occluded,
cave_hidden,
z = state->z; NULL,
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,
}; };

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "../overviewer.h"
#include <math.h>
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,
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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,
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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,
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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,
};

View File

@@ -15,7 +15,8 @@
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>. * with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "overviewer.h" #include "../overviewer.h"
#include "lighting.h"
#include <math.h> #include <math.h>
/* figures out the color from a given skylight and blocklight, /* figures out the color from a given skylight and blocklight,
@@ -35,7 +36,7 @@ static void
calculate_light_color_fancy(void *data, calculate_light_color_fancy(void *data,
unsigned char skylight, unsigned char blocklight, unsigned char skylight, unsigned char blocklight,
unsigned char *r, unsigned char *g, unsigned char *b) { unsigned char *r, unsigned char *g, unsigned char *b) {
RenderModeLighting *mode = (RenderModeLighting *)(data); RenderPrimitiveLighting *mode = (RenderPrimitiveLighting *)(data);
unsigned int index; unsigned int index;
PyObject *color; PyObject *color;
@@ -70,7 +71,7 @@ static void
calculate_light_color_fancy_night(void *data, calculate_light_color_fancy_night(void *data,
unsigned char skylight, unsigned char blocklight, unsigned char skylight, unsigned char blocklight,
unsigned char *r, unsigned char *g, unsigned char *b) { unsigned char *r, unsigned char *g, unsigned char *b) {
RenderModeLighting *mode = (RenderModeLighting *)(data); RenderPrimitiveLighting *mode = (RenderPrimitiveLighting *)(data);
unsigned int index; unsigned int index;
PyObject *color; PyObject *color;
@@ -95,7 +96,7 @@ calculate_light_color_fancy_night(void *data,
*/ */
inline unsigned char inline unsigned char
estimate_blocklevel(RenderModeLighting *self, RenderState *state, estimate_blocklevel(RenderPrimitiveLighting *self, RenderState *state,
int x, int y, int z, int *authoratative) { int x, int y, int z, int *authoratative) {
/* placeholders for later data arrays, coordinates */ /* placeholders for later data arrays, coordinates */
@@ -185,7 +186,7 @@ estimate_blocklevel(RenderModeLighting *self, RenderState *state,
} }
inline void inline void
get_lighting_color(RenderModeLighting *self, RenderState *state, get_lighting_color(RenderPrimitiveLighting *self, RenderState *state,
int x, int y, int z, int x, int y, int z,
unsigned char *r, unsigned char *g, unsigned char *b) { 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) { if (block == 10 || block == 11) {
/* lava blocks should always be lit! */ /* 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); 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 */ /* does per-face occlusion checking for do_shading_with_mask */
inline int 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 */ /* 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) { if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) {
unsigned char block = getArrayByte3D(state->blocks, x, y, z); 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 /* shades the drawn block with the given facemask, based on the
lighting results from (x, y, z) */ lighting results from (x, y, z) */
static inline void 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) { int x, int y, int z, PyObject *mask) {
unsigned char r, g, b; unsigned char r, g, b;
float comp_shade_strength; float comp_strength;
/* check occlusion */ /* 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; return;
get_lighting_color(self, state, x, y, z, &r, &g, &b); 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; r += (255 - r) * comp_strength;
g += (255 - g) * comp_shade_strength; g += (255 - g) * comp_strength;
b += (255 - b) * comp_shade_strength; b += (255 - b) * comp_strength;
tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0);
} }
static int static int
rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { lighting_start(void *data, RenderState *state, PyObject *support) {
RenderModeLighting* self; RenderPrimitiveLighting* self;
self = (RenderPrimitiveLighting *)data;
/* first, chain up */
int ret = rendermode_normal.start(data, state, options);
if (ret != 0)
return ret;
self = (RenderModeLighting *)data; /* don't skip sides by default */
self->skip_sides = 0;
/* skip sides by default */
self->skip_sides = 1;
self->shade_strength = 1.0; if (!render_mode_parse_option(support, "strength", "f", &(self->strength)))
if (!render_mode_parse_option(options, "shade_strength", "f", &(self->shade_strength)))
return 1; return 1;
if (!render_mode_parse_option(support, "night", "i", &(self->night)))
self->night = 0;
if (!render_mode_parse_option(options, "night", "i", &(self->night)))
return 1; return 1;
if (!render_mode_parse_option(support, "color", "i", &(self->color)))
self->color_light = 0;
if (!render_mode_parse_option(options, "color_light", "i", &(self->color_light)))
return 1; 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 // borrowed references, don't need to be decref'd
self->facemasks[0] = PyTuple_GetItem(self->facemasks_py, 0); self->facemasks[0] = PyTuple_GetItem(self->facemasks_py, 0);
self->facemasks[1] = PyTuple_GetItem(self->facemasks_py, 1); 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; self->calculate_light_color = calculate_light_color;
} }
if (self->color_light) { if (self->color) {
self->lightcolor = PyObject_CallMethod(state->textures, "loadLightColor", ""); self->lightcolor = PyObject_CallMethod(state->textures, "load_light_color", "");
if (self->lightcolor == Py_None) { if (self->lightcolor == Py_None) {
Py_DECREF(self->lightcolor); Py_DECREF(self->lightcolor);
self->lightcolor = NULL; self->lightcolor = NULL;
self->color_light = 0; self->color = 0;
} else { } else {
if (self->night) { if (self->night) {
self->calculate_light_color = calculate_light_color_fancy_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 static void
rendermode_lighting_finish(void *data, RenderState *state) { lighting_finish(void *data, RenderState *state) {
RenderModeLighting *self = (RenderModeLighting *)data; RenderPrimitiveLighting *self = (RenderPrimitiveLighting *)data;
Py_DECREF(self->facemasks_py); 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_left_blocklight);
Py_DECREF(self->up_right_skylight); Py_DECREF(self->up_right_skylight);
Py_DECREF(self->up_right_blocklight); 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 static void
rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeLighting* self; RenderPrimitiveLighting* self;
int x, y, z; int x, y, z;
/* first, chain up */ self = (RenderPrimitiveLighting *)data;
rendermode_normal.draw(data, state, src, mask, mask_light);
self = (RenderModeLighting *)data;
x = state->x, y = state->y, z = state->z; x = state->x, y = state->y, z = state->z;
if ((state->block == 9) || (state->block == 79)) { /* special case for water and ice */ 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[] = { RenderPrimitiveInterface primitive_lighting = {
{"shade_strength", "how dark to make the shadows, from 0.0 to 1.0 (default: 1.0)"}, "lighting", sizeof(RenderPrimitiveLighting),
{"night", "whether to use nighttime skylight settings (default: False)"}, lighting_start,
{"color_light", "whether to use colored light (default: False)"}, lighting_finish,
{NULL, NULL} NULL,
}; NULL,
lighting_draw,
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,
}; };

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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,
};

View File

@@ -15,9 +15,15 @@
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>. * with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "overviewer.h" #include "../overviewer.h"
#include "lighting.h"
#include <math.h> #include <math.h>
typedef struct {
/* inherits from lighting */
RenderPrimitiveLighting parent;
} RenderPrimitiveSmoothLighting;
/* structure representing one corner of a face (see below) */ /* structure representing one corner of a face (see below) */
struct SmoothLightingCorner { struct SmoothLightingCorner {
/* where this corner shows up on each block texture */ /* where this corner shows up on each block texture */
@@ -123,12 +129,12 @@ enum
}; };
static void 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; int i;
RenderModeLighting *lighting = (RenderModeLighting *)self; RenderPrimitiveLighting *lighting = (RenderPrimitiveLighting *)self;
int x = state->imgx, y = state->imgy; int x = state->imgx, y = state->imgy;
struct SmoothLightingCorner *pts = face.corners; 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_r[4] = {0, 0, 0, 0};
unsigned char pts_g[4] = {0, 0, 0, 0}; unsigned char pts_g[4] = {0, 0, 0, 0};
unsigned char pts_b[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; int cz = state->z + face.dz;
/* first, check for occlusion if the block is in the local chunk */ /* 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; return;
/* calculate the lighting colors for each point */ /* calculate the lighting colors for each point */
@@ -189,55 +195,38 @@ do_shading_with_rule(RenderModeSmoothLighting *self, RenderState *state, struct
} }
static int static int
rendermode_smooth_lighting_start(void *data, RenderState *state, PyObject *options) { smooth_lighting_start(void *data, RenderState *state, PyObject *support) {
/* first, chain up */ /* first, chain up */
int ret = rendermode_lighting.start(data, state, options); int ret = primitive_lighting.start(data, state, support);
if (ret != 0) if (ret != 0)
return ret; return ret;
return 0; return 0;
} }
static void static void
rendermode_smooth_lighting_finish(void *data, RenderState *state) { smooth_lighting_finish(void *data, RenderState *state) {
/* nothing special to do */ /* nothing special to do */
rendermode_lighting.finish(data, state); primitive_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);
} }
static void 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_top = 1;
int light_left = 1; int light_left = 1;
int light_right = 1; int light_right = 1;
RenderModeSmoothLighting *self = (RenderModeSmoothLighting *)data; RenderPrimitiveSmoothLighting *self = (RenderPrimitiveSmoothLighting *)data;
/* special case for leaves, water 8, water 9 /* special case for leaves, water 8, water 9
-- these are also smooth-lit! */ -- these are also smooth-lit! */
if (state->block != 18 && state->block != 8 && state->block != 9 && is_transparent(state->block)) if (state->block != 18 && state->block != 8 && state->block != 9 && is_transparent(state->block))
{ {
/* transparent blocks are rendered as usual, with flat lighting */ /* 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; return;
} }
/* non-transparent blocks get the special smooth treatment */ /* 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 */ /* special code for water */
if (state->block == 9) 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]); do_shading_with_rule(self, state, lighting_rules[FACE_RIGHT]);
} }
RenderModeInterface rendermode_smooth_lighting = { RenderPrimitiveInterface primitive_smooth_lighting = {
"smooth-lighting", "Smooth Lighting", "smooth-lighting", sizeof(RenderPrimitiveSmoothLighting),
"like \"lighting\", except smooth", smooth_lighting_start,
smooth_lighting_finish,
NULL, NULL,
&rendermode_lighting, NULL,
sizeof(RenderModeSmoothLighting), smooth_lighting_draw,
rendermode_smooth_lighting_start,
rendermode_smooth_lighting_finish,
rendermode_smooth_lighting_occluded,
rendermode_smooth_lighting_hidden,
rendermode_smooth_lighting_draw,
}; };

View File

@@ -19,175 +19,174 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
/* list of all render modes, ending in NULL /* this file defines render_primitives,
all of these will be available to the user, so DON'T include modes a list of all render primitives, ending in NULL
that are only useful as a base for other modes. */ all of these will be available to the user, so DON'T include primitives
static RenderModeInterface *render_modes[] = { that are only useful as a base for other primitives.
&rendermode_normal,
&rendermode_lighting, this file is auto-generated by setup.py */
&rendermode_smooth_lighting, #include "primitives.h"
&rendermode_cave,
&rendermode_spawn,
&rendermode_mineral,
NULL
};
PyObject *render_mode_options = NULL;
PyObject *custom_render_modes = NULL;
/* rendermode encapsulation */ /* rendermode encapsulation */
/* helper to recursively find options for a given mode */ /* helper to create a single primitive */
static inline PyObject * RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) {
render_mode_create_options(const char *mode) { RenderPrimitive *ret = NULL;
const char *parent = NULL; RenderPrimitiveInterface *iface = NULL;
PyObject *base_options, *ret, *parent_options; unsigned int i;
unsigned int i, found_concrete; 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); for (i = 0; render_primitives[i] != NULL; i++) {
if (base_options) { if (strcmp(render_primitives[i]->name, name) == 0) {
ret = PyDict_Copy(base_options); iface = render_primitives[i];
} 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;
}
break; break;
} }
} }
Py_DECREF(pyname);
if (iface == NULL)
return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "invalid primitive name: %s", name);
/* check custom mode info if needed */ ret = calloc(1, sizeof(RenderPrimitive));
if (found_concrete == 0) { if (ret == NULL) {
PyObject *custom = PyDict_GetItemString(custom_render_modes, mode); return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc a render primitive");
if (custom) { }
custom = PyDict_GetItemString(custom, "parent");
if (custom) { if (iface->data_size > 0) {
parent = PyString_AsString(custom); 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 */ ret->iface = iface;
if (parent) {
parent_options = render_mode_create_options(parent); if (iface->start) {
if (parent_options) { if (iface->start(ret->primitive, state, prim)) {
if (PyDict_Merge(ret, parent_options, 0) == -1) { free(ret->primitive);
Py_DECREF(ret); free(ret);
Py_DECREF(parent_options); return NULL;
return NULL;
}
Py_DECREF(parent_options);
} }
} }
return ret; return ret;
} }
/* helper to find the first concrete, C interface for a given mode */ RenderMode *render_mode_create(PyObject *mode, RenderState *state) {
inline static RenderModeInterface * RenderMode *ret = NULL;
render_mode_find_interface(const char *mode) { PyObject *mode_fast = NULL;
PyObject *custom;
const char *custom_parent;
unsigned int i; unsigned int i;
/* if it is *itself* concrete, we're done */ mode_fast = PySequence_Fast(mode, "Mode is not a sequence type");
for (i = 0; render_modes[i] != NULL; i++) { if (!mode_fast)
if (strcmp(render_modes[i]->name, mode) == 0) return NULL;
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);
}
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)); 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; ret->state = state;
ret->num_primitives = PySequence_Length(mode);
if (iface->start(ret->mode, state, options)) { ret->primitives = calloc(ret->num_primitives, sizeof(RenderPrimitive*));
Py_DECREF(options); for (i = 0; i < ret->num_primitives; i++) {
free(ret->mode); PyObject *pyprim = PySequence_Fast_GET_ITEM(mode_fast, i);
free(ret); RenderPrimitive *prim = render_primitive_create(pyprim, state);
return NULL;
if (!prim) {
render_mode_destroy(ret);
Py_DECREF(mode_fast);
return NULL;
}
ret->primitives[i] = prim;
} }
Py_DECREF(options);
return ret; return ret;
} }
void render_mode_destroy(RenderMode *self) { void render_mode_destroy(RenderMode *self) {
self->iface->finish(self->mode, self->state); unsigned int i;
free(self->mode);
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); free(self);
} }
int render_mode_occluded(RenderMode *self, int x, int y, int z) { 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) { 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) { 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 */ /* 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; va_list ap;
PyObject *item; PyObject *item, *dict;
int ret; int ret;
if (dict == NULL || name == NULL) if (support == NULL || name == NULL)
return 1; return 0;
dict = PyObject_GetAttrString(support, "option_values");
if (!dict)
return 0;
item = PyDict_GetItemString(dict, name); item = PyDict_GetItemString(dict, name);
if (item == NULL) if (item == NULL) {
return 1; Py_DECREF(dict);
return 0;
};
/* make sure the item we're parsing is a tuple /* make sure the item we're parsing is a tuple
for VaParse to work correctly */ 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); va_end(ap);
Py_DECREF(item); Py_DECREF(item);
Py_DECREF(dict);
if (!ret) { if (!ret) {
PyObject *errtype, *errvalue, *errtraceback; PyObject *errtype, *errvalue, *errtraceback;
@@ -219,330 +219,3 @@ int render_mode_parse_option(PyObject *dict, const char *name, const char *forma
return ret; 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;
}

View File

@@ -15,21 +15,27 @@
* with the Overviewer. If not, see <http://www.gnu.org/licenses/>. * with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* /* To make a new render primitive:
* To make a new render mode (the C part, at least):
* *
* * 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 * * create a file in src/primitives with the same name
* (see rendermodes-normal.c for an example: the "normal" mode) * so, Nether (named "nether") goes in `nether.c`.
* *
* * if you want to derive from (say) the "normal" mode, put * * declare a RenderPrimitiveInterface with the name primitive_name
* a RenderModeNormal entry at the top of your data struct, and * if you have an underscore in the name, replace it with a
* be sure to call your parent's functions in your own! * hyphen. height-fading uses primitive_height_fading.
* (see rendermode-night.c for a simple example derived from
* the "lighting" mode)
* *
* * 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__ #ifndef __RENDERMODES_H_INCLUDED__
@@ -38,30 +44,14 @@
#include <Python.h> #include <Python.h>
#include "overviewer.h" #include "overviewer.h"
/* render primitive interface */
typedef struct { typedef struct {
const char *name;
const char *description;
} RenderModeOption;
/* rendermode interface */
typedef struct _RenderModeInterface RenderModeInterface;
struct _RenderModeInterface {
/* the name of this mode */ /* the name of this mode */
const char *name; 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;
/* the size of the local storage for this rendermode */ /* the size of the local storage for this rendermode */
unsigned int data_size; 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 *); int (*start)(void *, RenderState *, PyObject *);
void (*finish)(void *, RenderState *); void (*finish)(void *, RenderState *);
/* returns non-zero to skip rendering this block because it's not visible */ /* 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); int (*hidden)(void *, RenderState *, int, int, int);
/* last two arguments are img and mask, from texture lookup */ /* last two arguments are img and mask, from texture lookup */
void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *); void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *);
}; } RenderPrimitiveInterface;
/* A quick note about the difference between occluded and hidden: /* 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. * 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 */ /* wrapper for passing around rendermodes */
struct _RenderMode { struct _RenderMode {
void *mode; unsigned int num_primitives;
RenderModeInterface *iface; RenderPrimitive **primitives;
RenderState *state; RenderState *state;
}; };
/* functions for creating / using rendermodes */ /* 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); void render_mode_destroy(RenderMode *self);
int render_mode_occluded(RenderMode *self, int x, int y, int z); int render_mode_occluded(RenderMode *self, int x, int y, int z);
int render_mode_hidden(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); void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light);
/* helper function for reading in rendermode options /* helper function for reading in rendermode options
works like PyArg_ParseTuple on a dictionary item */ works like PyArg_ParseTuple on a support object */
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, ...);
/* python metadata bindings */ /* XXX individual rendermode interface declarations follow */
PyObject *get_render_modes(PyObject *self, PyObject *args); #ifdef OLD_MODES
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;
/* OVERLAY */ /* OVERLAY */
typedef struct { typedef struct {
@@ -152,54 +117,6 @@ typedef struct {
} RenderModeOverlay; } RenderModeOverlay;
extern RenderModeInterface rendermode_overlay; 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 */ /* SPAWN */
typedef struct { typedef struct {
/* inherits from overlay */ /* inherits from overlay */
@@ -209,34 +126,6 @@ typedef struct {
} RenderModeSpawn; } RenderModeSpawn;
extern RenderModeInterface rendermode_spawn; 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 */ /* MINERAL */
typedef struct { typedef struct {
/* inherits from overlay */ /* inherits from overlay */
@@ -245,5 +134,6 @@ typedef struct {
void *minerals; void *minerals;
} RenderModeMineral; } RenderModeMineral;
extern RenderModeInterface rendermode_mineral; extern RenderModeInterface rendermode_mineral;
#endif /* OLD_MODES */
#endif /* __RENDERMODES_H_INCLUDED__ */ #endif /* __RENDERMODES_H_INCLUDED__ */

View File

@@ -56,8 +56,11 @@ class Textures(object):
def __getstate__(self): def __getstate__(self):
# we must get rid of the huge image lists, and other images # we must get rid of the huge image lists, and other images
attributes = self.__dict__.copy() attributes = self.__dict__.copy()
for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture']: for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture', 'lightcolor']:
del attributes[attr] try:
del attributes[attr]
except KeyError:
pass
return attributes return attributes
def __setstate__(self, attrs): def __setstate__(self, attrs):
# regenerate textures, if needed # regenerate textures, if needed
@@ -245,6 +248,18 @@ class Textures(object):
lavatexture = self.load_image("lava.png") lavatexture = self.load_image("lava.png")
self.lavatexture = lavatexture self.lavatexture = lavatexture
return 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): def _split_terrain(self, terrain):
"""Builds and returns a length 256 array of each 16x16 chunk """Builds and returns a length 256 array of each 16x16 chunk
@@ -650,24 +665,6 @@ def getBiomeData(worlddir, chunkX, chunkY):
currentBiomeData = data currentBiomeData = data
return 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 ## The other big one: @material and associated framework
## ##

View File

@@ -21,6 +21,7 @@ import imp
import os import os
import os.path import os.path
import sys import sys
import platform
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import logging import logging
from cStringIO import StringIO from cStringIO import StringIO
@@ -88,6 +89,33 @@ def findGitVersion():
except Exception: except Exception:
return "unknown" 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 # http://docs.python.org/library/itertools.html
def roundrobin(iterables): def roundrobin(iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C" "roundrobin('ABC', 'D', 'EF') --> A D E B F C"

View File

@@ -15,6 +15,7 @@ import glob
import platform import platform
import time import time
import overviewer_core.util as util import overviewer_core.util as util
import numpy
try: try:
import py2exe import py2exe
@@ -133,7 +134,6 @@ if py2exe is None:
# #
# Third-party modules - we depend on numpy for everything # Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions. # Obtain the numpy include directory. This logic works across numpy versions.
try: try:
numpy_include = numpy.get_include() numpy_include = numpy.get_include()
@@ -149,11 +149,16 @@ except Exception:
# used to figure out what files to compile # used to figure out what files to compile
render_modes = ['normal', 'lighting', 'smooth-lighting', 'cave'] # auto-created from files in primitives/, but we need the raw names so
render_modes += ['overlay', 'spawn', 'mineral'] # 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 = ['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_files += ['Draw.c']
c_overviewer_includes = ['overviewer.h', 'rendermodes.h'] 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} setup_kwargs['options']['build_ext'] = {'inplace' : 1}
# custom clean command to remove in-place extension # custom clean command to remove in-place extension
# and the version file # and the version file, primitives header
class CustomClean(clean): class CustomClean(clean):
def run(self): def run(self):
# do the normal cleanup # do the normal cleanup
@@ -177,31 +182,24 @@ class CustomClean(clean):
# try to remove '_composite.{so,pyd,...}' extension, # try to remove '_composite.{so,pyd,...}' extension,
# regardless of the current system's extension name convention # regardless of the current system's extension name convention
build_ext = self.get_finalized_command('build_ext') build_ext = self.get_finalized_command('build_ext')
pretty_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer') ext_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)
versionpath = os.path.join("overviewer_core", "overviewer_version.py") versionpath = os.path.join("overviewer_core", "overviewer_version.py")
if os.path.exists(versionpath): primspath = os.path.join("overviewer_core", "src", "primitives.h")
try:
if not self.dry_run: for fname in [ext_fname, versionpath, primspath]:
os.remove(versionpath) if os.path.exists(fname):
log.info("removing '%s'", versionpath) try:
except OSError: log.info("removing '%s'", fname)
log.warn("'%s' could not be cleaned -- permission denied", versionpath) if not self.dry_run:
else: os.remove(fname)
log.debug("'%s' does not exist -- can't clean it", versionpath)
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 # now try to purge all *.pyc files
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")): for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")):
for f in files: for f in files:
@@ -225,16 +223,34 @@ def generate_version_py():
except Exception: except Exception:
print "WARNING: failed to build overviewer_version file" 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): class CustomSDist(sdist):
def run(self): def run(self):
# generate the version file # generate the version file
generate_version_py() generate_version_py()
generate_primitives_h()
sdist.run(self) sdist.run(self)
class CustomBuild(build): class CustomBuild(build):
def run(self): def run(self):
# generate the version file # generate the version file
generate_version_py() generate_version_py()
generate_primitives_h()
build.run(self) build.run(self)
print "\nBuild Complete" print "\nBuild Complete"