static int rendermode_overlay_occluded(void *data, RenderState *state) { int x = state->x, y = state->y, z = state->z; if ( (x != 0) && (y != 15) && (z != 127) && !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; } return 0; }
static int rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) { if ( (x != 0) && (y != 15) && (z != 127) && !render_mode_hidden(state->rendermode, x-1, y, z) && !render_mode_hidden(state->rendermode, x, y, z+1) && !render_mode_hidden(state->rendermode, x, y+1, z) && !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; } return 0; }
static void get_color(void *data, RenderState *state, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) { int x = state->x, y = state->y, z_max = state->z, z; //RenderModeMineral* self = (RenderModeMineral *)data; *a = 0; for (z = 0; z <= z_max; z++) { int i, tmp, max_i = sizeof(orecolors) / sizeof(struct OreColor); unsigned char blockid = getArrayByte3D(state->blocks, x, y, z); for (i = 0; i < max_i && orecolors[i].blockid != 0; i++) { if (orecolors[i].blockid == blockid) { *r = orecolors[i].r; *g = orecolors[i].g; *b = orecolors[i].b; tmp = (128 - z_max + z) * 2 - 40; *a = MIN(MAX(0, tmp), 255); max_i = i; break; } } } }
static int rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) { RenderModeNormal *self = (RenderModeNormal *)data; if (z > self->max_depth || z < self->min_depth) { return 1; } if (self->nether) { /* hide all blocks above all air blocks */ int below_air = 0; while (z < 128) { if (getArrayByte3D(state->blocks, x, y, z) == 0) { below_air = 1; break; } z++; } if (!below_air) return 1; } return 0; }
static void rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeOverlay *self = (RenderModeOverlay *)data; unsigned char r, g, b, a; PyObject *top_block_py, *block_py; // exactly analogous to edge-line code for these special blocks int increment=0; if (state->block == 44) // half-step increment=6; else if (state->block == 78) // snow increment=9; /* clear the draw space -- set alpha to 0 within mask */ tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); /* skip rendering the overlay if we can't see it */ if (state->z != 127) { unsigned char top_block = getArrayByte3D(state->blocks, state->x, state->y, state->z+1); if (!is_transparent(top_block)) { return; } /* check to be sure this block is solid/fluid */ top_block_py = PyInt_FromLong(top_block); if (PySequence_Contains(self->solid_blocks, top_block_py) || PySequence_Contains(self->fluid_blocks, top_block_py)) { /* top block is fluid or solid, skip drawing */ Py_DECREF(top_block_py); return; } Py_DECREF(top_block_py); } /* check to be sure this block is solid/fluid */ block_py = PyInt_FromLong(state->block); if (!PySequence_Contains(self->solid_blocks, block_py) && !PySequence_Contains(self->fluid_blocks, block_py)) { /* not fluid or solid, skip drawing the overlay */ Py_DECREF(block_py); return; } Py_DECREF(block_py); /* get our color info */ self->get_color(data, state, &r, &g, &b, &a); /* do the overlay */ if (a > 0) { alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy + increment, 0, 0); tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy + increment, 0, 0); } }
/* shades the drawn block with the given facemask/black_color, based on the lighting results from (x, y, z) */ static inline void do_shading_with_mask(RenderModeLighting *self, RenderState *state, int x, int y, int z, PyObject *mask) { float black_coeff; /* first, check for occlusion if the block is in the local chunk */ if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) { unsigned char block = getArrayByte3D(state->blocks, x, y, z); if (!is_transparent(block) && !render_mode_hidden(state->rendermode, x, y, z)) { /* this face isn't visible, so don't draw anything */ return; } } else if (self->skip_sides && (x == -1) && (state->left_blocks != Py_None)) { unsigned char block = getArrayByte3D(state->left_blocks, 15, state->y, state->z); if (!is_transparent(block)) { /* the same thing but for adjacent chunks, this solves an ugly black doted line between chunks in night rendermode. This wouldn't be necessary if the textures were truly tessellate-able */ return; } } else if (self->skip_sides && (y == 16) && (state->right_blocks != Py_None)) { unsigned char block = getArrayByte3D(state->right_blocks, state->x, 0, state->z); if (!is_transparent(block)) { /* the same thing but for adjacent chunks, this solves an ugly black doted line between chunks in night rendermode. This wouldn't be necessary if the textures were truly tessellate-able */ return; } } black_coeff = get_lighting_coefficient(self, state, x, y, z); black_coeff *= self->shade_strength; alpha_over_full(state->img, self->black_color, mask, black_coeff, state->imgx, state->imgy, 0, 0); }
/* shades the drawn block with the given facemask/black_color, based on the lighting results from (x, y, z) */ static inline void do_shading_with_mask(RenderModeLighting *self, RenderState *state, int x, int y, int z, PyObject *mask) { float black_coeff; /* first, check for occlusion if the block is in the local chunk */ if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) { unsigned char block = getArrayByte3D(state->blocks, x, y, z); if (!is_transparent(block)) { /* this face isn't visible, so don't draw anything */ return; } } black_coeff = get_lighting_coefficient(self, state, x, y, z, NULL); alpha_over_full(state->img, self->black_color, mask, black_coeff, state->imgx, state->imgy, 0, 0); }
/* loads the appropriate light data for the given (possibly non-local) * coordinates, and returns a black_coeff this is exposed, so other (derived) * rendermodes can use it * * authoratative is a return slot for whether or not this lighting calculation * is true, or a guess. If we guessed, *authoratative will be false, but if it * was calculated correctly from available light data, it will be true. You * may (and probably should) pass NULL. */ inline float get_lighting_coefficient(RenderModeLighting *self, RenderState *state, int x, int y, int z, int *authoratative) { /* placeholders for later data arrays, coordinates */ PyObject *blocks = NULL; PyObject *skylight = NULL; PyObject *blocklight = NULL; int local_x = x, local_y = y, local_z = z; unsigned char block, skylevel, blocklevel; /* defaults to "guess" until told otherwise */ if (authoratative) *authoratative = 0; /* find out what chunk we're in, and translate accordingly */ if (x >= 0 && y < 16) { blocks = state->blocks; skylight = self->skylight; blocklight = self->blocklight; } else if (x < 0) { local_x += 16; blocks = state->left_blocks; skylight = self->left_skylight; blocklight = self->left_blocklight; } else if (y >= 16) { local_y -= 16; blocks = state->right_blocks; skylight = self->right_skylight; blocklight = self->right_blocklight; } /* make sure we have correctly-ranged coordinates */ if (!(local_x >= 0 && local_x < 16 && local_y >= 0 && local_y < 16 && local_z >= 0 && local_z < 128)) { return self->calculate_darkness(15, 0); } /* also, make sure we have enough info to correctly calculate lighting */ if (blocks == Py_None || blocks == NULL || skylight == Py_None || skylight == NULL || blocklight == Py_None || blocklight == NULL) { return self->calculate_darkness(15, 0); } block = getArrayByte3D(blocks, local_x, local_y, local_z); /* if this block is opaque, use a fully-lit coeff instead to prevent stippled lines along chunk boundaries! */ if (!is_transparent(block)) { return self->calculate_darkness(15, 0); } /* only do special half-step handling if no authoratative pointer was passed in, which is a sign that we're recursing */ if (block == 44 && authoratative == NULL) { float average_gather = 0.0f; unsigned int average_count = 0; int auth; float coeff; /* iterate through all surrounding blocks to take an average */ int dx, dy, dz; for (dx = -1; dx <= 1; dx += 2) { for (dy = -1; dy <= 1; dy += 2) { for (dz = -1; dz <= 1; dz += 2) { coeff = get_lighting_coefficient(self, state, x+dx, y+dy, z+dz, &auth); if (auth) { average_gather += coeff; average_count++; } } } } /* only return the average if at least one was authoratative */ if (average_count > 0) return average_gather / average_count; } if (block == 10 || block == 11) { /* lava blocks should always be lit! */ return 0.0f; } skylevel = getArrayByte3D(skylight, local_x, local_y, local_z); blocklevel = getArrayByte3D(blocklight, local_x, local_y, local_z); /* no longer a guess */ if (authoratative) *authoratative = 1; return self->calculate_darkness(skylevel, blocklevel); }
inline unsigned char estimate_blocklevel(RenderModeLighting *self, RenderState *state, int x, int y, int z, int *authoratative) { /* placeholders for later data arrays, coordinates */ PyObject *blocks = NULL; PyObject *blocklight = NULL; int local_x = x, local_y = y, local_z = z; unsigned char block, blocklevel; unsigned int average_count = 0, average_gather = 0, coeff = 0; /* defaults to "guess" until told otherwise */ if (authoratative) *authoratative = 0; /* find out what chunk we're in, and translate accordingly */ if (x >= 0 && y < 16) { blocks = state->blocks; blocklight = self->blocklight; } else if (x < 0) { local_x += 16; blocks = state->left_blocks; blocklight = self->left_blocklight; } else if (y >= 16) { local_y -= 16; blocks = state->right_blocks; blocklight = self->right_blocklight; } /* make sure we have correctly-ranged coordinates */ if (!(local_x >= 0 && local_x < 16 && local_y >= 0 && local_y < 16 && local_z >= 0 && local_z < 128)) { return 0; } /* also, make sure we have enough info to correctly calculate lighting */ if (blocks == Py_None || blocks == NULL || blocklight == Py_None || blocklight == NULL) { return 0; } block = getArrayByte3D(blocks, local_x, local_y, local_z); if (authoratative == NULL) { int auth; /* iterate through all surrounding blocks to take an average */ int dx, dy, dz, local_block; for (dx = -1; dx <= 1; dx += 2) { for (dy = -1; dy <= 1; dy += 2) { for (dz = -1; dz <= 1; dz += 2) { /* skip if block is out of range */ if (x+dx < 0 || x+dx >= 16 || y+dy < 0 || y+dy >= 16 || z+dz < 0 || z+dz >= 128) { continue; } coeff = estimate_blocklevel(self, state, x+dx, y+dy, z+dz, &auth); local_block = getArrayByte3D(blocks, x+dx, y+dy, z+dz); /* only add if the block is transparent, this seems to look better than using every block */ if (auth && is_transparent(local_block)) { average_gather += coeff; average_count++; } } } } } /* only return the average if at least one was authoratative */ if (average_count > 0) { return average_gather / average_count; } blocklevel = getArrayByte3D(blocklight, local_x, local_y, local_z); /* no longer a guess */ if (!(block == 44 || block == 53 || block == 67) && authoratative) { *authoratative = 1; } return blocklevel; }
inline float get_lighting_coefficient(RenderModeLighting *self, RenderState *state, int x, int y, int z) { /* placeholders for later data arrays, coordinates */ PyObject *blocks = NULL; PyObject *skylight = NULL; PyObject *blocklight = NULL; int local_x = x, local_y = y, local_z = z; unsigned char block, skylevel, blocklevel; /* find out what chunk we're in, and translate accordingly */ if (x >= 0 && y < 16) { blocks = state->blocks; skylight = self->skylight; blocklight = self->blocklight; } else if (x < 0) { local_x += 16; blocks = state->left_blocks; skylight = self->left_skylight; blocklight = self->left_blocklight; } else if (y >= 16) { local_y -= 16; blocks = state->right_blocks; skylight = self->right_skylight; blocklight = self->right_blocklight; } /* make sure we have correctly-ranged coordinates */ if (!(local_x >= 0 && local_x < 16 && local_y >= 0 && local_y < 16 && local_z >= 0 && local_z < 128)) { return self->calculate_darkness(15, 0); } /* also, make sure we have enough info to correctly calculate lighting */ if (blocks == Py_None || blocks == NULL || skylight == Py_None || skylight == NULL || blocklight == Py_None || blocklight == NULL) { return self->calculate_darkness(15, 0); } block = getArrayByte3D(blocks, local_x, local_y, local_z); /* if this block is opaque, use a fully-lit coeff instead to prevent stippled lines along chunk boundaries! */ if (!is_transparent(block)) { return self->calculate_darkness(15, 0); } skylevel = getArrayByte3D(skylight, local_x, local_y, local_z); blocklevel = getArrayByte3D(blocklight, local_x, local_y, local_z); /* special half-step handling */ if (block == 44 || block == 53 || block == 67) { unsigned int upper_block; /* stairs and half-blocks take the skylevel from the upper block if it's transparent */ if (local_z != 127) { int upper_counter = 0; /* but if the upper_block is one of these special half-steps, we need to look at *its* upper_block */ do { upper_counter++; upper_block = getArrayByte3D(blocks, local_x, local_y, local_z + upper_counter); } while ((upper_block == 44 || upper_block == 54 || upper_block == 67) && local_z < 127); if (is_transparent(upper_block)) { skylevel = getArrayByte3D(skylight, local_x, local_y, local_z + upper_counter); } } else { upper_block = 0; skylevel = 15; } /* the block has a bad blocklevel, estimate it from neigborhood * use given coordinates, no local ones! */ blocklevel = estimate_blocklevel(self, state, x, y, z, NULL); } if (block == 10 || block == 11) { /* lava blocks should always be lit! */ return 0.0f; } return self->calculate_darkness(skylevel, blocklevel); }
/* TODO triple check this to make sure reference counting is correct */ PyObject* chunk_render(PyObject *self, PyObject *args) { RenderState state; PyObject *modeobj; PyObject *blockmap; int xoff, yoff; PyObject *imgsize, *imgsize0_py, *imgsize1_py; int imgsize0, imgsize1; PyObject *blocks_py; PyObject *left_blocks_py; PyObject *right_blocks_py; PyObject *up_left_blocks_py; PyObject *up_right_blocks_py; RenderMode *rendermode; int i, j; PyObject *t = NULL; if (!PyArg_ParseTuple(args, "OOiiiOiiOO", &state.world, &state.regionset, &state.chunkx, &state.chunky, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures)) return NULL; /* set up the render mode */ state.rendermode = rendermode = render_mode_create(modeobj, &state); if (rendermode == NULL) { return NULL; // note that render_mode_create will // set PyErr. No need to set it here } /* get the blockmap from the textures object */ blockmap = PyObject_GetAttrString(state.textures, "blockmap"); if (blockmap == NULL) { render_mode_destroy(rendermode); return NULL; } if (blockmap == Py_None) { render_mode_destroy(rendermode); PyErr_SetString(PyExc_RuntimeError, "you must call Textures.generate()"); return NULL; } /* get the image size */ imgsize = PyObject_GetAttrString(state.img, "size"); imgsize0_py = PySequence_GetItem(imgsize, 0); imgsize1_py = PySequence_GetItem(imgsize, 1); Py_DECREF(imgsize); imgsize0 = PyInt_AsLong(imgsize0_py); imgsize1 = PyInt_AsLong(imgsize1_py); Py_DECREF(imgsize0_py); Py_DECREF(imgsize1_py); /* set all block data to unloaded */ for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { state.chunks[i][j].loaded = 0; } } /* get the block data for the center column, erroring out if needed */ if (load_chunk(&state, 0, 0, 1)) { render_mode_destroy(rendermode); Py_DECREF(blockmap); return NULL; } if (state.chunks[1][1].sections[state.chunky].blocks == NULL) { /* this section doesn't exist, let's skeddadle */ render_mode_destroy(rendermode); Py_DECREF(blockmap); unload_all_chunks(&state); Py_RETURN_NONE; } /* set blocks_py, state.blocks, and state.blockdatas as convenience */ blocks_py = state.blocks = state.chunks[1][1].sections[state.chunky].blocks; state.blockdatas = state.chunks[1][1].sections[state.chunky].data; /* set up the random number generator again for each chunk so tallgrass is in the same place, no matter what mode is used */ srand(1); for (state.x = 15; state.x > -1; state.x--) { for (state.z = 0; state.z < 16; state.z++) { /* set up the render coordinates */ state.imgx = xoff + state.x*12 + state.z*12; /* 16*12 -- offset for y direction, 15*6 -- offset for x */ state.imgy = yoff - state.x*6 + state.z*6 + 16*12 + 15*6; for (state.y = 0; state.y < 16; state.y++) { unsigned char ancilData; state.imgy -= 12; /* get blockid */ state.block = getArrayShort3D(blocks_py, state.x, state.y, state.z); if (state.block == 0 || render_mode_hidden(rendermode, state.x, state.y, state.z)) { continue; } /* make sure we're rendering inside the image boundaries */ if ((state.imgx >= imgsize0 + 24) || (state.imgx <= -24)) { continue; } if ((state.imgy >= imgsize1 + 24) || (state.imgy <= -24)) { continue; } /* check for occlusion */ if (render_mode_occluded(rendermode, state.x, state.y, state.z)) { continue; } /* everything stored here will be a borrowed ref */ if (block_has_property(state.block, NODATA)) { /* block shouldn't have data associated with it, set it to 0 */ ancilData = 0; state.block_data = 0; state.block_pdata = 0; } else { /* block has associated data, use it */ ancilData = getArrayByte3D(state.blockdatas, state.x, state.y, state.z); state.block_data = ancilData; /* block that need pseudo ancildata: * grass, water, glass, chest, restone wire, * ice, fence, portal, iron bars, glass panes */ if ((state.block == 2) || (state.block == 9) || (state.block == 20) || (state.block == 54) || (state.block == 55) || (state.block == 64) || (state.block == 71) || (state.block == 79) || (state.block == 85) || (state.block == 90) || (state.block == 101) || (state.block == 102) || (state.block == 113) || (state.block == 139)) { ancilData = generate_pseudo_data(&state, ancilData); state.block_pdata = ancilData; } else { state.block_pdata = 0; } } /* make sure our block info is in-bounds */ if (state.block >= max_blockid || ancilData >= max_data) continue; /* get the texture */ t = PyList_GET_ITEM(blockmap, max_data * state.block + ancilData); /* if we don't get a texture, try it again with 0 data */ if ((t == NULL || t == Py_None) && ancilData != 0) t = PyList_GET_ITEM(blockmap, max_data * state.block); /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { PyObject *src, *mask, *mask_light; int randx = 0, randy = 0; src = PyTuple_GetItem(t, 0); mask = PyTuple_GetItem(t, 0); mask_light = PyTuple_GetItem(t, 1); if (mask == Py_None) mask = src; if (state.block == 31) { /* add a random offset to the postion of the tall grass to make it more wild */ randx = rand() % 6 + 1 - 3; randy = rand() % 6 + 1 - 3; state.imgx += randx; state.imgy += randy; } render_mode_draw(rendermode, src, mask, mask_light); if (state.block == 31) { /* undo the random offsets */ state.imgx -= randx; state.imgy -= randy; } } } } } /* free up the rendermode info */ render_mode_destroy(rendermode); Py_DECREF(blockmap); unload_all_chunks(&state); Py_RETURN_NONE; }
static void rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeNormal *self = (RenderModeNormal *)data; /* draw the block! */ alpha_over(state->img, src, mask, state->imgx, state->imgy, 0, 0); /* check for biome-compatible blocks * * 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. * * Make sure that in textures.py, the generated textures are the * biome-compliant ones! The tinting is now all done here. */ if (/* grass, but not snowgrass */ (state->block == 2 && !(state->z < 127 && getArrayByte3D(state->blocks, state->x, state->y, state->z+1) == 78)) || /* water */ state->block == 8 || state->block == 9 || /* leaves */ state->block == 18 || /* tallgrass, but not dead shrubs */ (state->block == 31 && state->block_data != 0) || /* pumpkin/melon stem, not fully grown. Fully grown stems * get constant brown color (see textures.py) */ (((state->block == 104) || (state->block == 105)) && (state->block_data != 7)) || /* vines */ state->block == 106 || /* lily pads */ state->block == 111) { /* do the biome stuff! */ PyObject *facemask = mask; unsigned char r, g, b; 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; 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)); 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; } 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 padas */ 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; } 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; } } if (facemask) tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0); } if (self->height_fading) { /* do some height fading */ PyObject *height_color = self->white_color; /* negative alpha => darkness, positive => light */ float alpha = (1.0 / (1 + expf((70 - state->z) / 11.0))) * 0.6 - 0.55; if (alpha < 0.0) { alpha *= -1; height_color = self->black_color; } alpha_over_full(state->img, height_color, mask_light, alpha, state->imgx, state->imgy, 0, 0); } /* Draw some edge lines! */ // draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1) if (state->block == 44 || state->block == 78 || !is_transparent(state->block)) { Imaging img_i = imaging_python_to_c(state->img); unsigned char ink[] = {0, 0, 0, 255 * self->edge_opacity}; int increment=0; if (state->block == 44) // half-step increment=6; else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) increment=9; if ((state->x == 15) && (state->up_right_blocks != Py_None)) { unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); if (side_block != state->block && is_transparent(side_block)) { ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); } } else if (state->x != 15) { unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); if (side_block != state->block && is_transparent(side_block)) { ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); } } // if y != 0 and blocks[x,y-1,z] == 0 // chunk boundries are annoying if ((state->y == 0) && (state->up_left_blocks != Py_None)) { unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); if (side_block != state->block && is_transparent(side_block)) { ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); } } else if (state->y != 0) { unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); if (side_block != state->block && is_transparent(side_block)) { // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); } } } }
unsigned char check_adjacent_blocks(RenderState *state, int x,int y,int z, unsigned char blockid) { /* * Generates a pseudo ancillary data for blocks that depend of * what are surrounded and don't have ancillary data. This * function is through generate_pseudo_data. * * This uses a binary number of 4 digits to encode the info. * The encode is: * * 0b1234: * Bit: 1 2 3 4 * Side: +x +y -x -y * Values: bit = 0 -> The corresponding side block has different blockid * bit = 1 -> The corresponding side block has same blockid * Example: if the bit1 is 1 that means that there is a block with * blockid in the side of the +x direction. */ unsigned char pdata=0; if (state->x == 15) { /* +x direction */ if (state->up_right_blocks != Py_None) { /* just in case we are in the end of the world */ if (getArrayByte3D(state->up_right_blocks, 0, y, z) == blockid) { pdata = pdata|(1 << 3); } } } else { if (getArrayByte3D(state->blocks, x + 1, y, z) == blockid) { pdata = pdata|(1 << 3); } } if (state->y == 15) { /* +y direction*/ if (state->right_blocks != Py_None) { if (getArrayByte3D(state->right_blocks, x, 0, z) == blockid) { pdata = pdata|(1 << 2); } } } else { if (getArrayByte3D(state->blocks, x, y + 1, z) == blockid) { pdata = pdata|(1 << 2); } } if (state->x == 0) { /* -x direction*/ if (state->left_blocks != Py_None) { if (getArrayByte3D(state->left_blocks, 15, y, z) == blockid) { pdata = pdata|(1 << 1); } } } else { if (getArrayByte3D(state->blocks, x - 1, y, z) == blockid) { pdata = pdata|(1 << 1); } } if (state->y == 0) { /* -y direction */ if (state->up_left_blocks != Py_None) { if (getArrayByte3D(state->up_left_blocks, x, 15, z) == blockid) { pdata = pdata|(1 << 0); } } } else { if (getArrayByte3D(state->blocks, x, y - 1, z) == blockid) { pdata = pdata|(1 << 0); } } return pdata; }
/* TODO triple check this to make sure reference counting is correct */ PyObject* chunk_render(PyObject *self, PyObject *args) { RenderState state; int xoff, yoff; PyObject *imgsize, *imgsize0_py, *imgsize1_py; int imgsize0, imgsize1; PyObject *blocks_py; PyObject *left_blocks_py; PyObject *right_blocks_py; PyObject *up_left_blocks_py; PyObject *up_right_blocks_py; RenderModeInterface *rendermode; void *rm_data; PyObject *t = NULL; if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &state.blockdata_expanded)) return NULL; /* fill in important modules */ state.textures = textures; state.chunk = chunk_mod; /* set up the render mode */ rendermode = get_render_mode(&state); rm_data = calloc(1, rendermode->data_size); if (rendermode->start(rm_data, &state)) { free(rm_data); return Py_BuildValue("i", "-1"); } /* get the image size */ imgsize = PyObject_GetAttrString(state.img, "size"); imgsize0_py = PySequence_GetItem(imgsize, 0); imgsize1_py = PySequence_GetItem(imgsize, 1); Py_DECREF(imgsize); imgsize0 = PyInt_AsLong(imgsize0_py); imgsize1 = PyInt_AsLong(imgsize1_py); Py_DECREF(imgsize0_py); Py_DECREF(imgsize1_py); /* get the block data directly from numpy: */ blocks_py = PyObject_GetAttrString(state.self, "blocks"); state.blocks = blocks_py; left_blocks_py = PyObject_GetAttrString(state.self, "left_blocks"); state.left_blocks = left_blocks_py; right_blocks_py = PyObject_GetAttrString(state.self, "right_blocks"); state.right_blocks = right_blocks_py; up_left_blocks_py = PyObject_GetAttrString(state.self, "up_left_blocks"); state.up_left_blocks = up_left_blocks_py; up_right_blocks_py = PyObject_GetAttrString(state.self, "up_right_blocks"); state.up_right_blocks = up_right_blocks_py; for (state.x = 15; state.x > -1; state.x--) { for (state.y = 0; state.y < 16; state.y++) { PyObject *blockid = NULL; /* set up the render coordinates */ state.imgx = xoff + state.x*12 + state.y*12; /* 128*12 -- offset for z direction, 15*6 -- offset for x */ state.imgy = yoff - state.x*6 + state.y*6 + 128*12 + 15*6; for (state.z = 0; state.z < 128; state.z++) { state.imgy -= 12; /* get blockid */ state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z); if (state.block == 0) { continue; } /* make sure we're rendering inside the image boundaries */ if ((state.imgx >= imgsize0 + 24) || (state.imgx <= -24)) { continue; } if ((state.imgy >= imgsize1 + 24) || (state.imgy <= -24)) { continue; } /* decref'd on replacement *and* at the end of the z for block */ if (blockid) { Py_DECREF(blockid); } blockid = PyInt_FromLong(state.block); // check for occlusion if (rendermode->occluded(rm_data, &state)) { continue; } // everything stored here will be a borrowed ref /* get the texture and mask from block type / ancil. data */ if (!PySequence_Contains(special_blocks, blockid)) { /* t = textures.blockmap[blockid] */ t = PyList_GetItem(blockmap, state.block); } else { PyObject *tmp; unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) { ancilData = generate_pseudo_data(&state, ancilData); } tmp = PyTuple_New(2); Py_INCREF(blockid); /* because SetItem steals */ PyTuple_SetItem(tmp, 0, blockid); PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); /* this is a borrowed reference. no need to decref */ t = PyDict_GetItem(specialblockmap, tmp); Py_DECREF(tmp); } /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { PyObject *src, *mask, *mask_light; src = PyTuple_GetItem(t, 0); mask = PyTuple_GetItem(t, 1); mask_light = PyTuple_GetItem(t, 2); if (mask == Py_None) mask = src; rendermode->draw(rm_data, &state, src, mask, mask_light); } } if (blockid) { Py_DECREF(blockid); blockid = NULL; } } } /* free up the rendermode info */ rendermode->finish(rm_data, &state); free(rm_data); Py_DECREF(blocks_py); Py_XDECREF(left_blocks_py); Py_XDECREF(right_blocks_py); Py_XDECREF(up_left_blocks_py); Py_XDECREF(up_right_blocks_py); return Py_BuildValue("i",2); }
unsigned char generate_pseudo_data(RenderState *state, unsigned char ancilData) { /* * Generates a fake ancillary data for blocks that are drawn * depending on what are surrounded. */ int x = state->x, y = state->y, z = state->z; unsigned char data = 0; if (state->block == 2) { /* grass */ /* return 0x10 if grass is covered in snow */ if (z < 127 && getArrayByte3D(state->blocks, x, y, z+1) == 78) return 0x10; return ancilData; } else if (state->block == 9) { /* water */ /* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */ if (ancilData == 0) { /* static water */ if ((z != 127) && (getArrayByte3D(state->blocks, x, y, z+1) == 9)) { data = 0; } else { data = 16; } return data; /* = 0b10000 */ } else if ((ancilData > 0) && (ancilData < 8)) { /* flowing water */ data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | 0x10; return data; } else if (ancilData >= 8) { /* falling water */ data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f); return data; } } else if (state->block == 85) { /* fences */ return check_adjacent_blocks(state, x, y, z, state->block); } else if (state->block == 55) { /* redstone */ /* three addiotional bit are added, one for on/off state, and * another two for going-up redstone wire in the same block * (connection with the level z+1) */ unsigned char above_level_data = 0, same_level_data = 0, below_level_data = 0, possibly_connected = 0, final_data = 0; /* check for air in z+1, no air = no connection with upper level */ if ((z != 127) && (getArrayByte3D(state->left_blocks, x, y, z) == 0)) { above_level_data = check_adjacent_blocks(state, x, y, z + 1, state->block); } /* else above_level_data = 0 */ /* check connection with same level */ same_level_data = check_adjacent_blocks(state, x, y, z, 55); /* check the posibility of connection with z-1 level, check for air */ possibly_connected = check_adjacent_blocks(state, x, y, z, 0); /* check connection with z-1 level */ if (z != 0) { below_level_data = check_adjacent_blocks(state, x, y, z - 1, state->block); } /* else below_level_data = 0 */ final_data = above_level_data | same_level_data | (below_level_data & possibly_connected); /* add the three bits */ if (ancilData > 0) { /* powered redstone wire */ final_data = final_data | 0x40; } if ((above_level_data & 0x01)) { /* draw top left going up redstonewire */ final_data = final_data | 0x20; } if ((above_level_data & 0x08)) { /* draw top right going up redstonewire */ final_data = final_data | 0x10; } return final_data; } else if (state-> block == 54) { /* chests */ /* the top 2 bits are used to store the type of chest * (single or double), the 2 bottom bits are used for * orientation, look textures.py for more information. */ /* if placed alone chests always face west, return 0 to make a * chest facing west */ unsigned char chest_data = 0, air_data = 0, final_data = 0; /* search for chests */ chest_data = check_adjacent_blocks(state, x, y, z, 54); /* search for air */ air_data = check_adjacent_blocks(state, x, y, z, 0); if (chest_data == 1) { /* another chest in the east */ final_data = final_data | 0x8; /* only can face to north or south */ if ( (air_data & 0x2) == 2 ) { final_data = final_data | 0x1; /* facing north */ } else { final_data = final_data | 0x3; /* facing south */ } } else if (chest_data == 2) { /* in the north */ final_data = final_data | 0x4; /* only can face to east or west */ if ( !((air_data & 0x4) == 4) ) { /* 0 = west */ final_data = final_data | 0x2; /* facing east */ } } else if (chest_data == 4) { /*in the west */ final_data = final_data | 0x4; if ( (air_data & 0x2) == 2 ) { final_data = final_data | 0x1; /* facing north */ } else { final_data = final_data | 0x3; /* facing south */ } } else if (chest_data == 8) { /*in the south */ final_data = final_data | 0x8; if ( !((air_data & 0x4) == 4) ) { final_data = final_data | 0x2; /* facing east */ } } else if (chest_data == 0) { /* Single chest, determine the orientation */ if ( ((air_data & 0x8) == 0) && ((air_data & 0x2) == 2) ) { /* block in +x and no block in -x */ final_data = final_data | 0x1; /* facing north */ } else if ( ((air_data & 0x2) == 0) && ((air_data & 0x8) == 8)) { final_data = final_data | 0x3; } else if ( ((air_data & 0x4) == 0) && ((air_data & 0x1) == 1)) { final_data = final_data | 0x2; } /* else, facing west, value = 0 */ } else { /* more than one adjacent chests! render as normal chest */ return 0; } return final_data; } else if (state->block == 90) { return check_adjacent_blocks(state, x, y, z, state->block); } return 0; }