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);
}
Пример #11
0
/* 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);
            }
        }
    }
}
Пример #13
0
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;
}
Пример #14
0
/* 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);
}
Пример #15
0
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;

}