0

Merge branch 'dtt-c-render' into overlays

Conflicts:
	overviewer.py
	setup.py
This commit is contained in:
Aaron Griffith
2011-03-29 05:57:56 -04:00
18 changed files with 215 additions and 72 deletions

View File

@@ -177,8 +177,8 @@ class ChunkRenderer(object):
self.queue = queue self.queue = queue
self.regionfile = worldobj.get_region_path(*chunkcoords) self.regionfile = worldobj.get_region_path(*chunkcoords)
if not os.path.exists(self.regionfile): #if not os.path.exists(self.regionfile):
raise ValueError("Could not find regionfile: %s" % self.regionfile) # raise ValueError("Could not find regionfile: %s" % self.regionfile)
## TODO TODO all of this class ## TODO TODO all of this class
@@ -187,7 +187,7 @@ class ChunkRenderer(object):
#chunkcoords = filename_split[1:3] #chunkcoords = filename_split[1:3]
#self.coords = map(world.base36decode, chunkcoords) #self.coords = map(world.base36decode, chunkcoords)
self.blockid = "%d.%d" % chunkcoords #self.blockid = "%d.%d" % chunkcoords
# chunk coordinates (useful to converting local block coords to # chunk coordinates (useful to converting local block coords to
# global block coords) # global block coords)
@@ -197,12 +197,6 @@ class ChunkRenderer(object):
self.world = worldobj self.world = worldobj
self.rendermode = rendermode self.rendermode = rendermode
if self.world.useBiomeData:
# make sure we've at least *tried* to load the color arrays in this process...
textures.prepareBiomeData(self.world.worlddir)
if not textures.grasscolor or not textures.foliagecolor:
raise Exception("Can't find grasscolor.png or foliagecolor.png")
def _load_level(self): def _load_level(self):
"""Loads and returns the level structure""" """Loads and returns the level structure"""
if not hasattr(self, "_level"): if not hasattr(self, "_level"):
@@ -412,22 +406,7 @@ class ChunkRenderer(object):
For cave mode, all blocks that have any direct sunlight are not For cave mode, all blocks that have any direct sunlight are not
rendered, and blocks are drawn with a color tint depending on their rendered, and blocks are drawn with a color tint depending on their
depth.""" depth."""
blocks = self.blocks
pseudo_ancildata_blocks = set([85])
left_blocks = self.left_blocks
right_blocks = self.right_blocks
if cave:
# Cave mode. Actually go through and 0 out all blocks that are not in a
# cave, so that it only renders caves.
# Places where the skylight is not 0 (there's some amount of skylight
# touching it) change it to something that won't get rendered, AND
# won't get counted as "transparent".
blocks = blocks.copy()
blocks[self.skylight != 0] = 21
blockData = get_blockdata_array(self.level) blockData = get_blockdata_array(self.level)
blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8)
# Even elements get the lower 4 bits # Even elements get the lower 4 bits
@@ -435,7 +414,6 @@ class ChunkRenderer(object):
# Odd elements get the upper 4 bits # Odd elements get the upper 4 bits
blockData_expanded[:,:,1::2] = blockData >> 4 blockData_expanded[:,:,1::2] = blockData >> 4
tileEntities = get_tileentity_data(self.level)
# Each block is 24x24 # Each block is 24x24
# The next block on the X axis adds 12px to x and subtracts 6px from y in the image # The next block on the X axis adds 12px to x and subtracts 6px from y in the image
@@ -449,6 +427,7 @@ class ChunkRenderer(object):
c_overviewer.render_loop(self, img, xoff, yoff, blockData_expanded) c_overviewer.render_loop(self, img, xoff, yoff, blockData_expanded)
tileEntities = get_tileentity_data(self.level)
for entity in tileEntities: for entity in tileEntities:
if entity['id'] == 'Sign': if entity['id'] == 'Sign':
msg=' \n'.join([entity['Text1'], entity['Text2'], entity['Text3'], entity['Text4']]) msg=' \n'.join([entity['Text1'], entity['Text2'], entity['Text3'], entity['Text4']])

View File

@@ -2,8 +2,11 @@
var config = { var config = {
fileExt: '{imgformat}', fileExt: '{imgformat}',
tileSize: 384, tileSize: 384,
defaultZoom: 1, defaultZoom: 2,
maxZoom: {maxzoom}, maxZoom: {maxzoom},
// center on this point, in world coordinates, ex:
//center: [0,0,0],
center: {spawn_coords},
cacheMinutes: 0, // Change this to have browsers automatically request new images every x minutes cacheMinutes: 0, // Change this to have browsers automatically request new images every x minutes
bg_color: '#1A1A1A', bg_color: '#1A1A1A',
debug: false debug: false
@@ -51,10 +54,3 @@ var mapTypeData=[
var mapTypeData = {maptypedata}; var mapTypeData = {maptypedata};
// Please leave the following variables here:
var markerCollection = {}; // holds groups of markers
var map;
var markersInit = false;
var regionsInit = false;

View File

@@ -94,6 +94,9 @@ class MapGen(object):
config = config.replace( config = config.replace(
"{imgformat}", str(imgformat)) "{imgformat}", str(imgformat))
config = config.replace("{spawn_coords}",
json.dumps(list(self.world.spawn)))
# create generated map type data, from given quadtrees # create generated map type data, from given quadtrees
# FIXME hook this into render_modes in setup.py, somehow # FIXME hook this into render_modes in setup.py, somehow
overlay_types = ['spawn'] overlay_types = ['spawn']
@@ -131,6 +134,11 @@ class MapGen(object):
self.web_assets_hook(self) self.web_assets_hook(self)
return return
def finalize(self):
if self.skipjs:
return
# since we will only discover PointsOfInterest in chunks that need to be # since we will only discover PointsOfInterest in chunks that need to be
# [re]rendered, POIs like signs in unchanged chunks will not be listed # [re]rendered, POIs like signs in unchanged chunks will not be listed
# in self.world.POI. To make sure we don't remove these from markers.js # in self.world.POI. To make sure we don't remove these from markers.js
@@ -157,5 +165,3 @@ class MapGen(object):
output.write(' // ]},\n') output.write(' // ]},\n')
output.write('];') output.write('];')
if self.web_assets_hook:
self.web_assets_hook(self)

View File

@@ -57,15 +57,17 @@ def main():
cpus = multiprocessing.cpu_count() cpus = multiprocessing.cpu_count()
except NotImplementedError: except NotImplementedError:
cpus = 1 cpus = 1
avail_rendermodes = c_overviewer.get_render_modes()
parser = ConfigOptionParser(usage=helptext, config="settings.py") parser = ConfigOptionParser(usage=helptext, config="settings.py")
parser.add_option("-V", "--version", dest="version", help="Displays version information and then exits", action="store_true") parser.add_option("-V", "--version", dest="version", help="Displays version information and then exits", action="store_true")
parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int") parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int")
parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True) parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True)
parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True) parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True)
parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.") parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.")
# TODO hook this up to render_modes in setup.py parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by commas. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True)
parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn"], required=True, default="normal", listify=True) parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", help="List available render modes and exit.", commandLineOnly=True)
parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg. NOTE: png will always be used as the intermediate image format.", configFileOnly=True ) parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg. NOTE: png will always be used as the intermediate image format.", configFileOnly=True )
parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True) parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True)
parser.add_option("--web-assets-hook", dest="web_assets_hook", help="If provided, run this function after the web assets have been copied, but before actual tile rendering begins. It should accept a QuadtreeGen object as its only argument.", action="store", metavar="SCRIPT", type="function", configFileOnly=True) parser.add_option("--web-assets-hook", dest="web_assets_hook", help="If provided, run this function after the web assets have been copied, but before actual tile rendering begins. It should accept a QuadtreeGen object as its only argument.", action="store", metavar="SCRIPT", type="function", configFileOnly=True)
@@ -89,6 +91,13 @@ def main():
except: except:
pass pass
sys.exit(0) sys.exit(0)
if options.list_rendermodes:
rendermode_info = map(c_overviewer.get_render_mode_info, avail_rendermodes)
name_width = max(map(lambda i: len(i['name']), rendermode_info))
for info in rendermode_info:
print "{name:{0}} {description}".format(name_width, **info)
sys.exit(0)
if len(args) < 1: if len(args) < 1:
print "You need to give me your world number or directory" print "You need to give me your world number or directory"
@@ -196,6 +205,8 @@ def main():
# render the tiles! # render the tiles!
r.go(options.procs) r.go(options.procs)
m.finalize()
def delete_all(worlddir, tiledir): def delete_all(worlddir, tiledir):
# TODO should we delete tiledir here too? # TODO should we delete tiledir here too?

View File

@@ -333,7 +333,7 @@ class QuadtreeGen(object):
def render_worldtile(self, chunks, colstart, colend, rowstart, rowend, path): def render_worldtile(self, chunks, colstart, colend, rowstart, rowend, path, poi_queue=None):
"""Renders just the specified chunks into a tile and save it. Unlike usual """Renders just the specified chunks into a tile and save it. Unlike usual
python conventions, rowend and colend are inclusive. Additionally, the python conventions, rowend and colend are inclusive. Additionally, the
chunks around the edges are half-way cut off (so that neighboring tiles chunks around the edges are half-way cut off (so that neighboring tiles
@@ -434,6 +434,8 @@ class QuadtreeGen(object):
# Compile this image # Compile this image
tileimg = Image.new("RGBA", (width, height), (38,92,255,0)) tileimg = Image.new("RGBA", (width, height), (38,92,255,0))
world = self.world
rendermode = self.rendermode
# col colstart will get drawn on the image starting at x coordinates -(384/2) # col colstart will get drawn on the image starting at x coordinates -(384/2)
# row rowstart will get drawn on the image starting at y coordinates -(192/2) # row rowstart will get drawn on the image starting at y coordinates -(192/2)
for col, row, chunkx, chunky, regionfile in chunks: for col, row, chunkx, chunky, regionfile in chunks:
@@ -441,8 +443,10 @@ class QuadtreeGen(object):
ypos = -96 + (row-rowstart)*96 ypos = -96 + (row-rowstart)*96
# draw the chunk! # draw the chunk!
# TODO POI queue a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue)
chunk.render_to_image((chunkx, chunky), tileimg, (xpos, ypos), self, False, None) a.chunk_render(tileimg, xpos, ypos, None)
# chunk.render_to_image((chunkx, chunky), tileimg, (xpos, ypos), self, False, None)
# Save them # Save them
tileimg.save(imgpath) tileimg.save(imgpath)

View File

@@ -14,6 +14,7 @@
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>. # with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
import multiprocessing import multiprocessing
import Queue
import itertools import itertools
from itertools import cycle, islice from itertools import cycle, islice
import os import os
@@ -59,7 +60,14 @@ def pool_initializer(rendernode):
#stash the quadtree objects in a global variable after fork() for windows compat. #stash the quadtree objects in a global variable after fork() for windows compat.
global child_rendernode global child_rendernode
child_rendernode = rendernode child_rendernode = rendernode
for quadtree in rendernode.quadtrees:
if quadtree.world.useBiomeData:
import textures
# make sure we've at least *tried* to load the color arrays in this process...
textures.prepareBiomeData(quadtree.world.worlddir)
if not textures.grasscolor or not textures.foliagecolor:
raise Exception("Can't find grasscolor.png or foliagecolor.png")
#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"
@@ -84,10 +92,22 @@ class RenderNode(object):
self.quadtrees = quadtrees self.quadtrees = quadtrees
#bind an index value to the quadtree so we can find it again #bind an index value to the quadtree so we can find it again
#and figure out which worlds are where
i = 0 i = 0
self.worlds = []
for q in quadtrees: for q in quadtrees:
q._render_index = i q._render_index = i
i += 1 i += 1
if q.world not in self.worlds:
self.worlds.append(q.world)
manager = multiprocessing.Manager()
# queue for receiving interesting events from the renderer
# (like the discovery of signs!
#stash into the world object like we stash an index into the quadtree
for world in self.worlds:
world.poi_q = manager.Queue()
def print_statusline(self, complete, total, level, unconditional=False): def print_statusline(self, complete, total, level, unconditional=False):
if unconditional: if unconditional:
@@ -111,12 +131,14 @@ class RenderNode(object):
# Create a pool # Create a pool
if procs == 1: if procs == 1:
pool = FakePool() pool = FakePool()
global child_rendernode pool_initializer(self)
child_rendernode = self
else: else:
pool = multiprocessing.Pool(processes=procs,initializer=pool_initializer,initargs=(self,)) pool = multiprocessing.Pool(processes=procs,initializer=pool_initializer,initargs=(self,))
#warm up the pool so it reports all the worker id's #warm up the pool so it reports all the worker id's
pool.map(bool,xrange(multiprocessing.cpu_count()),1) if logging.getLogger().level >= 10:
pool.map(bool,xrange(multiprocessing.cpu_count()),1)
else:
pool.map_async(bool,xrange(multiprocessing.cpu_count()),1)
quadtrees = self.quadtrees quadtrees = self.quadtrees
@@ -151,6 +173,19 @@ class RenderNode(object):
timestamp = timestamp2 timestamp = timestamp2
count_to_remove = (1000//batch_size) count_to_remove = (1000//batch_size)
if count_to_remove < len(results): if count_to_remove < len(results):
for world in self.worlds:
try:
while (1):
# an exception will break us out of this loop
item = world.poi_q.get(block=False)
if item[0] == "newpoi":
if item[1] not in world.POI:
#print "got an item from the queue!"
world.POI.append(item[1])
elif item[0] == "removePOI":
world.persistentData['POI'] = filter(lambda x: x['chunk'] != item[1], world.persistentData['POI'])
except Queue.Empty:
pass
while count_to_remove > 0: while count_to_remove > 0:
count_to_remove -= 1 count_to_remove -= 1
complete += results.popleft().get() complete += results.popleft().get()
@@ -166,6 +201,19 @@ class RenderNode(object):
while len(results) > 0: while len(results) > 0:
complete += results.popleft().get() complete += results.popleft().get()
self.print_statusline(complete, total, 1) self.print_statusline(complete, total, 1)
for world in self.worlds:
try:
while (1):
# an exception will break us out of this loop
item = world.poi_q.get(block=False)
if item[0] == "newpoi":
if item[1] not in world.POI:
#print "got an item from the queue!"
world.POI.append(item[1])
elif item[0] == "removePOI":
world.persistentData['POI'] = filter(lambda x: x['chunk'] != item[1], world.persistentData['POI'])
except Queue.Empty:
pass
self.print_statusline(complete, total, 1, True) self.print_statusline(complete, total, 1, True)
@@ -233,7 +281,7 @@ class RenderNode(object):
yield pool.apply_async(func=render_worldtile_batch, args= [batch]) yield pool.apply_async(func=render_worldtile_batch, args= [batch])
batch = [] batch = []
if jobcount > 0: if jobcount > 0:
yield pool.apply_async(func=render_worldtile_batch, args= [batch]) yield pool.apply_async(func=render_worldtile_batch, args= [batch])
def _apply_render_inntertile(self, pool, zoom,batch_size): def _apply_render_inntertile(self, pool, zoom,batch_size):
"""Same as _apply_render_worltiles but for the inntertile routine. """Same as _apply_render_worltiles but for the inntertile routine.
@@ -262,7 +310,7 @@ class RenderNode(object):
yield pool.apply_async(func=render_innertile_batch, args= [batch]) yield pool.apply_async(func=render_innertile_batch, args= [batch])
@catch_keyboardinterrupt @catch_keyboardinterrupt
def render_worldtile_batch(batch): def render_worldtile_batch(batch):
global child_rendernode global child_rendernode
rendernode = child_rendernode rendernode = child_rendernode
count = 0 count = 0
@@ -275,6 +323,7 @@ def render_worldtile_batch(batch):
rowstart = job[3] rowstart = job[3]
rowend = job[4] rowend = job[4]
path = job[5] path = job[5]
poi_queue = quadtree.world.poi_q
path = quadtree.full_tiledir+os.sep+path path = quadtree.full_tiledir+os.sep+path
# (even if tilechunks is empty, render_worldtile will delete # (even if tilechunks is empty, render_worldtile will delete
# existing images if appropriate) # existing images if appropriate)
@@ -282,7 +331,7 @@ def render_worldtile_batch(batch):
tilechunks = quadtree.get_chunks_in_range(colstart, colend, rowstart,rowend) tilechunks = quadtree.get_chunks_in_range(colstart, colend, rowstart,rowend)
#logging.debug(" tilechunks: %r", tilechunks) #logging.debug(" tilechunks: %r", tilechunks)
quadtree.render_worldtile(tilechunks,colstart, colend, rowstart, rowend, path) quadtree.render_worldtile(tilechunks,colstart, colend, rowstart, rowend, path, poi_queue)
return count return count
@catch_keyboardinterrupt @catch_keyboardinterrupt

View File

@@ -50,13 +50,15 @@ except AttributeError:
numpy_include = numpy.get_numpy_include() numpy_include = numpy.get_numpy_include()
# used to figure out what files to compile # used to figure out what files to compile
# TODO and, potentially, to check which are available render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn']
render_modes = ['normal', 'lighting', 'night', 'spawn', 'overlay']
c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c', 'src/rendermodes.c'] c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c', 'src/rendermodes.c']
c_overviewer_files += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes) c_overviewer_files += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes)
setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include], extra_link_args=[])) c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h']
setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include], depends=c_overviewer_includes, extra_link_args=[]))
# tell build_ext to build the extension in-place # tell build_ext to build the extension in-place
# (NOT in build/) # (NOT in build/)
setup_kwargs['options']['build_ext'] = {'inplace' : 1} setup_kwargs['options']['build_ext'] = {'inplace' : 1}

View File

@@ -23,7 +23,7 @@
static int endianness = UNKNOWN_ENDIAN; static int endianness = UNKNOWN_ENDIAN;
void init_endian() { void init_endian(void) {
/* figure out what our endianness is! */ /* figure out what our endianness is! */
short word = 0x0001; short word = 0x0001;
char* byte = (char*)(&word); char* byte = (char*)(&word);

View File

@@ -22,6 +22,10 @@ static PyMethodDef COverviewerMethods[] = {
"alpha over composite function"}, "alpha over composite function"},
{"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"},
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

View File

@@ -228,6 +228,7 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject
} }
RenderModeInterface rendermode_lighting = { RenderModeInterface rendermode_lighting = {
"lighting", "draw shadows from the lighting data",
sizeof(RenderModeLighting), sizeof(RenderModeLighting),
rendermode_lighting_start, rendermode_lighting_start,
rendermode_lighting_finish, rendermode_lighting_finish,

View File

@@ -60,6 +60,7 @@ rendermode_night_draw(void *data, RenderState *state, PyObject *src, PyObject *m
} }
RenderModeInterface rendermode_night = { RenderModeInterface rendermode_night = {
"night", "like \"lighting\", except at night",
sizeof(RenderModeNight), sizeof(RenderModeNight),
rendermode_night_start, rendermode_night_start,
rendermode_night_finish, rendermode_night_finish,

View File

@@ -164,6 +164,7 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
} }
RenderModeInterface rendermode_normal = { RenderModeInterface rendermode_normal = {
"normal", "nothing special, just render the blocks",
sizeof(RenderModeNormal), sizeof(RenderModeNormal),
rendermode_normal_start, rendermode_normal_start,
rendermode_normal_finish, rendermode_normal_finish,

View File

@@ -119,6 +119,7 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject
} }
RenderModeInterface rendermode_overlay = { RenderModeInterface rendermode_overlay = {
"overlay", "base rendermode for informational overlays",
sizeof(RenderModeOverlay), sizeof(RenderModeOverlay),
rendermode_overlay_start, rendermode_overlay_start,
rendermode_overlay_finish, rendermode_overlay_finish,

View File

@@ -105,6 +105,7 @@ rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *m
} }
RenderModeInterface rendermode_spawn = { RenderModeInterface rendermode_spawn = {
"spawn", "draws a red overlay where monsters can spawn at night",
sizeof(RenderModeSpawn), sizeof(RenderModeSpawn),
rendermode_spawn_start, rendermode_spawn_start,
rendermode_spawn_finish, rendermode_spawn_finish,

View File

@@ -18,21 +18,84 @@
#include "overviewer.h" #include "overviewer.h"
#include <string.h> #include <string.h>
/* list of all render modes, ending in NULL
all of these will be available to the user, so DON'T include modes
that are only useful as a base for other modes. */
static RenderModeInterface *render_modes[] = {
&rendermode_normal,
&rendermode_lighting,
&rendermode_night,
&rendermode_spawn,
NULL
};
/* decides which render mode to use */ /* decides which render mode to use */
RenderModeInterface *get_render_mode(RenderState *state) { RenderModeInterface *get_render_mode(RenderState *state) {
/* default: normal */ unsigned int i;
RenderModeInterface *iface = &rendermode_normal; /* default: NULL --> an error */
RenderModeInterface *iface = NULL;
PyObject *rendermode_py = PyObject_GetAttrString(state->self, "rendermode"); PyObject *rendermode_py = PyObject_GetAttrString(state->self, "rendermode");
const char *rendermode = PyString_AsString(rendermode_py); const char *rendermode = PyString_AsString(rendermode_py);
if (strcmp(rendermode, "lighting") == 0) { for (i = 0; render_modes[i] != NULL; i++) {
iface = &rendermode_lighting; if (strcmp(render_modes[i]->name, rendermode) == 0) {
} else if (strcmp(rendermode, "night") == 0) { iface = render_modes[i];
iface = &rendermode_night; break;
} else if (strcmp(rendermode, "spawn") == 0) { }
iface = &rendermode_spawn;
} }
Py_DECREF(rendermode_py); Py_DECREF(rendermode_py);
return iface; return iface;
} }
/* bindings for python -- get all the rendermode names */
PyObject *get_render_modes(PyObject *self, PyObject *args) {
PyObject *modes;
unsigned int i;
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);
}
return modes;
}
/* 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;
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]->description);
PyDict_SetItemString(info, "description", tmp);
Py_DECREF(tmp);
return info;
}
}
Py_DECREF(info);
Py_RETURN_NONE;
}

View File

@@ -29,7 +29,7 @@
* (see rendermode-night.c for a simple example derived from * (see rendermode-night.c for a simple example derived from
* the "lighting" mode) * the "lighting" mode)
* *
* * add a condition to get_render_mode() in rendermodes.c * * add your mode to the list in rendermodes.c
*/ */
#ifndef __RENDERMODES_H_INCLUDED__ #ifndef __RENDERMODES_H_INCLUDED__
@@ -39,6 +39,11 @@
/* rendermode interface */ /* rendermode interface */
typedef struct { typedef struct {
/* the name of this mode */
const char* name;
/* the short description of this render mode */
const char* description;
/* 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;
@@ -53,6 +58,9 @@ typedef struct {
/* figures out the render mode to use from the given ChunkRenderer */ /* figures out the render mode to use from the given ChunkRenderer */
RenderModeInterface *get_render_mode(RenderState *state); RenderModeInterface *get_render_mode(RenderState *state);
/* python bindings */
PyObject *get_render_modes(PyObject *self, PyObject *args);
PyObject *get_render_mode_info(PyObject *self, PyObject *args);
/* individual rendermode interface declarations follow */ /* individual rendermode interface declarations follow */

View File

@@ -1,3 +1,10 @@
var markerCollection = {}; // holds groups of markers
var map;
var markersInit = false;
var regionsInit = false;
var prevInfoWindow = null; var prevInfoWindow = null;
function prepareSignMarker(marker, item) { function prepareSignMarker(marker, item) {
@@ -275,8 +282,12 @@ function initialize() {
var query = location.search.substring(1); var query = location.search.substring(1);
var lat = 0.5; var defaultCenter = fromWorldToLatLng(config.center[0],
var lng = 0.5; config.center[1],
config.center[2]);
var lat = defaultCenter.lat();
var lng = defaultCenter.lng();
var zoom = config.defaultZoom; var zoom = config.defaultZoom;
var pairs = query.split("&"); var pairs = query.split("&");
for (var i=0; i<pairs.length; i++) { for (var i=0; i<pairs.length; i++) {

View File

@@ -84,9 +84,10 @@ class World(object):
self.regionfiles = regionfiles self.regionfiles = regionfiles
# set the number of region file handles we will permit open at any time before we start closing them # set the number of region file handles we will permit open at any time before we start closing them
# self.regionlimit = 1000 # self.regionlimit = 1000
# the max number of chunks we will keep before removing them # the max number of chunks we will keep before removing them (includes emptry chunks)
self.chunklimit = 1024*6 # this should be a multipule of the max chunks per region or things could get wonky ??? self.chunklimit = 1024
self.chunkcount = 0 self.chunkcount = 0
self.empty_chunk = [None,None]
logging.debug("Done scanning regions") logging.debug("Done scanning regions")
# figure out chunk format is in use # figure out chunk format is in use
@@ -116,7 +117,8 @@ class World(object):
else: else:
# some defaults # some defaults
self.persistentData = dict(POI=[]) self.persistentData = dict(POI=[])
def get_region_path(self, chunkX, chunkY): def get_region_path(self, chunkX, chunkY):
"""Returns the path to the region that contains chunk (chunkX, chunkY) """Returns the path to the region that contains chunk (chunkX, chunkY)
""" """
@@ -131,16 +133,18 @@ class World(object):
chunks = regioninfo[2] chunks = regioninfo[2]
chunk_data = chunks.get((x,y)) chunk_data = chunks.get((x,y))
if chunk_data is None: if chunk_data is None:
nbt = self.load_region(filename).load_chunk(x, y)
if nbt is None:
chunks[(x,y)] = [None,None]
return None ## return none. I think this is who we should indicate missing chunks
#raise IOError("No such chunk in region: (%i, %i)" % (x, y))
#prune the cache if required #prune the cache if required
if self.chunkcount > self.chunklimit: #todo: make the emptying the chunk cache slightly less crazy if self.chunkcount > self.chunklimit: #todo: make the emptying the chunk cache slightly less crazy
[self.reload_region(regionfile) for regionfile in self.regions if regionfile <> filename] [self.reload_region(regionfile) for regionfile in self.regions if regionfile <> filename]
self.chunkcount = 0
self.chunkcount += 1 self.chunkcount += 1
nbt = self.load_region(filename).load_chunk(x, y)
if nbt is None:
chunks[(x,y)] = self.empty_chunk
return None ## return none. I think this is who we should indicate missing chunks
#raise IOError("No such chunk in region: (%i, %i)" % (x, y))
#we cache the transformed data, not it's raw form #we cache the transformed data, not it's raw form
data = nbt.read_all() data = nbt.read_all()
level = data[1]['Level'] level = data[1]['Level']
@@ -222,6 +226,7 @@ class World(object):
self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ, self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ,
msg="Spawn", type="spawn", chunk=(inChunkX,inChunkZ))) msg="Spawn", type="spawn", chunk=(inChunkX,inChunkZ)))
self.spawn = (spawnX, spawnY, spawnZ)
def go(self, procs): def go(self, procs):
"""Scan the world directory, to fill in """Scan the world directory, to fill in