biomes now work, still no swamp purple though
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user