0

added back biome colors, and misc. cleanup in world.py

This commit is contained in:
Aaron Griffith
2012-02-08 15:25:22 -05:00
parent 834598da0f
commit a1dacfa994
4 changed files with 91 additions and 83 deletions

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 18
#define OVERVIEWER_EXTENSION_VERSION 19
/* Python PIL, and numpy headers */
#include <Python.h>
@@ -35,7 +35,7 @@
/* macro for getting a value out of various numpy arrays */
#define getArrayByte3D(array, x,y,z) (*(unsigned char *)(PyArray_GETPTR3((array), (x), (y), (z))))
#define getArrayShort1D(array, x) (*(unsigned short *)(PyArray_GETPTR1((array), (x))))
#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))

View File

@@ -18,9 +18,7 @@
#include "../overviewer.h"
typedef struct {
/* coordinates of the chunk, inside its region file */
int chunk_x, chunk_y;
/* biome data for the region */
/* biome data for the chunk */
PyObject *biome_data;
/* grasscolor and foliagecolor lookup tables */
PyObject *grasscolor, *foliagecolor, *watercolor;
@@ -35,44 +33,15 @@ base_start(void *data, RenderState *state, PyObject *support) {
/* biome-compliant grass mask (includes sides!) */
self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture");
/* careful now -- C's % operator works differently from python's
we can't just do x % 32 like we did before */
self->chunk_x = state->chunkx;
self->chunk_y = state->chunkz;
while (self->chunk_x < 0)
self->chunk_x += 32;
while (self->chunk_y < 0)
self->chunk_y += 32;
self->chunk_x %= 32;
self->chunk_y %= 32;
/* XXX ignore biomes for now :( */
/*if (PyObject_IsTrue(use_biomes)) {
self->biome_data = PyObject_CallMethod(state->textures, "getBiomeData", "OOO",
worlddir, chunk_x_py, chunk_y_py);
if (self->biome_data == Py_None) {
Py_DECREF(self->biome_data);
self->biome_data = NULL;
self->foliagecolor = NULL;
self->grasscolor = NULL;
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_GetAttrString(state->textures, "foliagecolor");
self->grasscolor = PyObject_GetAttrString(state->textures, "grasscolor");
self->watercolor = PyObject_GetAttrString(state->textures, "watercolor");
if (self->watercolor == Py_None)
{
Py_DECREF(self->watercolor);
self->watercolor = NULL;
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", "");
}
}
} else {*/
self->biome_data = NULL;
self->foliagecolor = NULL;
self->grasscolor = NULL;
self->watercolor = NULL;
/*}*/
return 0;
}
@@ -159,8 +128,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
unsigned int index;
PyObject *color = NULL;
index = ((self->chunk_y * 16) + state->y) * 16 * 32 + (self->chunk_x * 16) + state->x;
index = big_endian_ushort(getArrayShort1D(self->biome_data, index));
index = big_endian_ushort(getArrayShort2D(self->biome_data, state->x, state->y));
switch (state->block) {
case 2:
@@ -211,7 +179,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
color = PySequence_GetItem(self->grasscolor, index);
break;
case 111:
/* lily padas */
/* lily pads */
color = PySequence_GetItem(self->grasscolor, index);
break;
default:

View File

@@ -56,7 +56,7 @@ class Textures(object):
def __getstate__(self):
# we must get rid of the huge image lists, and other images
attributes = self.__dict__.copy()
for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture', 'firetexture', 'portaltexture', 'lightcolor']:
for attr in ['terrain_images', 'blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture', 'firetexture', 'portaltexture', 'lightcolor', 'grasscolor', 'foliagecolor', 'watercolor']:
try:
del attributes[attr]
except KeyError:
@@ -295,6 +295,24 @@ class Textures(object):
self.lightcolor = lightcolor
return lightcolor
def load_grass_color(self):
"""Helper function to load the grass color texture."""
if not hasattr(self, "grasscolor"):
self.grasscolor = list(self.load_image("grasscolor.png").getdata())
return self.grasscolor
def load_foliage_color(self):
"""Helper function to load the foliage color texture."""
if not hasattr(self, "foliagecolor"):
self.foliagecolor = list(self.load_image("foliagecolor.png").getdata())
return self.foliagecolor
def load_water_color(self):
"""Helper function to load the water color texture."""
if not hasattr(self, "watercolor"):
self.watercolor = list(self.load_image("watercolor.png").getdata())
return self.watercolor
def _split_terrain(self, terrain):
"""Builds and returns a length 256 array of each 16x16 chunk
of texture.

View File

@@ -18,7 +18,6 @@ import os
import os.path
from glob import glob
import logging
import collections
import numpy
@@ -30,35 +29,17 @@ This module has routines for extracting information about available worlds
"""
base36decode = functools.partial(int, base=36)
cached = collections.defaultdict(dict)
class ChunkDoesntExist(Exception):
pass
def base36encode(number, alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
'''
Convert an integer to a base36 string.
'''
if not isinstance(number, (int, long)):
raise TypeError('number must be an integer')
newn = abs(number)
# Special case for zero
if number == 0:
return '0'
base36 = ''
while newn != 0:
newn, i = divmod(newn, len(alphabet))
base36 = alphabet[i] + base36
if number < 0:
return "-" + base36
return base36
class BiomeDataDoesntExist(Exception):
pass
def log_other_exceptions(func):
"""A decorator that prints out any errors that are not ChunkDoesntExist
errors. This decorates get_chunk because the C code is likely to swallow
exceptions, so this will at least make them visible.
"""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.
"""
functools.wraps(func)
@@ -67,6 +48,8 @@ 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
@@ -151,10 +134,6 @@ class World(object):
# TODO figure out where to handle regionlists
self.useBiomeData = os.path.exists(os.path.join(worlddir, 'biomes'))
if not self.useBiomeData:
logging.info("Notice: Not using biome data for tinting")
def get_regionsets(self):
return self.regionsets
def get_regionset(self, index):
@@ -259,7 +238,8 @@ class RegionSet(object):
logging.debug("Done scanning regions")
# Caching implementaiton: a simple LRU cache
# Decorate the get_chunk method with the cache decorator
# 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
@@ -284,6 +264,46 @@ 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):
"""Returns a dictionary object representing the "Level" NBT Compound
@@ -449,6 +469,11 @@ 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)
@@ -467,9 +492,6 @@ class RotatedRegionSet(RegionSet):
x,z = self.rotate(x,z)
yield x,z,mtime
class ChunkDoesntExist(Exception):
pass
def get_save_dir():
"""Returns the path to the local saves directory
* On Windows, at %APPDATA%/.minecraft/saves/