static void edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { PrimitiveEdgeLines *self = (PrimitiveEdgeLines *)data; /* Draw some edge lines! */ 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->opacity}; unsigned short side_block; int x = state->x, y = state->y, z = state->z; int increment=0; if (state->block == 44 && ((state->block_data & 0x8) == 0 )) // half-step BUT no upsidown half-step increment=6; else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) increment=9; /* +X side */ side_block = get_data(state, BLOCKS, x+1, y, z); if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x+1, y, z))) { 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); } /* -Z side */ side_block = get_data(state, BLOCKS, x, y, z-1); if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x, y, z-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); } } }
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; }
void texture_info::set_coloralphamode(SDL_Texture *texture_id, const render_color *color) { UINT32 sr = (UINT32)(255.0f * color->r); UINT32 sg = (UINT32)(255.0f * color->g); UINT32 sb = (UINT32)(255.0f * color->b); UINT32 sa = (UINT32)(255.0f * color->a); if (color->r >= 1.0f && color->g >= 1.0f && color->b >= 1.0f && is_opaque(color->a)) { SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF); SDL_SetTextureAlphaMod(texture_id, 0xFF); } /* coloring-only case */ else if (is_opaque(color->a)) { SDL_SetTextureColorMod(texture_id, sr, sg, sb); SDL_SetTextureAlphaMod(texture_id, 0xFF); } /* alpha and/or coloring case */ else if (!is_transparent(color->a)) { SDL_SetTextureColorMod(texture_id, sr, sg, sb); SDL_SetTextureAlphaMod(texture_id, sa); } else { SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF); SDL_SetTextureAlphaMod(texture_id, 0x00); } }
static void lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderPrimitiveLighting* self; int x, y, z; self = (RenderPrimitiveLighting *)data; x = state->x, y = state->y, z = state->z; if ((state->block == 9) || (state->block == 79)) { /* special case for water and ice */ /* looks like we need a new case for lighting, there are * blocks that are transparent for occlusion calculations and * need per-face shading if the face is drawn. */ if ((state->block_pdata & 16) == 16) { do_shading_with_mask(self, state, x, y+1, z, self->facemasks[0]); } if ((state->block_pdata & 2) == 2) { /* bottom left */ do_shading_with_mask(self, state, x-1, y, z, self->facemasks[1]); } if ((state->block_pdata & 4) == 4) { /* bottom right */ do_shading_with_mask(self, state, x, y, z+1, self->facemasks[2]); } /* leaves are transparent for occlusion calculations but they * per face-shading to look as in game */ } else if (is_transparent(state->block) && (state->block != 18)) { /* transparent: do shading on whole block */ do_shading_with_mask(self, state, x, y, z, mask_light); } else { /* opaque: do per-face shading */ do_shading_with_mask(self, state, x, y+1, z, self->facemasks[0]); do_shading_with_mask(self, state, x-1, y, z, self->facemasks[1]); do_shading_with_mask(self, state, x, y, z+1, self->facemasks[2]); } }
int to_system(Color color) { if (is_transparent(color)) return -1; else return makecol(getr(color), getg(color), getb(color)); }
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); } }
/* does per-face occlusion checking for do_shading_with_mask */ inline int lighting_is_face_occluded(RenderState *state, int skip_sides, int x, int y, int z) { /* first, check for occlusion if the block is in the local chunk */ if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 16) { unsigned short block = getArrayShort3D(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 1; } } else if (!skip_sides) { unsigned short block = get_data(state, BLOCKS, x, y, 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 1; } } return 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); }
inline unsigned char estimate_blocklevel(RenderPrimitiveLighting *self, RenderState *state, int x, int y, int z, int *authoratative) { /* placeholders for later data arrays, coordinates */ unsigned char block, blocklevel; unsigned int average_count = 0, average_gather = 0, coeff = 0; /* defaults to "guess" until told otherwise */ if (authoratative) *authoratative = 0; block = get_data(state, BLOCKS, x, y, 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) { coeff = estimate_blocklevel(self, state, x+dx, y+dy, z+dz, &auth); local_block = get_data(state, 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 = get_data(state, BLOCKLIGHT, x, y, z); /* no longer a guess */ if (!(block == 44 || block == 53 || block == 67 || block == 108 || block == 109) && authoratative) { *authoratative = 1; } return blocklevel; }
inline void get_lighting_color(RenderPrimitiveLighting *self, RenderState *state, int x, int y, int z, unsigned char *r, unsigned char *g, unsigned char *b) { /* placeholders for later data arrays, coordinates */ unsigned char block, skylevel, blocklevel; block = get_data(state, BLOCKS, x, y, z); skylevel = get_data(state, SKYLIGHT, x, y, z); blocklevel = get_data(state, BLOCKLIGHT, x, y, z); /* special half-step handling, stairs handling */ /* Anvil also needs to be here, blockid 145 */ if (block == 44 || block == 53 || block == 67 || block == 108 || block == 109 || block == 114 || block == 128 || block == 134 || block == 135 || block == 136 || block == 145 || block == 156) { unsigned int upper_block; /* stairs and half-blocks take the skylevel from the upper block if it's transparent */ 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 = get_data(state, BLOCKS, x, y + upper_counter, z); } while (upper_block == 44 || upper_block == 53 || upper_block == 67 || upper_block == 108 || upper_block == 109 || upper_block == 114 || upper_block == 128 || upper_block == 134 || upper_block == 135 || upper_block == 136 || upper_block == 156 ); if (is_transparent(upper_block)) { skylevel = get_data(state, SKYLIGHT, x, y + upper_counter, z); } else { 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! */ *r = 255; *g = 255; *b = 255; return; } self->calculate_light_color(self, MIN(skylevel, 15), MIN(blocklevel, 15), r, g, b); }
void exposed_faces( Map *map, int x, int y, int z, int *f1, int *f2, int *f3, int *f4, int *f5, int *f6) { *f1 = is_transparent(map_get(map, x - 1, y, z)); *f2 = is_transparent(map_get(map, x + 1, y, z)); *f3 = is_transparent(map_get(map, x, y + 1, z)); *f4 = is_transparent(map_get(map, x, y - 1, z)) && (y > 0); *f5 = is_transparent(map_get(map, x, y, z + 1)); *f6 = is_transparent(map_get(map, x, y, z - 1)); }
/* 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); }
//===========================================================================================================// // Utility Functions // //===========================================================================================================// void rex_sprite::flatten() { if (num_layers == 1) return; //Paint the last layer onto the second-to-last for (int i = 0; i < width*height; ++i) { rltk::vchar* overlay = get_tile(num_layers - 1, i); if (!is_transparent(overlay)) { *get_tile(num_layers - 2, i) = *overlay; } } //Remove the last layer --num_layers; //Recurse flatten(); }
void overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderPrimitiveOverlay *self = (RenderPrimitiveOverlay *)data; unsigned char r, g, b, a; unsigned short top_block; // 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; /* skip rendering the overlay if we can't see it */ top_block = get_data(state, BLOCKS, state->x, state->y+1, state->z); if (!is_transparent(top_block)) { return; } /* check to be sure this block is solid/fluid */ if (block_has_property(top_block, SOLID) || block_has_property(top_block, FLUID)) { /* top block is fluid or solid, skip drawing */ return; } /* check to be sure this block is solid/fluid */ if (!block_has_property(state->block, SOLID) && !block_has_property(state->block, FLUID)) { /* not fluid or solid, skip drawing the overlay */ return; } /* get our color info */ self->get_color(data, state, &r, &g, &b, &a); /* do the overlay */ if (a > 0) { alpha_over_full(state->img, self->white_color, self->facemask_top, a/255.f, state->imgx, state->imgy + increment, 0, 0); tint_with_mask(state->img, r, g, b, 255, self->facemask_top, state->imgx, state->imgy + increment, 0, 0); } }
static void rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { RenderModeLighting* self; int x, y, z; /* first, chain up */ rendermode_normal.draw(data, state, src, mask); self = (RenderModeLighting *)data; x = state->x, y = state->y, z = state->z; if (is_transparent(state->block)) { /* transparent: do shading on whole block */ do_shading_with_mask(self, state, x, y, z, mask); } else { /* opaque: do per-face shading */ do_shading_with_mask(self, state, x, y, z+1, self->facemasks[0]); do_shading_with_mask(self, state, x-1, y, z, self->facemasks[1]); do_shading_with_mask(self, state, x, y+1, z, self->facemasks[2]); } }
static void rendermode_smooth_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { int light_top = 1; int light_left = 1; int light_right = 1; RenderModeSmoothLighting *self = (RenderModeSmoothLighting *)data; /* special case for leaves, water 8, water 9 -- these are also smooth-lit! */ if (state->block != 18 && state->block != 8 && state->block != 9 && is_transparent(state->block)) { /* transparent blocks are rendered as usual, with flat lighting */ rendermode_lighting.draw(data, state, src, mask, mask_light); return; } /* non-transparent blocks get the special smooth treatment */ /* nothing special to do, but we do want to avoid vanilla * lighting mode draws */ rendermode_normal.draw(data, state, src, mask, mask_light); /* special code for water */ if (state->block == 9) { if (!(state->block_pdata & (1 << 4))) light_top = 0; if (!(state->block_pdata & (1 << 1))) light_left = 0; if (!(state->block_pdata & (1 << 2))) light_right = 0; } if (light_top) do_shading_with_rule(self, state, lighting_rules[FACE_TOP]); if (light_left) do_shading_with_rule(self, state, lighting_rules[FACE_LEFT]); if (light_right) do_shading_with_rule(self, state, lighting_rules[FACE_RIGHT]); }
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); }
void WorkerItem::computeChunk(World *world) { char *opaque = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(float)); char *light = (char *)calloc(XZ_SIZE * XZ_SIZE * Y_SIZE, sizeof(char)); char *highest = (char *)calloc(XZ_SIZE * XZ_SIZE, sizeof(char)); int ox = p * CHUNK_SIZE - CHUNK_SIZE - 1; int oy = -1; int oz = q * CHUNK_SIZE - CHUNK_SIZE - 1; // check for lights int has_light = 0; if (SHOW_LIGHTS) { for (int a = 0; a < 3; a++) { for (int b = 0; b < 3; b++) { Map *map = light_maps[a][b]; if (map && map->size) { has_light = 1; } } } } // populate opaque array for (int a = 0; a < 3; a++) { for (int b = 0; b < 3; b++) { Map *map = block_maps[a][b]; if (!map) { continue; } MAP_FOR_EACH(map, ex, ey, ez, ew) { int rawx = Map::getX(i); int rawy = Map::getY(i); int rawz = Map::getZ(i); int x2 = rawx + map->dx - ox; int y2 = rawy + map->dy - oy; int z2 = rawz + map->dz - oz; int x = 0; int y = 0; int z = 0; if(entry->e.computed) { x = x2; y = y2; z = z2; } int w = ew; // TODO: this should be unnecessary if (x < 0 || y < 0 || z < 0) { continue; } if (x >= XZ_SIZE || y >= Y_SIZE || z >= XZ_SIZE) { continue; } // END TODO opaque[XYZ(x, y, z)] = !is_transparent(w); if (opaque[XYZ(x, y, z)]) { highest[XZ(x, z)] = MAX(highest[XZ(x, z)], y); } } END_MAP_FOR_EACH; } }
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); } } } }
Array VoxelMesher::build(const VoxelBuffer &buffer, unsigned int channel, Vector3i min, Vector3i max) { uint64_t time_before = OS::get_singleton()->get_ticks_usec(); ERR_FAIL_COND_V(_library.is_null(), Array()); ERR_FAIL_COND_V(channel >= VoxelBuffer::MAX_CHANNELS, Array()); const VoxelLibrary &library = **_library; for (unsigned int i = 0; i < MAX_MATERIALS; ++i) { Arrays &a = _arrays[i]; a.positions.clear(); a.normals.clear(); a.uvs.clear(); a.colors.clear(); a.indices.clear(); } float baked_occlusion_darkness; if (_bake_occlusion) baked_occlusion_darkness = _baked_occlusion_darkness / 3.0; // The technique is Culled faces. // Could be improved with greedy meshing: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/ // However I don't feel it's worth it yet: // - Not so much gain for organic worlds with lots of texture variations // - Works well with cubes but not with any shape // - Slower // => Could be implemented in a separate class? // Data must be padded, hence the off-by-one Vector3i::sort_min_max(min, max); const Vector3i pad(1, 1, 1); min.clamp_to(pad, max); max.clamp_to(min, buffer.get_size() - pad); int index_offset = 0; // Iterate 3D padded data to extract voxel faces. // This is the most intensive job in this class, so all required data should be as fit as possible. // The buffer we receive MUST be dense (i.e not compressed, and channels allocated). // That means we can use raw pointers to voxel data inside instead of using the higher-level getters, // and then save a lot of time. uint8_t *type_buffer = buffer.get_channel_raw(Voxel::CHANNEL_TYPE); // _ // | \ // /\ \\ // / /|\\\ // | |\ \\\ // | \_\ \\| // | | ) // \ | | // \ / CRASH_COND(type_buffer == NULL); //CRASH_COND(memarr_len(type_buffer) != buffer.get_volume() * sizeof(uint8_t)); // Build lookup tables so to speed up voxel access. // These are values to add to an address in order to get given neighbor. int row_size = buffer.get_size().y; int deck_size = buffer.get_size().x * row_size; int side_neighbor_lut[Cube::SIDE_COUNT]; side_neighbor_lut[Cube::SIDE_LEFT] = row_size; side_neighbor_lut[Cube::SIDE_RIGHT] = -row_size; side_neighbor_lut[Cube::SIDE_BACK] = -deck_size; side_neighbor_lut[Cube::SIDE_FRONT] = deck_size; side_neighbor_lut[Cube::SIDE_BOTTOM] = -1; side_neighbor_lut[Cube::SIDE_TOP] = 1; int edge_neighbor_lut[Cube::EDGE_COUNT]; edge_neighbor_lut[Cube::EDGE_BOTTOM_BACK] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_BACK]; edge_neighbor_lut[Cube::EDGE_BOTTOM_FRONT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_FRONT]; edge_neighbor_lut[Cube::EDGE_BOTTOM_LEFT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_LEFT]; edge_neighbor_lut[Cube::EDGE_BOTTOM_RIGHT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_RIGHT]; edge_neighbor_lut[Cube::EDGE_BACK_LEFT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_LEFT]; edge_neighbor_lut[Cube::EDGE_BACK_RIGHT] = side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_RIGHT]; edge_neighbor_lut[Cube::EDGE_FRONT_LEFT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_LEFT]; edge_neighbor_lut[Cube::EDGE_FRONT_RIGHT] = side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_RIGHT]; edge_neighbor_lut[Cube::EDGE_TOP_BACK] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_BACK]; edge_neighbor_lut[Cube::EDGE_TOP_FRONT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_FRONT]; edge_neighbor_lut[Cube::EDGE_TOP_LEFT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_LEFT]; edge_neighbor_lut[Cube::EDGE_TOP_RIGHT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_RIGHT]; int corner_neighbor_lut[Cube::CORNER_COUNT]; corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_LEFT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_LEFT]; corner_neighbor_lut[Cube::CORNER_BOTTOM_BACK_RIGHT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_RIGHT]; corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_RIGHT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_RIGHT]; corner_neighbor_lut[Cube::CORNER_BOTTOM_FRONT_LEFT] = side_neighbor_lut[Cube::SIDE_BOTTOM] + side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_LEFT]; corner_neighbor_lut[Cube::CORNER_TOP_BACK_LEFT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_LEFT]; corner_neighbor_lut[Cube::CORNER_TOP_BACK_RIGHT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_BACK] + side_neighbor_lut[Cube::SIDE_RIGHT]; corner_neighbor_lut[Cube::CORNER_TOP_FRONT_RIGHT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_RIGHT]; corner_neighbor_lut[Cube::CORNER_TOP_FRONT_LEFT] = side_neighbor_lut[Cube::SIDE_TOP] + side_neighbor_lut[Cube::SIDE_FRONT] + side_neighbor_lut[Cube::SIDE_LEFT]; uint64_t time_prep = OS::get_singleton()->get_ticks_usec() - time_before; time_before = OS::get_singleton()->get_ticks_usec(); for (unsigned int z = min.z; z < max.z; ++z) { for (unsigned int x = min.x; x < max.x; ++x) { for (unsigned int y = min.y; y < max.y; ++y) { // min and max are chosen such that you can visit 1 neighbor away from the current voxel without size check // TODO In this intensive routine, there is a way to make voxel access fastest by getting a pointer to the channel, // and using offset lookup to get neighbors rather than going through get_voxel validations int voxel_index = y + x * row_size + z * deck_size; int voxel_id = type_buffer[voxel_index]; if (voxel_id != 0 && library.has_voxel(voxel_id)) { const Voxel &voxel = library.get_voxel_const(voxel_id); Arrays &arrays = _arrays[voxel.get_material_id()]; // Hybrid approach: extract cube faces and decimate those that aren't visible, // and still allow voxels to have geometry that is not a cube // Sides for (unsigned int side = 0; side < Cube::SIDE_COUNT; ++side) { const PoolVector<Vector3> &positions = voxel.get_model_side_positions(side); int vertex_count = positions.size(); if (vertex_count != 0) { int neighbor_voxel_id = type_buffer[voxel_index + side_neighbor_lut[side]]; // TODO Better face visibility test if (is_face_visible(library, voxel, neighbor_voxel_id)) { // The face is visible int shaded_corner[8] = { 0 }; if (_bake_occlusion) { // Combinatory solution for https://0fps.net/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/ for (unsigned int j = 0; j < 4; ++j) { unsigned int edge = Cube::g_side_edges[side][j]; int edge_neighbor_id = type_buffer[voxel_index + edge_neighbor_lut[edge]]; if (!is_transparent(library, edge_neighbor_id)) { shaded_corner[Cube::g_edge_corners[edge][0]] += 1; shaded_corner[Cube::g_edge_corners[edge][1]] += 1; } } for (unsigned int j = 0; j < 4; ++j) { unsigned int corner = Cube::g_side_corners[side][j]; if (shaded_corner[corner] == 2) { shaded_corner[corner] = 3; } else { int corner_neigbor_id = type_buffer[voxel_index + corner_neighbor_lut[corner]]; if (!is_transparent(library, corner_neigbor_id)) { shaded_corner[corner] += 1; } } } } PoolVector<Vector3>::Read rv = positions.read(); PoolVector<Vector2>::Read rt = voxel.get_model_side_uv(side).read(); // Subtracting 1 because the data is padded Vector3 pos(x - 1, y - 1, z - 1); // Append vertices of the faces in one go, don't use push_back { int append_index = arrays.positions.size(); arrays.positions.resize(arrays.positions.size() + vertex_count); Vector3 *w = arrays.positions.ptrw() + append_index; for (unsigned int i = 0; i < vertex_count; ++i) { w[i] = rv[i] + pos; } } { int append_index = arrays.uvs.size(); arrays.uvs.resize(arrays.uvs.size() + vertex_count); memcpy(arrays.uvs.ptrw() + append_index, rt.ptr(), vertex_count * sizeof(Vector2)); } { int append_index = arrays.normals.size(); arrays.normals.resize(arrays.normals.size() + vertex_count); Vector3 *w = arrays.normals.ptrw() + append_index; for (unsigned int i = 0; i < vertex_count; ++i) { w[i] = Cube::g_side_normals[side].to_vec3(); } } if (_bake_occlusion) { // Use color array int append_index = arrays.colors.size(); arrays.colors.resize(arrays.colors.size() + vertex_count); Color *w = arrays.colors.ptrw() + append_index; for (unsigned int i = 0; i < vertex_count; ++i) { Vector3 v = rv[i]; // General purpose occlusion colouring. // TODO Optimize for cubes // TODO Fix occlusion inconsistency caused by triangles orientation? Not sure if worth it float shade = 0; for (unsigned int j = 0; j < 4; ++j) { unsigned int corner = Cube::g_side_corners[side][j]; if (shaded_corner[corner]) { float s = baked_occlusion_darkness * static_cast<float>(shaded_corner[corner]); float k = 1.0 - Cube::g_corner_position[corner].distance_to(v); if (k < 0.0) k = 0.0; s *= k; if (s > shade) shade = s; } } float gs = 1.0 - shade; w[i] = Color(gs, gs, gs); } } const PoolVector<int> &side_indices = voxel.get_model_side_indices(side); PoolVector<int>::Read ri = side_indices.read(); unsigned int index_count = side_indices.size(); { int i = arrays.indices.size(); arrays.indices.resize(arrays.indices.size() + index_count); int *w = arrays.indices.ptrw(); for(unsigned int j = 0; j < index_count; ++j) { w[i++] = index_offset + ri[j]; } } index_offset += vertex_count; } } } // Inside if (voxel.get_model_positions().size() != 0) { // TODO Get rid of push_backs const PoolVector<Vector3> &vertices = voxel.get_model_positions(); int vertex_count = vertices.size(); PoolVector<Vector3>::Read rv = vertices.read(); PoolVector<Vector3>::Read rn = voxel.get_model_normals().read(); PoolVector<Vector2>::Read rt = voxel.get_model_uv().read(); Vector3 pos(x - 1, y - 1, z - 1); for (unsigned int i = 0; i < vertex_count; ++i) { arrays.normals.push_back(rn[i]); arrays.uvs.push_back(rt[i]); arrays.positions.push_back(rv[i] + pos); } if(_bake_occlusion) { // TODO handle ambient occlusion on inner parts arrays.colors.push_back(Color(1,1,1)); } const PoolVector<int> &indices = voxel.get_model_indices(); PoolVector<int>::Read ri = indices.read(); unsigned int index_count = indices.size(); for(unsigned int i = 0; i < index_count; ++i) { arrays.indices.push_back(index_offset + ri[i]); } index_offset += vertex_count; } } } } } uint64_t time_meshing = OS::get_singleton()->get_ticks_usec() - time_before; time_before = OS::get_singleton()->get_ticks_usec(); // Commit mesh // print_line(String("Made mesh v: ") + String::num(_arrays[0].positions.size()) // + String(", i: ") + String::num(_arrays[0].indices.size())); Array surfaces; // TODO We could return a single byte array and use Mesh::add_surface down the line? for (int i = 0; i < MAX_MATERIALS; ++i) { const Arrays &arrays = _arrays[i]; if (arrays.positions.size() != 0) { /*print_line("Arrays:"); for(int i = 0; i < arrays.positions.size(); ++i) print_line(String(" P {0}").format(varray(arrays.positions[i]))); for(int i = 0; i < arrays.normals.size(); ++i) print_line(String(" N {0}").format(varray(arrays.normals[i]))); for(int i = 0; i < arrays.uvs.size(); ++i) print_line(String(" UV {0}").format(varray(arrays.uvs[i])));*/ Array mesh_arrays; mesh_arrays.resize(Mesh::ARRAY_MAX); { PoolVector<Vector3> positions; PoolVector<Vector2> uvs; PoolVector<Vector3> normals; PoolVector<Color> colors; PoolVector<int> indices; raw_copy_to(positions, arrays.positions); raw_copy_to(uvs, arrays.uvs); raw_copy_to(normals, arrays.normals); raw_copy_to(colors, arrays.colors); raw_copy_to(indices, arrays.indices); mesh_arrays[Mesh::ARRAY_VERTEX] = positions; mesh_arrays[Mesh::ARRAY_TEX_UV] = uvs; mesh_arrays[Mesh::ARRAY_NORMAL] = normals; mesh_arrays[Mesh::ARRAY_COLOR] = colors; mesh_arrays[Mesh::ARRAY_INDEX] = indices; } surfaces.append(mesh_arrays); } } uint64_t time_commit = OS::get_singleton()->get_ticks_usec() - time_before; //print_line(String("P: {0}, M: {1}, C: {2}").format(varray(time_prep, time_meshing, time_commit))); return surfaces; }
gfx::Size Graphics::drawStringAlgorithm(const std::string& str, Color fg, Color bg, const gfx::Rect& rc, int align, bool draw) { gfx::Point pt(0, rc.y); if ((align & (JI_MIDDLE | JI_BOTTOM)) != 0) { gfx::Size preSize = drawStringAlgorithm(str, ColorNone, ColorNone, rc, 0, false); if (align & JI_MIDDLE) pt.y = rc.y + rc.h/2 - preSize.h/2; else if (align & JI_BOTTOM) pt.y = rc.y + rc.h - preSize.h; } gfx::Size calculatedSize(0, 0); size_t beg, end, new_word_beg, old_end; std::string line; // Draw line-by-line for (beg=end=0; end != std::string::npos; ) { pt.x = rc.x; // Without word-wrap if ((align & JI_WORDWRAP) == 0) { end = str.find('\n', beg); } // With word-wrap else { old_end = std::string::npos; for (new_word_beg=beg;;) { end = str.find_first_of(" \n", new_word_beg); // If we have already a word to print (old_end != npos), and // we are out of the available width (rc.w) using the new "end", if ((old_end != std::string::npos) && (pt.x+measureString(str.substr(beg, end-beg)).w > rc.w)) { // We go back to the "old_end" and paint from "beg" to "end" end = old_end; break; } // If we have more words to print... else if (end != std::string::npos) { // Force line break, now we have to paint from "beg" to "end" if (str[end] == '\n') break; // White-space, this is a beginning of a new word. new_word_beg = end+1; } // We are in the end of text else break; old_end = end; } } // Get the entire line to be painted line = str.substr(beg, end-beg); gfx::Size lineSize = measureString(line); calculatedSize.w = MAX(calculatedSize.w, lineSize.w); // Render the text if (draw) { int xout; if ((align & JI_CENTER) == JI_CENTER) xout = pt.x + rc.w/2 - lineSize.w/2; else if ((align & JI_RIGHT) == JI_RIGHT) xout = pt.x + rc.w - lineSize.w; else xout = pt.x; ji_font_set_aa_mode(m_currentFont, to_system(bg)); textout_ex(m_bmp, m_currentFont, line.c_str(), m_dx+xout, m_dy+pt.y, to_system(fg), to_system(bg)); if (!is_transparent(bg)) fillAreaBetweenRects(bg, gfx::Rect(rc.x, pt.y, rc.w, lineSize.h), gfx::Rect(xout, pt.y, lineSize.w, lineSize.h)); } pt.y += lineSize.h; calculatedSize.h += lineSize.h; beg = end+1; } // Fill bottom area if (draw && !is_transparent(bg)) { if (pt.y < rc.y+rc.h) fillRect(bg, gfx::Rect(rc.x, pt.y, rc.w, rc.y+rc.h-pt.y)); } return calculatedSize; }
/* 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); }
bool LOSfinder::bresen_y(PosPoint source, PosPoint target) { int x = source.posx; int error = 0; int delta_y = std::abs(source.posy - target.posy); int deltaerr = std::abs(source.posx - target.posx); int deltastep = sign(target.posx - source.posx); int incrstep = sign(target.posy - source.posy); for (int y = source.posy; y != target.posy; y += incrstep) { if ((x % RAY_MULTIPLIER) == 0 && (y % RAY_MULTIPLIER) == 0) { // when in corner check side neighbours // if both of them are not transparent then corner is not transparent PosPoint left_neighbour(x + deltastep, y, source.posz); PosPoint right_neighbour(x, y + incrstep, source.posz); if (!is_transparent(left_neighbour) && !is_transparent(right_neighbour)) { return false; } } else if (x % RAY_MULTIPLIER == 0) { // when ray hits an edge check both tiles. Since ray travels through edge both of them // must be transparent PosPoint left_neighbour(x - deltastep, y, source.posz); PosPoint right_neighbour(x + deltastep, y, source.posz); if (!is_transparent(left_neighbour) || !is_transparent(right_neighbour)) { return false; } } else if (y % RAY_MULTIPLIER == 0) { // second case of edge handling PosPoint left_neighbour(x, y - incrstep, source.posz); PosPoint right_neighbour(x, y + incrstep, source.posz); if (!is_transparent(left_neighbour) || !is_transparent(right_neighbour)) { return false; } } else { PosPoint new_point(x, y, source.posz); if (!is_transparent(new_point)) { return false; } } error += deltaerr; if (error >= delta_y) { x += deltastep; error -= delta_y; } } return true; }