0

Merge branch 'dtt-c-render' into overlays

Conflicts:
	setup.py
This commit is contained in:
Aaron Griffith
2011-04-16 11:25:04 -04:00
11 changed files with 405 additions and 82 deletions

53
CONTRIBUTORS.rst Normal file
View File

@@ -0,0 +1,53 @@
============
Contributors
============
This file contains a list of every person who has contributed code to
Overviewer. It was created from the git commit log, and should include
everyone, but we may have missed a few.
Not currently included (but hopefully soon) are countless testers and bug
reporters that helped fixed the many bugs that have popped up in the course of
development.
---------------
Original Author
---------------
* Andrew Brown <brownan@gmail.com>
-------------------------
Long-term Contributions
-------------------------
These contributors have made many changes, over a fairly long time span, or
for many different parts of the code.
* Alejandro Aguilera <fenixin@lavabit.com>
* Andrew Chin <achin@eminence32.net>
* Aaron Griffith <aargri@gmail.com>
* Alex Headley <aheadley@waysaboutstuff.com>
* Alex Jurkiewicz <alex@bluebottle.net.au>
* Xon <Xon@localhost>
------------------------
Short-term Contributions
------------------------
These contributors have made specific changes for a particular bug fix or
feature.
* arrai <array.of.intellect@gmail.com>
* Kyle Brantley <kyle@averageurl.com>
* cbarber <CraigBarber@taryx.com>
* Alex Cline <cline@vivisimo.com>
* Stephen Fluin <stephen@mistuph.com>
* Benjamin Herr <ben@0x539.de>
* Ryan Hitchman <hitchmanr@gmail.com>
* Jenny <jennytoo@gmail.com>
* Michael Jensen <emjay1988@gmail.com>
* Ryan McCue <ryanmccue@cubegames.net>
* Morlok8k <otis.spankmeyer@gmail.com>
* Gregory Short <gshort2@gmail.com>
* Sam Steele <sam@sigbox.c99.org>
* timwolla <timwolla@mail.develfusion.com>

View File

@@ -1,21 +1,26 @@
====================
Minecraft Overviewer
====================
By Andrew Brown and contributors
By Andrew Brown and contributors (see CONTRIBUTORS.rst).
http://github.com/brownan/Minecraft-Overviewer
Generates large resolution images of a Minecraft map.
In short, this program reads in Minecraft world files and renders very large
resolution images. It performs a similar function to the existing Minecraft
Cartographer program but with a slightly different goal in mind: to generate
large resolution images such that one can zoom in and see details.
resolution images that can be viewed through a Google Maps interface. It
performs a similar function to the existing Minecraft Cartographer program but
with a slightly different goal in mind: to generate large resolution images
such that one can zoom in and see details.
See some examples here!
http://github.com/brownan/Minecraft-Overviewer/wiki/Map-examples
(To contact me, send me a message on Github)
Further documentation may be found at
https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation
To contact the developers and other users, go to the site at the top of this
README, or go to #overviewer on irc.freenode.net.
Features
========
@@ -56,6 +61,12 @@ If something doesn't work, let me know.
Using the Overviewer
====================
For a quick-start guide, see
https://github.com/brownan/Minecraft-Overviewer/wiki/Quick-Start-Guide
If you are upgrading from an older Overviewer to the new DTT code, see
https://github.com/brownan/Minecraft-Overviewer/wiki/DTT-Upgrade-Guide
Disclaimers
-----------
Before you dive into using this, just be aware that, for large maps, there is a
@@ -89,7 +100,7 @@ you can use the Overviewer:
texture packs out there.
Biome Tinting
~~~~~~~~~~~~~
-------------
With the Halloween update, biomes were added to Minecraft. In order to get
biome-accurate tinting, the Overviewer can use biome data produced by the
Minecraft Biome Extractor tool. This tool can be downloaded from:
@@ -102,12 +113,12 @@ then the Overviewer will use a static tinting for grass and leaves.
Compiling the C Extension
-------------------------
The C Extension for Overviewer is no longer optional. In addition to providing
a higher quality image compositing function that looks better on maps with lighting
enabled, it now does the bulk of the rendering.
The C Extension for Overviewer is no longer optional. In addition to
providing a higher quality image compositing function that looks better on
maps with lighting enabled, it now does the bulk of the rendering.
If you downloaded Overviewer as a binary package, this extension will already be
compiled for you.
If you downloaded Overviewer as a binary package, this extension will already
be compiled for you.
If you have a C compiler and the Python development libraries set up, you can
compile this extension like this::
@@ -119,7 +130,7 @@ look for a package named 'python-dev', 'python-devel' or similar. Also, some
Python distributions do not install "Imaging.h" and "ImPlatform.h" properly. If
you get errors complaining about them, you can get them from the PIL source, or
at <http://svn.effbot.org/public/tags/pil-1.1.7/libImaging/>. Just put them in
the same directory as "_composite.c".
the same directory as "overviewer.py".
For more detailed instructions, check the wiki:
https://github.com/brownan/Minecraft-Overviewer/wiki/Build-Instructions
@@ -142,12 +153,6 @@ Options
-h, --help
Shows the list of options and exits
--imgformat=FORMAT
Set the output image format used for the tiles. The default is 'png',
but 'jpg' is also supported. Note that regardless of what you choose,
Overviewer will still use PNG for cached images to avoid recompression
artifacts.
-p PROCS, --processes=PROCS
Adding the "-p" option will utilize more cores during processing. This
can speed up rendering quite a bit. The default is set to the same
@@ -157,34 +162,6 @@ Options
python overviewer.py -p 5 <Path to World> <Output Directory>
-z ZOOM, --zoom=ZOOM
The Overviewer by default will detect how many zoom levels are required
to show your entire map. This option sets it manually.
*You do not normally need to set this option!*
This is equivalent to setting the dimensions of the highest zoom level. It
does not actually change how the map is rendered, but rather *how much of
the map is rendered.* (Calling this option "zoom" may be a bit misleading,
I know)
To be precise, it sets the width and height of the highest zoom level, in
tiles. A zoom level of z means the highest zoom level of your map will be
2^z by 2^z tiles.
This option map be useful if you have some outlier chunks causing your map
to be too large, or you want to render a smaller portion of your map,
instead of rendering everything.
This will render your map with 7 zoom levels::
python overviewer.py -z 7 <Path to World> <Output Directory>
Remember that each additional zoom level adds 4 times as many tiles as
the last. This can add up fast, zoom level 10 has over a million tiles.
Tiles with no content will not be rendered, but they still take a small
amount of time to process.
-d, --delete
This option changes the mode of execution. No tiles are rendered, and
instead, files are deleted.
@@ -204,38 +181,65 @@ Options
a certain date. Or perhaps you can incrementally update your map by passing
in a subset of regions each time. It's up to you!
--lighting
This option enables map lighting, using lighting information stored by
Minecraft inside the chunks. This will make your map prettier, at the cost
of update speed.
Note that for existing, unlit maps, you may want to clear your cache
(with -d) before updating the map to use lighting. Otherwise, only updated
chunks will have lighting enabled.
--rendermodes=MODE1[,MODE2,...]
Use this option to specify which render mode to use, such as lighting or
night. Use --list-rendermodes to get a list of available rendermodes, and
a short description of each. If you provide more than one mode (separated
by commas), Overviewer will render all of them at once, and provide a
toggle on the resulting map to switch between them.
--night
This option enables --lighting, and renders the world at night.
--web-assets-hook=HOOK
This option lets you specify a script to run after the web assets have been
copied into the output directory, but before any tile rendering takes
place. This is an ideal time to do any custom postprocessing for markers.js
or other web assets.
The script should be executable, and it should accept one argument:
the path to the output directory.
--list-rendermodes
List the available render modes, and a short description of each.
Settings
--------
You can optionally store settings in a file named settings.py. It is a regular
python script, so you can use any python functions or modules you want.
This section needs to be expanded
For a sample settings file, look at 'sample.settings.py'. Note that this file
is not meant to be used directly, but instead it should be used as a
collection of examples to guide writing your own.
For a sample settings file, look at sample.settings.py
Here's a (possibly incomplete) list of available settings, which are available
in settings.py. Note that you can also set command-line options in a similar
way.
imgformat=FORMAT
Set the output image format used for the tiles. The default is 'png',
but 'jpg' is also supported.
zoom=ZOOM
The Overviewer by default will detect how many zoom levels are required
to show your entire map. This option sets it manually.
*You do not normally need to set this option!*
This is equivalent to setting the dimensions of the highest zoom level. It
does not actually change how the map is rendered, but rather *how much of
the map is rendered.* (Calling this option "zoom" may be a bit misleading,
I know)
To be precise, it sets the width and height of the highest zoom level, in
tiles. A zoom level of z means the highest zoom level of your map will be
2^z by 2^z tiles.
This option map be useful if you have some outlier chunks causing your map
to be too large, or you want to render a smaller portion of your map,
instead of rendering everything.
Remember that each additional zoom level adds 4 times as many tiles as
the last. This can add up fast, zoom level 10 has over a million tiles.
Tiles with no content will not be rendered, but they still take a small
amount of time to process.
web_assets_hook
This option lets you define a function to run after the web assets have
been copied into the output directory, but before any tile rendering takes
place. This is an ideal time to do any custom postprocessing for
markers.js or other web assets.
This function should accept one argument: a QuadtreeGen object.
Viewing the Results
-------------------

View File

@@ -284,7 +284,14 @@ class ChunkRenderer(object):
self._load_up_right()
return self._up_right_blocks
up_right_blocks = property(_load_up_right_blocks)
def _load_up_right_skylight(self):
"""Loads and returns lower-right skylight array"""
if not hasattr(self, "_up_right_skylight"):
self._load_up_right()
return self._up_right_skylight
up_right_skylight = property(_load_up_right_skylight)
def _load_up_left(self):
"""Loads and sets data from upper-left chunk"""
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1)
@@ -305,6 +312,13 @@ class ChunkRenderer(object):
return self._up_left_blocks
up_left_blocks = property(_load_up_left_blocks)
def _load_up_left_skylight(self):
"""Loads and returns lower-right skylight array"""
if not hasattr(self, "_up_left_skylight"):
self._load_up_left()
return self._up_left_skylight
up_left_skylight = property(_load_up_left_skylight)
def generate_pseudo_ancildata(self,x,y,z,blockid, north_position = 0 ):
""" Generates a pseudo ancillary data for blocks that depend of
what are surrounded and don't have ancillary data
@@ -458,8 +472,10 @@ def generate_depthcolors():
g = 0
b = 0
for z in range(128):
img = Image.new("RGB", (24,24), (r,g,b))
depth_colors.append(img)
depth_colors.append(r)
depth_colors.append(g)
depth_colors.append(b)
if z < 32:
g += 7
elif z < 64:

View File

@@ -69,7 +69,7 @@ class QuadtreeGen(object):
# Make the destination dir
if not os.path.exists(destdir):
os.mkdir(destdir)
os.makedirs(os.path.abspath(destdir))
if tiledir is None:
tiledir = rendermode
self.tiledir = tiledir

View File

@@ -55,11 +55,10 @@ except:
pil_include = []
# used to figure out what files to compile
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn']
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave']
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 += ['src/Draw.c']
c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h']

View File

@@ -26,7 +26,7 @@
// increment this value if you've made a change to the c extesion
// and want to force users to rebuild
#define OVERVIEWER_EXTENSION_VERSION 3
#define OVERVIEWER_EXTENSION_VERSION 4
/* Python PIL, and numpy headers */
#include <Python.h>

233
src/rendermode-cave.c Normal file
View File

@@ -0,0 +1,233 @@
/*
* 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>
//~
//~ /* figures out the black_coeff from a given skylight and blocklight, used in
//~ lighting calculations -- note this is *different* from the one in
//~ rendermode-lighting.c (the "skylight - 11" part) */
//~ static float calculate_darkness(unsigned char skylight, unsigned char blocklight) {
//~ return 1.0f - powf(0.8f, 15.0 - MAX(blocklight, skylight - 11));
//~ }
static int
rendermode_cave_occluded(void *data, RenderState *state) {
int x = state->x, y = state->y, z = state->z, dz = 0;
RenderModeCave* self;
self = (RenderModeCave *)data;
/* check if the block is touching skylight */
if (z != 127) {
if (getArrayByte3D(self->skylight, x, y, z+1) != 0) {
return 1;
}
if ((x == 15)) {
if (self->up_right_skylight != Py_None) {
if (getArrayByte3D(self->up_right_skylight, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x+1, y, z) != 0) {
return 1;
}
}
if (x == 0) {
if (self->left_skylight != Py_None) {
if (getArrayByte3D(self->left_skylight, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x-1, y, z) != 0) {
return 1;
}
}
if (y == 15) {
if (self->right_skylight != Py_None) {
if (getArrayByte3D(self->right_skylight, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x, y+1, z) != 0) {
return 1;
}
}
if (y == 0) {
if (self->up_left_skylight != Py_None) {
if (getArrayByte3D(self->up_left_skylight, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x, y-1, z) != 0) {
return 1;
}
}
/* check for normal occlusion */
/* use ajacent chunks, if not you get blocks spreaded in chunk edges */
if ( (x == 0) && (y != 15) ) {
if (state->left_blocks != Py_None) {
if (!is_transparent(getArrayByte3D(state->left_blocks, 15, y, z)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) {
return 1;
}
} else {
return 1;
}
}
if ( (x != 0) && (y == 15) ) {
if (state->right_blocks != Py_None) {
if (!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
!is_transparent(getArrayByte3D(state->right_blocks, x, 0, z)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1))) {
return 1;
}
} else {
return 1;
}
}
if ( (x == 0) && (y == 15) ) {
if ((state->left_blocks != Py_None) &&
(state->right_blocks != Py_None)) {
if (!is_transparent(getArrayByte3D(state->left_blocks, 15, y, z)) &&
!is_transparent(getArrayByte3D(state->right_blocks, x, 0, z)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1))) {
return 1;
}
} else {
return 1;
}
}
if ( (x != 0) && (y != 15) &&
!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) &&
!is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) {
return 1;
}
} else { /* if z == 127 skip */
return 1;
}
/* check for lakes and seas and don't render them */
/* at this point of the code the block has no skylight
* and is not occluded, but a deep sea can fool these
* 2 tests */
if ((getArrayByte3D(state->blocks, x, y, z) == 9) ||
(getArrayByte3D(state->blocks, x, y, z+1) == 9)) {
for (dz = z+1; dz < 127; dz++) { /* go up and check for skylight */
if (getArrayByte3D(self->skylight, x, y, dz) != 0) {
return 1;
}
if (getArrayByte3D(state->blocks, x, y, dz) != 9) {
/* we are out of the water! and there's no skylight
* , i.e. is a cave lake or something similar */
return 0;
}
}
}
return 0;
}
static int
rendermode_cave_start(void *data, RenderState *state) {
RenderModeCave* self;
int ret;
self = (RenderModeCave *)data;
/* first, chain up */
ret = rendermode_normal.start(data, state);
if (ret != 0)
return ret;
/* if there's skylight we are in the surface! */
self->skylight = PyObject_GetAttrString(state->self, "skylight");
self->left_skylight = PyObject_GetAttrString(state->self, "left_skylight");
self->right_skylight = PyObject_GetAttrString(state->self, "right_skylight");
self->up_left_skylight = PyObject_GetAttrString(state->self, "up_left_skylight");
self->up_right_skylight = PyObject_GetAttrString(state->self, "up_right_skylight");
/* colors for tinting */
self->depth_colors = PyObject_GetAttrString(state->chunk, "depth_colors");
return 0;
}
static void
rendermode_cave_finish(void *data, RenderState *state) {
RenderModeCave* self;
self = (RenderModeCave *)data;
Py_DECREF(self->skylight);
Py_DECREF(self->left_skylight);
Py_DECREF(self->right_skylight);
Py_DECREF(self->up_left_skylight);
Py_DECREF(self->up_right_skylight);
Py_DECREF(self->depth_colors);
rendermode_normal.finish(data, state);
}
static void
rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) {
RenderModeCave* self;
int z, r, g, b;
self = (RenderModeCave *)data;
z = state->z;
r = 0, g = 0, b = 0;
/* draw the normal block */
rendermode_normal.draw(data, state, src, mask);
/* get the colors and tint and tint */
/* TODO TODO for a nether mode there isn't tinting! */
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, mask, state->imgx, state->imgy, 0, 0);
}
RenderModeInterface rendermode_cave = {
"cave", "render only caves in normal mode",
sizeof(RenderModeCave),
rendermode_cave_start,
rendermode_cave_finish,
rendermode_cave_occluded,
rendermode_cave_draw,
};

View File

@@ -26,6 +26,7 @@ static RenderModeInterface *render_modes[] = {
&rendermode_lighting,
&rendermode_night,
&rendermode_spawn,
&rendermode_cave,
NULL
};

View File

@@ -132,4 +132,22 @@ typedef struct {
} RenderModeSpawn;
extern RenderModeInterface rendermode_spawn;
/* CAVE */
typedef struct {
/* render blocks with lighting mode */
RenderModeNormal 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;
/* colors used for tinting */
PyObject *depth_colors;
} RenderModeCave;
extern RenderModeInterface rendermode_cave;
#endif /* __RENDERMODES_H_INCLUDED__ */

View File

@@ -39,7 +39,7 @@ def findGitVersion():
with open(os.path.join(this_dir,".git","HEAD")) as f:
data = f.read().strip()
if data.startswith("ref: "):
if not os.path.exists(os.path.join(this_dir,data[5:])):
if not os.path.exists(os.path.join(this_dir, ".git", data[5:])):
return data
with open(os.path.join(this_dir, ".git", data[5:])) as g:
return g.read().strip()

View File

@@ -16,6 +16,7 @@
import functools
import os
import os.path
from glob import glob
import multiprocessing
import Queue
import sys
@@ -287,12 +288,10 @@ class World(object):
p = f.split(".")
yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f))
else:
for dirpath, dirnames, filenames in os.walk(os.path.join(self.worlddir, 'region')):
if not dirnames and filenames and "DIM-1" not in dirpath:
for f in filenames:
if f.startswith("r.") and f.endswith(".mcr"):
p = f.split(".")
yield (int(p[1]), int(p[2]), join(dirpath, f))
for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"):
dirpath, f = os.path.split(path)
p = f.split(".")
yield (int(p[1]), int(p[2]), join(dirpath, f))
def get_save_dir():
"""Returns the path to the local saves directory