void TopdownTileRenderer::renderChunk(const mc::Chunk& chunk, RGBAImage& tile, int dx, int dy) { // TODO implement preblit water render behavior int texture_size = images->getTextureSize(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { std::deque<RenderBlock> blocks; // TODO make this water thing a bit nicer bool in_water = false; int water = 0; mc::LocalBlockPos localpos(x, z, 0); //int height = chunk.getHeightAt(localpos); //localpos.y = height; localpos.y = -1; if (localpos.y >= mc::CHUNK_HEIGHT*16 || localpos.y < 0) localpos.y = mc::CHUNK_HEIGHT*16 - 1; uint16_t id = chunk.getBlockID(localpos); while (id == 0 && localpos.y > 0) { localpos.y--; id = chunk.getBlockID(localpos); } if (localpos.y < 0) continue; while (localpos.y >= 0) { mc::BlockPos globalpos = localpos.toGlobalPos(chunk.getPos()); id = chunk.getBlockID(localpos); if (id == 0) { in_water = false; localpos.y--; continue; } uint16_t data = chunk.getBlockData(localpos); bool is_water = (id == 8 || id == 9) && data == 0; if (render_mode->isHidden(globalpos, id, data)) { localpos.y--; continue; } if (is_water && !use_preblit_water) { if (is_water == in_water) { localpos.y--; continue; } in_water = is_water; } else if (use_preblit_water) { if (!is_water) water = 0; else { water++; if (water > images->getMaxWaterPreblit()) { auto it = blocks.begin(); while (it != blocks.end()) { auto current = it++; if (it == blocks.end() || (it->id != 8 && it->id != 9)) { RenderBlock& top = *current; // blocks.erase(current); top.id = 8; top.data = OPAQUE_WATER; top.block = images->getBlock(top.id, top.data); render_mode->draw(top.block, top.pos, top.id, top.data); // blocks.insert(current, top); break; } else { blocks.erase(current); } } break; } } } data = checkNeighbors(globalpos, id, data); RGBAImage block = images->getBlock(id, data); if (Biome::isBiomeBlock(id, data)) { block = images->getBiomeBlock(id, data, getBiomeOfBlock(globalpos, &chunk)); } render_mode->draw(block, globalpos, id, data); RenderBlock render_block; render_block.block = block; render_block.id = id; render_block.data = data; render_block.pos = globalpos; blocks.push_back(render_block); if (!images->isBlockTransparent(id, data)) break; localpos.y--; } while (blocks.size() > 0) { RenderBlock render_block = blocks.back(); tile.alphaBlit(render_block.block, dx + x*texture_size, dy + z*texture_size); blocks.pop_back(); } } } }
void IsometricTileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { // some vars, set correct image size int block_size = images->getBlockSize(); tile.setSize(getTileSize(), getTileSize()); // get the maximum count of water blocks // blitted about each over, until they are nearly opaque int max_water = images->getMaxWaterPreblit(); // all visible blocks which are rendered in this tile std::set<RenderBlock> blocks; // iterate over the highest blocks in the tile // we use as tile position tile_pos+tile_offset because the offset means that // we treat the tile position as tile_pos, but it's actually tile_pos+tile_offset for (TileTopBlockIterator it(tile_pos, block_size, tile_width); !it.end(); it.next()) { // water render behavior n1: // are we already in a row of water? bool in_water = false; // water render behavior n2: // water counter, how many water blocks are at the moment in this row? int water = 0; // the render block objects in our current block row std::set<RenderBlock> row_nodes; // then iterate over the blocks, which are on the tile at the same position, // beginning from the highest block for (BlockRowIterator block(it.current); !block.end(); block.next()) { // get current chunk position mc::ChunkPos current_chunk_pos(block.current); // check if current chunk is not null // and if the chunk wasn't replaced in the cache (i.e. position changed) if (current_chunk == nullptr || current_chunk->getPos() != current_chunk_pos) // get chunk if not //if (!state.world->hasChunkSection(current_chunk, block.current.y)) // continue; current_chunk = world->getChunk(current_chunk_pos); if (current_chunk == nullptr) { // here is nothing (= air), // so reset state if we are in water in_water = false; continue; } // get local block position mc::LocalBlockPos local(block.current); // now get block id uint16_t id = current_chunk->getBlockID(local); // air is completely transparent so continue if (id == 0) { in_water = false; continue; } // now get the block data uint16_t data = current_chunk->getBlockData(local); // check if the render mode hides this block if (render_mode->isHidden(block.current, id, data)) continue; bool is_water = (id == 8 || id == 9) && data == 0; if (is_water && !use_preblit_water) { // water render behavior n1: // render only the top sides of the water blocks // and darken the ground with the lighting data // used for lighting rendermode // if we are already in water, skip checking this water block if (is_water && in_water) continue; in_water = is_water; } else if (use_preblit_water) { // water render behavior n2: // render the top side of every water block // have also preblit water blocks to skip redundant alphablitting // no lighting is needed because the 'opaque-water-effect' // is created by blitting the top sides of the water blocks // one above the other if (!is_water) { // if not water, reset the counter water = 0; } else { water++; // when we have enough water in a row // we can stop searching more blocks // and replace the already added render blocks with a preblit water block if (water > max_water) { std::set<RenderBlock>::const_iterator it = row_nodes.begin(); // iterate through the render blocks in this row while (it != row_nodes.end()) { std::set<RenderBlock>::const_iterator current = it++; // check if we have reached the top most water block if (it == row_nodes.end() || (it->id != 8 && it->id != 9)) { RenderBlock top = *current; row_nodes.erase(current); // check for neighbors mc::Block south, west; south = getBlock(top.pos + mc::DIR_SOUTH); west = getBlock(top.pos + mc::DIR_WEST); id = 8; data = OPAQUE_WATER; bool neighbor_south = !south.isFullWater(); if (neighbor_south) // data |= DATA_SOUTH; data |= OPAQUE_WATER_SOUTH; bool neighbor_west = !west.isFullWater(); if (neighbor_west) // data |= DATA_WEST; data |= OPAQUE_WATER_WEST; // get image and replace the old render block with this //top.image = images->getOpaqueWater(neighbor_south, // neighbor_west); top.image = images->getBlock(id, data); // don't forget the render mode render_mode->draw(top.image, top.pos, id, data); row_nodes.insert(top); break; } else { // water render block row_nodes.erase(current); } } break; } } } // check for special data (neighbor related) // get block image, check for transparency, create render block... data = checkNeighbors(block.current, id, data); //if (is_water && (data & DATA_WEST) && (data & DATA_SOUTH)) // continue; RGBAImage image; bool transparent = images->isBlockTransparent(id, data); // check for biome data if (Biome::isBiomeBlock(id, data)) image = images->getBiomeBlock(id, data, getBiomeOfBlock(block.current, current_chunk)); else image = images->getBlock(id, data); RenderBlock node; node.x = it.draw_x; node.y = it.draw_y; node.pos = block.current; node.image = image; node.id = id; node.data = data; // let the render mode do their magic with the block image render_mode->draw(node.image, node.pos, id, data); // insert into current row row_nodes.insert(node); // if this block is not transparent, then break if (!transparent) break; } // iterate through the created render blocks for (std::set<RenderBlock>::const_iterator it = row_nodes.begin(); it != row_nodes.end(); ++it) { std::set<RenderBlock>::const_iterator next = it; next++; // insert render block to if (next == row_nodes.end()) { blocks.insert(*it); } else { // skip unnecessary leaves if (it->id == 18 && next->id == 18 && (next->data & 3) == (it->data & 3)) continue; blocks.insert(*it); } } } // now blit all blocks for (std::set<RenderBlock>::const_iterator it = blocks.begin(); it != blocks.end(); ++it) { tile.alphaBlit(it->image, it->x, it->y); } }