0

biomes now work, still no swamp purple though

This commit is contained in:
Aaron Griffith
2012-02-22 04:14:44 -05:00
parent 4ac7c1222a
commit 414b754e24
4 changed files with 159 additions and 176 deletions

View File

@@ -146,9 +146,9 @@ int load_chunk(RenderState* state, int x, int z, unsigned char required) {
if (sections) {
sections = PySequence_Fast(sections, "Sections tag was not a list!");
}
Py_DECREF(chunk);
if (sections == NULL) {
// exception set, again
Py_DECREF(chunk);
if (!required) {
PyErr_Clear();
}
@@ -156,6 +156,7 @@ int load_chunk(RenderState* state, int x, int z, unsigned char required) {
}
/* set up reasonable defaults */
dest->biomes = NULL;
for (i = 0; i < SECTIONS_PER_CHUNK; i++)
{
dest->sections[i].blocks = NULL;
@@ -164,6 +165,9 @@ int load_chunk(RenderState* state, int x, int z, unsigned char required) {
dest->sections[i].blocklight = NULL;
}
dest->biomes = PyDict_GetItemString(chunk, "Biomes");
Py_INCREF(dest->biomes);
for (i = 0; i < PySequence_Fast_GET_SIZE(sections); i++) {
PyObject *ycoord = NULL;
int sectiony = 0;
@@ -177,6 +181,7 @@ int load_chunk(RenderState* state, int x, int z, unsigned char required) {
load_chunk_section(dest, sectiony, section);
}
Py_DECREF(sections);
Py_DECREF(chunk);
dest->loaded = 1;
return 0;
@@ -573,6 +578,7 @@ chunk_render(PyObject *self, PyObject *args) {
for (j = 0; j < 3; j++) {
if (state.chunks[i][j].loaded) {
int k;
Py_XDECREF(state.chunks[i][j].biomes);
for (k = 0; k < SECTIONS_PER_CHUNK; k++) {
Py_XDECREF(state.chunks[i][j].sections[k].blocks);
Py_XDECREF(state.chunks[i][j].sections[k].data);

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 24
#define OVERVIEWER_EXTENSION_VERSION 25
/* Python PIL, and numpy headers */
#include <Python.h>
@@ -38,11 +38,13 @@
in y/z/x order */
#define getArrayByte3D(array, x,y,z) (*(unsigned char *)(PyArray_GETPTR3((array), (y), (z), (x))))
#define getArrayShort3D(array, x,y,z) (*(unsigned short *)(PyArray_GETPTR3((array), (y), (z), (x))))
#define getArrayByte2D(array, x,y) (*(unsigned char *)(PyArray_GETPTR2((array), (x), (y))))
#define getArrayShort2D(array, x,y) (*(unsigned short *)(PyArray_GETPTR2((array), (x), (y))))
/* generally useful MAX / MIN macros */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define CLAMP(x, a, b) (MIN(MAX(x, a), b))
/* in composite.c */
Imaging imaging_python_to_c(PyObject *obj);
@@ -71,6 +73,8 @@ typedef struct _RenderMode RenderMode;
typedef struct {
/* whether this chunk is loaded: use load_chunk to load */
int loaded;
/* chunk biome array */
PyObject *biomes;
/* all the sections in a given chunk */
struct {
/* all there is to know about each section */
@@ -143,6 +147,7 @@ typedef enum
DATA,
BLOCKLIGHT,
SKYLIGHT,
BIOMES,
} DataType;
static inline unsigned int get_data(RenderState *state, DataType type, int x, int y, int z)
{
@@ -198,6 +203,8 @@ static inline unsigned int get_data(RenderState *state, DataType type, int x, in
case SKYLIGHT:
data_array = state->chunks[chunkx][chunkz].sections[chunky].skylight;
break;
case BIOMES:
data_array = state->chunks[chunkx][chunkz].biomes;
};
if (data_array == NULL)
@@ -205,6 +212,8 @@ static inline unsigned int get_data(RenderState *state, DataType type, int x, in
if (type == BLOCKS)
return getArrayShort3D(data_array, x, y, z);
if (type == BIOMES)
return getArrayByte2D(data_array, x, z);
return getArrayByte3D(data_array, x, y, z);
}

View File

@@ -18,14 +18,57 @@
#include "../overviewer.h"
typedef struct {
/* biome data for the chunk */
PyObject *biome_data;
/* grasscolor and foliagecolor lookup tables */
PyObject *grasscolor, *foliagecolor, *watercolor;
/* biome-compatible grass/leaf textures */
PyObject *grass_texture;
} PrimitiveBase;
typedef struct {
const char* name;
float temperature;
float rainfall;
} Biome;
/* each entry in this table is yanked *directly* out of the minecraft source
* temp/rainfall are taken from what MCP calls setTemperatureRainfall
*
* keep in mind the x/y coordinate in the color tables is found *after*
* multiplying rainfall and temperature for the second coordinate, *and* the
* origin is in the lower-right. <3 biomes.
*/
static Biome biome_table[] = {
/* 0 */
{"Ocean", 0.5, 0.5},
{"Plains", 0.8, 0.4},
{"Desert", 2.0, 0.0},
{"Extreme Hills", 0.2, 0.3},
{"Forest", 0.7, 0.8},
/* 5 */
{"Taiga", 0.05, 0.8},
{"Swampland", 0.8, 0.9},
{"River", 0.5, 0.5},
{"Hell", 2.0, 0.0},
{"Sky", 0.5, 0.5},
/* 10 */
{"FrozenOcean", 0.0, 0.5},
{"FrozenRiver", 0.0, 0.5},
{"Ice Plains", 0.0, 0.5},
{"Ice Mountains", 0.0, 0.5},
{"MushroomIsland", 0.9, 1.0},
/* 15 */
{"MushroomIslandShore", 0.9, 1.0},
{"Beach", 0.8, 0.4},
{"DesertHills", 2.0, 0.0},
{"ForestHills", 0.7, 0.8},
{"TaigaHills", 0.05, 0.8},
/* 20 */
{"Extreme Hills Edge", 0.2, 0.3},
{"Jungle", 2.0, 0.45}, /* <-- GUESS, but a good one */
};
#define NUM_BIOMES (sizeof(biome_table) / sizeof(Biome))
static int
base_start(void *data, RenderState *state, PyObject *support) {
PrimitiveBase *self = (PrimitiveBase *)data;
@@ -33,15 +76,10 @@ base_start(void *data, RenderState *state, PyObject *support) {
/* biome-compliant grass mask (includes sides!) */
self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture");
self->biome_data = PyObject_CallMethod(state->regionset, "get_biome_data", "ii", state->chunkx, state->chunkz);
if (self->biome_data == NULL) {
/* error while loading biome info, or no biomes at all */
PyErr_Clear();
} else {
self->foliagecolor = PyObject_CallMethod(state->textures, "load_foliage_color", "");
self->grasscolor = PyObject_CallMethod(state->textures, "load_grass_color", "");
self->watercolor = PyObject_CallMethod(state->textures, "load_water_color", "");
}
/* color lookup tables */
self->foliagecolor = PyObject_CallMethod(state->textures, "load_foliage_color", "");
self->grasscolor = PyObject_CallMethod(state->textures, "load_grass_color", "");
self->watercolor = PyObject_CallMethod(state->textures, "load_water_color", "");
return 0;
}
@@ -50,11 +88,10 @@ static void
base_finish(void *data, RenderState *state) {
PrimitiveBase *self = (PrimitiveBase *)data;
Py_XDECREF(self->biome_data);
Py_XDECREF(self->foliagecolor);
Py_XDECREF(self->grasscolor);
Py_XDECREF(self->watercolor);
Py_XDECREF(self->grass_texture);
Py_DECREF(self->foliagecolor);
Py_DECREF(self->grasscolor);
Py_DECREF(self->watercolor);
Py_DECREF(self->grass_texture);
}
static int
@@ -72,13 +109,6 @@ base_occluded(void *data, RenderState *state, int x, int y, int z) {
return 0;
}
static int
base_hidden(void *data, RenderState *state, int x, int y, int z) {
PrimitiveBase *self = (PrimitiveBase *)data;
return 0;
}
static void
base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
PrimitiveBase *self = (PrimitiveBase *)data;
@@ -91,9 +121,8 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
* NOTES for maintainers:
*
* To add a biome-compatible block, add an OR'd condition to this
* following if block, a case to the first switch statement to handle when
* biome info IS available, and another case to the second switch
* statement for when biome info ISN'T available.
* following if block, and a case to the switch statement to handle biome
* coloring.
*
* Make sure that in textures.py, the generated textures are the
* biome-compliant ones! The tinting is now all done here.
@@ -116,113 +145,102 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
{
/* do the biome stuff! */
PyObject *facemask = mask;
unsigned char r, g, b;
unsigned char r = 255, g = 255, b = 255;
PyObject *color_table = NULL;
unsigned char flip_xy = 0;
if (state->block == 2) {
/* grass needs a special facemask */
facemask = self->grass_texture;
}
if (self->biome_data) {
/* we have data, so use it! */
unsigned int index;
switch (state->block) {
case 2:
/* grass */
color_table = self->grasscolor;
break;
case 8:
case 9:
/* water */
color_table = self->watercolor;
break;
case 18:
/* leaves */
color_table = self->foliagecolor;
if (state->block_data == 2)
{
/* birch!
birch foliage color is flipped XY-ways */
flip_xy = 1;
}
break;
case 31:
/* tall grass */
color_table = self->grasscolor;
break;
case 104:
/* pumpkin stem */
color_table = self->grasscolor;
break;
case 105:
/* melon stem */
color_table = self->grasscolor;
break;
case 106:
/* vines */
color_table = self->grasscolor;
break;
case 111:
/* lily pads */
color_table = self->grasscolor;
break;
default:
break;
};
if (color_table) {
int dx, dz;
unsigned char tablex, tabley;
float temp = 0.0, rain = 0.0;
PyObject *color = NULL;
index = big_endian_ushort(getArrayShort2D(self->biome_data, state->x, state->y));
/* average over all neighbors */
for (dx = -1; dx <= 1; dx++) {
for (dz = -1; dz <= 1; dz += (dx == 0 ? 2 : 1)) {
unsigned char biome = get_data(state, BIOMES, state->x + dx, state->y, state->z + dz);
if (biome > NUM_BIOMES)
biome = 0;
switch (state->block) {
case 2:
/* grass */
color = PySequence_GetItem(self->grasscolor, index);
break;
case 8:
case 9:
/* water */
if (self->watercolor)
{
color = PySequence_GetItem(self->watercolor, index);
} else {
color = NULL;
facemask = NULL;
temp += biome_table[biome].temperature;
rain += biome_table[biome].rainfall;
}
break;
case 18:
/* leaves */
if (state->block_data != 2)
{
/* not birch! */
color = PySequence_GetItem(self->foliagecolor, index);
} else {
/* birch!
birch foliage color is flipped XY-ways */
unsigned int index_x = 255 - (index % 256);
unsigned int index_y = 255 - (index / 256);
index = index_y * 256 + index_x;
color = PySequence_GetItem(self->foliagecolor, index);
}
break;
case 31:
/* tall grass */
color = PySequence_GetItem(self->grasscolor, index);
break;
case 104:
/* pumpkin stem */
color = PySequence_GetItem(self->grasscolor, index);
break;
case 105:
/* melon stem */
color = PySequence_GetItem(self->grasscolor, index);
break;
case 106:
/* vines */
color = PySequence_GetItem(self->grasscolor, index);
break;
case 111:
/* lily pads */
color = PySequence_GetItem(self->grasscolor, index);
break;
default:
break;
};
if (color)
{
/* we've got work to do */
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0));
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color);
}
} else {
if (state->block == 2 || state->block == 31 ||
state->block == 104 || state->block == 105)
/* grass and pumpkin/melon stems */
{
r = 115;
g = 175;
b = 71;
temp /= 8.0;
rain /= 8.0;
/* make sure they're sane */
temp = CLAMP(temp, 0.0, 1.0);
rain = CLAMP(rain, 0.0, 1.0);
/* convert to x/y coordinates in color table */
tablex = 255 - (255 * temp);
tabley = 255 - (255 * temp * rain);
if (flip_xy) {
unsigned char tmp = 255 - tablex;
tablex = 255 - tabley;
tabley = tmp;
}
if (state->block == 8 || state->block == 9)
/* water */
{
/* by default water is fine with nothing */
facemask = NULL;
}
if (state->block == 18 || state->block == 106 || state->block == 111)
/* leaves, vines and lyli pads */
{
r = 37;
g = 118;
b = 25;
}
/* look up color! */
color = PySequence_GetItem(color_table, tabley * 256 + tablex);
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0));
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
Py_DECREF(color);
}
if (facemask)
tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0);
/* final coloration */
tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0);
}
}
@@ -231,6 +249,6 @@ RenderPrimitiveInterface primitive_base = {
base_start,
base_finish,
base_occluded,
base_hidden,
NULL,
base_draw,
};

View File

@@ -32,14 +32,11 @@ This module has routines for extracting information about available worlds
class ChunkDoesntExist(Exception):
pass
class BiomeDataDoesntExist(Exception):
pass
def log_other_exceptions(func):
"""A decorator that prints out any errors that are not
ChunkDoesntExist or BiomeDataDoesntExist errors. This decorates
get_chunk because the C code is likely to swallow exceptions, so
this will at least make them visible.
ChunkDoesntExist errors. This decorates get_chunk because the C
code is likely to swallow exceptions, so this will at least make
them visible.
"""
functools.wraps(func)
@@ -48,8 +45,6 @@ def log_other_exceptions(func):
return func(*args)
except ChunkDoesntExist:
raise
except BiomeDataDoesntExist:
raise
except Exception, e:
logging.exception("%s raised this exception", func.func_name)
raise
@@ -239,7 +234,6 @@ class RegionSet(object):
# Caching implementaiton: a simple LRU cache
# Decorate the getter methods with the cache decorator
self._get_biome_data_for_region = cache.lru_cache(cachesize)(self._get_biome_data_for_region)
self.get_chunk = cache.lru_cache(cachesize)(self.get_chunk)
# Re-initialize upon unpickling
@@ -264,45 +258,6 @@ class RegionSet(object):
else:
raise Exception("Woah, what kind of dimension is this! %r" % self.regiondir)
# this is decorated with cache.lru_cache in __init__(). Be aware!
@log_other_exceptions
def _get_biome_data_for_region(self, regionx, regionz):
"""Get the block of biome data for an entire region. Biome
data is in the format output by Minecraft Biome Extractor:
http://code.google.com/p/minecraft-biome-extractor/"""
# biomes only make sense for the overworld, right now
if self.get_type() != "overworld":
raise BiomeDataDoesntExist("Biome data is not available for '%s'." % (self.get_type(),))
# biomes are, unfortunately, in a different place than regiondir
biomefile = os.path.split(self.regiondir)[0]
biomefile = os.path.join(biomefile, 'biomes', 'b.%d.%d.biome' % (regionx, regionz))
try:
with open(biomefile, 'rb') as f:
data = f.read()
if not len(data) == 512 * 512 * 2:
raise BiomeDataDoesntExist("File `%s' does not have correct size." % (biomefile,))
data = numpy.frombuffer(data, dtype=numpy.dtype(">u2"))
# reshape and transpose to get [x, z] indices
return numpy.transpose(numpy.reshape(data, (512, 512)))
except IOError:
raise BiomeDataDoesntExist("File `%s' could not be read." % (biomefile,))
@log_other_exceptions
def get_biome_data(self, x, z):
"""Get the block of biome data for the given chunk. Biome data
is returned as a 16x16 numpy array of indices into the
corresponding biome color images."""
regionx = x // 32
regionz = z // 32
blockx = (x % 32) * 16
blockz = (z % 32) * 16
region_biomes = self._get_biome_data_for_region(regionx, regionz)
return region_biomes[blockx:blockx+16,blockz:blockz+16]
# this is decorated with cache.lru_cache in __init__(). Be aware!
@log_other_exceptions
def get_chunk(self, x, z):
@@ -507,11 +462,6 @@ class RotatedRegionSet(RegionSet):
def __setstate__(self, args):
self.__init__(args[0], args[1])
def get_biome_data(self, x, z):
x,z = self.unrotate(x,z)
biome_data = super(RotatedRegionSet, self).get_biome_data(x,z)
return numpy.rot90(biome_data, self.north_dir)
def get_chunk(self, x, z):
x,z = self.unrotate(x,z)
chunk_data = super(RotatedRegionSet, self).get_chunk(x,z)