/* Copyright (c) 2013-2015 Jesper Öqvist <jesper@llbit.se> * * This file is part of Chunky. * * Chunky is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Chunky is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Chunky. If not, see <http://www.gnu.org/licenses/>. */ package se.llbit.chunky.renderer.scene; import se.llbit.chunky.model.WaterModel; import se.llbit.chunky.world.Block; import se.llbit.chunky.world.BlockData; import se.llbit.chunky.world.Chunk; import se.llbit.chunky.world.ChunkPosition; import se.llbit.math.Octree; import se.llbit.math.Vector3i; /** * Processes the Octree after it has been loaded and updates block states for * blocks that depend on neighbor blocks. Octree finalization is be done after * all chunks have been loaded because before then we can't reliably test for * neighbor blocks. * * @author Jesper Öqvist <jesper@llbit.se> */ public class OctreeFinalizer { /** * Finalize a chunk in the octree. * * @param octree Octree to finalize * @param origin Origin of the octree * @param cp Position of the chunk to finalize */ public static void finalizeChunk(Octree octree, Vector3i origin, ChunkPosition cp) { for (int cy = 0 - origin.y; cy < Chunk.Y_MAX - origin.y; ++cy) { for (int cz = 0; cz < 16; ++cz) { int z = cz + cp.z * 16 - origin.z; for (int cx = 0; cx < 16; ++cx) { int x = cx + cp.x * 16 - origin.x; int type = octree.get(x, cy, z); Block block = Block.get(type); // Set non-visible blocks to be stone, in order to merge large patches. if ((cx == 0 || cx == 15 || cz == 0 || cz == 15) && cy > -origin.y && cy < Chunk.Y_MAX - origin.y - 1 && type != Block.STONE_ID && block.isOpaque) { if (Block.get(octree.get(x - 1, cy, z)).isOpaque && Block .get(octree.get(x + 1, cy, z)).isOpaque && Block .get(octree.get(x, cy - 1, z)).isOpaque && Block .get(octree.get(x, cy + 1, z)).isOpaque && Block .get(octree.get(x, cy, z - 1)).isOpaque && Block .get(octree.get(x, cy, z + 1)).isOpaque) { octree.set(Block.STONE_ID, x, cy, z); continue; } } int fullBlock; int data; int level0; int level; int corner0; int corner1; int corner2; int corner3; int connections; int dir; int bd; int bd_alt; int tex; int otherId; Block other; Block other_alt; Block above; Block west; Block east; Block north; Block south; switch (block.id) { case Block.LARGE_FLOWER_ID: data = type >> BlockData.OFFSET; if ((data & 8) != 0) { // Get flower kind from block beneath. int kind = (octree.get(x, cy - 1, z) >> BlockData.OFFSET) & 7; type = (type & ~(15 << BlockData.OFFSET)) | ((8 | kind) << BlockData.OFFSET); octree.set(type, x, cy, z); } break; case Block.WATER_ID: fullBlock = (type >> WaterModel.FULL_BLOCK) & 1; if (fullBlock != 0) break; level0 = 8 - (0xF & (type >> 8)); corner0 = level0; corner1 = level0; corner2 = level0; corner3 = level0; data = octree.get(x - 1, cy, z); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner3 += level; corner0 += level; data = octree.get(x - 1, cy, z + 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner0 += level; data = octree.get(x, cy, z + 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner0 += level; corner1 += level; data = octree.get(x + 1, cy, z + 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner1 += level; data = octree.get(x + 1, cy, z); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner1 += level; corner2 += level; data = octree.get(x + 1, cy, z - 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner2 += level; data = octree.get(x, cy, z - 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner2 += level; corner3 += level; data = octree.get(x - 1, cy, z - 1); level = level0; if ((data & 0xFF) == Block.WATER_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner3 += level; corner0 = Math.min(7, 8 - (corner0 / 4)); corner1 = Math.min(7, 8 - (corner1 / 4)); corner2 = Math.min(7, 8 - (corner2 / 4)); corner3 = Math.min(7, 8 - (corner3 / 4)); type |= (corner0 << 16); type |= (corner1 << 20); type |= (corner2 << 24); type |= (corner3 << 28); octree.set(type, x, cy, z); break; case Block.LAVA_ID: fullBlock = (type >> WaterModel.FULL_BLOCK) & 1; if (fullBlock != 0) break; level0 = 8 - (0xF & (type >> 8)); corner0 = level0; corner1 = level0; corner2 = level0; corner3 = level0; data = octree.get(x - 1, cy, z); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner3 += level; corner0 += level; data = octree.get(x - 1, cy, z + 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner0 += level; data = octree.get(x, cy, z + 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner0 += level; corner1 += level; data = octree.get(x + 1, cy, z + 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner1 += level; data = octree.get(x + 1, cy, z); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner1 += level; corner2 += level; data = octree.get(x + 1, cy, z - 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner2 += level; data = octree.get(x, cy, z - 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner2 += level; corner3 += level; data = octree.get(x - 1, cy, z - 1); level = level0; if ((data & 0xFF) == Block.LAVA_ID) { fullBlock = (data >> WaterModel.FULL_BLOCK) & 1; level = 8 - (1 - fullBlock) * (7 & (data >> 8)); } else if (!Block.get(data).isSolid) { level = 0; } corner3 += level; corner0 = Math.min(7, 8 - (corner0 / 4)); corner1 = Math.min(7, 8 - (corner1 / 4)); corner2 = Math.min(7, 8 - (corner2 / 4)); corner3 = Math.min(7, 8 - (corner3 / 4)); type |= (corner0 << 16); type |= (corner1 << 20); type |= (corner2 << 24); type |= (corner3 << 28); octree.set(type, x, cy, z); break; case Block.TRIPWIRE_ID: otherId = 0xFF & octree.get(x - 1, cy, z); if (otherId == Block.TRIPWIRE_ID || otherId == Block.TRIPWIREHOOK_ID) { type |= 1 << 12; } else { otherId = 0xFF & octree.get(x + 1, cy, z); if (otherId == Block.TRIPWIRE_ID || otherId == Block.TRIPWIREHOOK_ID) { type |= 1 << 12; } } octree.set(type, x, cy, z); break; case Block.REDSTONEWIRE_ID: above = Block.get(octree.get(x, cy + 1, z)); west = Block.get(octree.get(x - 1, cy, z)); east = Block.get(octree.get(x + 1, cy, z)); north = Block.get(octree.get(x, cy, z - 1)); south = Block.get(octree.get(x, cy, z + 1)); if (above == Block.AIR) { int westAbove = 0xFF & octree.get(x - 1, cy + 1, z); if (west.isSolid && westAbove == Block.REDSTONEWIRE_ID) { // Wire on west block side. type |= 1 << BlockData.RSW_WEST_CONNECTION; type |= 1 << BlockData.RSW_WEST_SIDE; } int eastAbove = 0xFF & octree.get(x + 1, cy + 1, z); if (east.isSolid && eastAbove == Block.REDSTONEWIRE_ID) { // Wire on east block side. type |= 1 << BlockData.RSW_EAST_CONNECTION; type |= 1 << BlockData.RSW_EAST_SIDE; } int northAbove = 0xFF & octree.get(x, cy + 1, z - 1); if (north.isSolid && northAbove == Block.REDSTONEWIRE_ID) { // Wire on north block side. type |= 1 << BlockData.RSW_NORTH_CONNECTION; type |= 1 << BlockData.RSW_NORTH_SIDE; } int southAbove = 0xFF & octree.get(x, cy + 1, z + 1); if (south.isSolid && southAbove == Block.REDSTONEWIRE_ID) { // Wire on south block side. type |= 1 << BlockData.RSW_SOUTH_CONNECTION; type |= 1 << BlockData.RSW_SOUTH_SIDE; } } if (west.isRedstoneWireConnector()) { type |= 1 << BlockData.RSW_WEST_CONNECTION; } else if (west == Block.AIR) { int westBelow = 0xFF & octree.get(x - 1, cy - 1, z); if (westBelow == Block.REDSTONEWIRE_ID) { type |= 1 << BlockData.RSW_WEST_CONNECTION; } } if (east.isRedstoneWireConnector()) { type |= 1 << BlockData.RSW_EAST_CONNECTION; } else if (east == Block.AIR) { int eastBelow = 0xFF & octree.get(x + 1, cy - 1, z); if (eastBelow == Block.REDSTONEWIRE_ID) { type |= 1 << BlockData.RSW_EAST_CONNECTION; } } if (north.isRedstoneWireConnector()) { type |= 1 << BlockData.RSW_NORTH_CONNECTION; } else if (north == Block.AIR) { int northBelow = 0xFF & octree.get(x, cy - 1, z - 1); if (northBelow == Block.REDSTONEWIRE_ID) { type |= 1 << BlockData.RSW_NORTH_CONNECTION; } } if (south.isRedstoneWireConnector()) { type |= 1 << BlockData.RSW_SOUTH_CONNECTION; } else if (south == Block.AIR) { int southBelow = 0xFF & octree.get(x, cy - 1, z + 1); if (southBelow == Block.REDSTONEWIRE_ID) { type |= 1 << BlockData.RSW_SOUTH_CONNECTION; } } octree.set(type, x, cy, z); break; case Block.MELONSTEM_ID: if ((0xFF & octree.get(x - 1, cy, z)) == Block.MELON_ID) { type |= 1 << 16; } else if ((0xFF & octree.get(x + 1, cy, z)) == Block.MELON_ID) { type |= 2 << 16; } else if ((0xFF & octree.get(x, cy, z - 1)) == Block.MELON_ID) { type |= 3 << 16; } else if ((0xFF & octree.get(x, cy, z + 1)) == Block.MELON_ID) { type |= 4 << 16; } octree.set(type, x, cy, z); break; case Block.PUMPKINSTEM_ID: if ((0xFF & octree.get(x - 1, cy, z)) == Block.PUMPKIN_ID) { type |= 1 << 16; } else if ((0xFF & octree.get(x + 1, cy, z)) == Block.PUMPKIN_ID) { type |= 2 << 16; } else if ((0xFF & octree.get(x, cy, z - 1)) == Block.PUMPKIN_ID) { type |= 3 << 16; } else if ((0xFF & octree.get(x, cy, z + 1)) == Block.PUMPKIN_ID) { type |= 4 << 16; } octree.set(type, x, cy, z); break; case Block.TRAPPEDCHEST_ID: dir = type >> 8; tex = 0; if (dir < 4) { if ((0xFF & octree.get(x - 1, cy, z)) == Block.TRAPPEDCHEST_ID) { tex = 1 + (dir - 1) % 2; } else if ((0xFF & octree.get(x + 1, cy, z)) == Block.TRAPPEDCHEST_ID) { tex = 1 + dir % 2; } } else { if ((0xFF & octree.get(x, cy, z - 1)) == Block.TRAPPEDCHEST_ID) { tex = 1 + dir % 2; } else if ((0xFF & octree.get(x, cy, z + 1)) == Block.TRAPPEDCHEST_ID) { tex = 1 + (dir - 1) % 2; } } type |= tex << 16; octree.set(type, x, cy, z); break; case Block.CHEST_ID: dir = type >> 8; tex = 0; if (dir < 4) { if ((0xFF & octree.get(x - 1, cy, z)) == Block.CHEST_ID) { tex = 1 + (dir - 1) % 2; } else if ((0xFF & octree.get(x + 1, cy, z)) == Block.CHEST_ID) { tex = 1 + dir % 2; } } else { if ((0xFF & octree.get(x, cy, z - 1)) == Block.CHEST_ID) { tex = 1 + dir % 2; } else if ((0xFF & octree.get(x, cy, z + 1)) == Block.CHEST_ID) { tex = 1 + (dir - 1) % 2; } } type |= tex << 16; octree.set(type, x, cy, z); break; case Block.IRONBARS_ID: other = Block.get(octree.get(x, cy, z - 1)); if (other.isIronBarsConnector()) { type |= BlockData.CONNECTED_NORTH << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isIronBarsConnector()) { type |= BlockData.CONNECTED_SOUTH << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isIronBarsConnector()) { type |= BlockData.CONNECTED_EAST << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isIronBarsConnector()) { type |= BlockData.CONNECTED_WEST << BlockData.GLASS_PANE_OFFSET; } octree.set(type, x, cy, z); break; case Block.GLASSPANE_ID: case Block.STAINED_GLASSPANE_ID: other = Block.get(octree.get(x, cy, z - 1)); if (other.isGlassPaneConnector()) { type |= BlockData.CONNECTED_NORTH << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isGlassPaneConnector()) { type |= BlockData.CONNECTED_SOUTH << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isGlassPaneConnector()) { type |= BlockData.CONNECTED_EAST << BlockData.GLASS_PANE_OFFSET; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isGlassPaneConnector()) { type |= BlockData.CONNECTED_WEST << BlockData.GLASS_PANE_OFFSET; } octree.set(type, x, cy, z); break; case Block.STONEWALL_ID: connections = 0; other = Block.get(octree.get(x, cy, z - 1)); if (other.isStoneWallConnector()) { connections |= BlockData.CONNECTED_NORTH; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isStoneWallConnector()) { connections |= BlockData.CONNECTED_SOUTH; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isStoneWallConnector()) { connections |= BlockData.CONNECTED_EAST; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isStoneWallConnector()) { connections |= BlockData.CONNECTED_WEST; } type |= connections << BlockData.STONEWALL_CONN; if (connections != 3 && connections != 12) { type |= 1 << BlockData.STONEWALL_CORNER; } else if (cy + 1 < Chunk.Y_MAX) { otherId = (0xFF & octree.get(x, cy + 1, z)); if (otherId == Block.TORCH_ID || otherId == Block.REDSTONETORCHON_ID || otherId == Block.REDSTONETORCHOFF_ID) { type |= 1 << BlockData.STONEWALL_CORNER; } } octree.set(type, x, cy, z); break; case Block.FENCE_ID: case Block.SPRUCEFENCE_ID: case Block.BIRCHFENCE_ID: case Block.JUNGLEFENCE_ID: case Block.DARKOAKFENCE_ID: case Block.ACACIAFENCE_ID: other = Block.get(octree.get(x, cy, z - 1)); if (other.isFenceConnector()) { type |= BlockData.CONNECTED_NORTH << BlockData.OFFSET; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isFenceConnector()) { type |= BlockData.CONNECTED_SOUTH << BlockData.OFFSET; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isFenceConnector()) { type |= BlockData.CONNECTED_EAST << BlockData.OFFSET; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isFenceConnector()) { type |= BlockData.CONNECTED_WEST << BlockData.OFFSET; } octree.set(type, x, cy, z); break; case Block.NETHERBRICKFENCE_ID: other = Block.get(octree.get(x, cy, z - 1)); if (other.isNetherBrickFenceConnector()) { type |= BlockData.CONNECTED_NORTH << BlockData.OFFSET; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isNetherBrickFenceConnector()) { type |= BlockData.CONNECTED_SOUTH << BlockData.OFFSET; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isNetherBrickFenceConnector()) { type |= BlockData.CONNECTED_EAST << BlockData.OFFSET; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isNetherBrickFenceConnector()) { type |= BlockData.CONNECTED_WEST << BlockData.OFFSET; } octree.set(type, x, cy, z); break; case Block.FENCEGATE_ID: case Block.SPRUCEFENCEGATE_ID: case Block.BIRCHFENCEGATE_ID: case Block.JUNGLEFENCEGATE_ID: case Block.DARKOAKFENCEGATE_ID: case Block.ACACIAFENCEGATE_ID: dir = 3 & (type >> BlockData.OFFSET); if (dir == 0 || dir == 2) { // facing north or south int westId = (0xFF & octree.get(x - 1, cy, z)); int eastId = (0xFF & octree.get(x + 1, cy, z)); if (westId == Block.STONEWALL_ID && eastId == Block.STONEWALL_ID) { type |= 1 << BlockData.FENCEGATE_LOW; octree.set(type, x, cy, z); } } else { // facing east or west int northId = (0xFF & octree.get(x, cy, z - 1)); int southId = (0xFF & octree.get(x, cy, z + 1)); if (northId == Block.STONEWALL_ID && southId == Block.STONEWALL_ID) { type |= 1 << BlockData.FENCEGATE_LOW; octree.set(type, x, cy, z); } } break; case Block.OAKWOODSTAIRS_ID: case Block.STONESTAIRS_ID: case Block.BRICKSTAIRS_ID: case Block.STONEBRICKSTAIRS_ID: case Block.NETHERBRICKSTAIRS_ID: case Block.SANDSTONESTAIRS_ID: case Block.SPRUCEWOODSTAIRS_ID: case Block.BIRCHWOODSTAIRS_ID: case Block.JUNGLEWOODSTAIRS_ID: case Block.QUARTZSTAIRS_ID: case Block.ACACIASTAIRS_ID: case Block.DARKOAKSTAIRS_ID: // all corner notation (s-e, n-w, etc) is indicating gradient // check if this is a corner stair block int stairdata = type >> BlockData.OFFSET; int rotation = 3 & stairdata; int upsidedown = type & BlockData.UPSIDE_DOWN_STAIR; switch (rotation) { case 0: // ascending east bd = octree.get(x + 1, cy, z);// behind other = Block.get(bd); bd_alt = octree.get(x - 1, cy, z);// in front of other_alt = Block.get(bd_alt); if (other.isStair() && (bd & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd >> BlockData.OFFSET)) { case 2: // if stair behind ascends south we have outer s-e corner // unless stair to the left has same orientation if (!sameStair(octree, type, x, cy, z - 1)) { type |= BlockData.SOUTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 3: // if stair behind ascends north we have n-e corner // unless stair to the right has same orientation if (!sameStair(octree, type, x, cy, z + 1)) { type |= BlockData.NORTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } else if (other_alt.isStair() && (bd_alt & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd_alt >> BlockData.OFFSET)) { case 2: // if stair in front ascends south we have inner s-e corner // unless stair to the right has same orientation if (!sameStair(octree, type, x, cy, z + 1)) { type |= BlockData.INNER_SOUTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 3: // if stair in front ascends north we have inner n-e corner // unless stair to the left has same orientation if (!sameStair(octree, type, x, cy, z - 1)) { type |= BlockData.INNER_NORTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } break; case 1: // ascending west bd = octree.get(x - 1, cy, z);// behind other = Block.get(bd); bd_alt = octree.get(x + 1, cy, z);// in front of other_alt = Block.get(bd_alt); if (other.isStair() && (bd & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd >> BlockData.OFFSET)) { case 2: // if stair behind ascends south we have outer s-w corner // unless stair to the right has same orientation if (!sameStair(octree, type, x, cy, z - 1)) { type |= BlockData.SOUTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 3: // if stair behind ascends north we have outer n-w corner // unless stair to the left has same orientation if (!sameStair(octree, type, x, cy, z + 1)) { type |= BlockData.NORTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } else if (other_alt.isStair() && (bd_alt & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd_alt >> BlockData.OFFSET)) { case 2: // if stair in front ascends south we have inner s-w corner // unless stair to the left has same orientation if (!sameStair(octree, type, x, cy, z + 1)) { type |= BlockData.INNER_SOUTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 3: // if stair in front ascends north we have inner n-w corner // unless stair to the right has same orientation if (!sameStair(octree, type, x, cy, z - 1)) { type |= BlockData.INNER_NORTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } break; case 2: // ascending south bd = octree.get(x, cy, z + 1);// behind other = Block.get(bd); bd_alt = octree.get(x, cy, z - 1);// in front of other_alt = Block.get(bd_alt); if (other.isStair() && (bd & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd >> BlockData.OFFSET)) { case 0: // if stair behind ascends east we have outer s-e corner if (!sameStair(octree, type, x - 1, cy, z)) { type |= BlockData.SOUTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 1: // if stair behind ascends west we have outer s-w corner if (!sameStair(octree, type, x + 1, cy, z)) { type |= BlockData.SOUTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } else if (other_alt.isStair() && (bd_alt & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd_alt >> BlockData.OFFSET)) { case 0: // if stair in front ascends east we have inner s-e corner if (!sameStair(octree, type, x + 1, cy, z)) { type |= BlockData.INNER_SOUTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 1: // if stair in front ascends west we have inner s-w corner if (!sameStair(octree, type, x - 1, cy, z)) { type |= BlockData.INNER_SOUTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } break; case 3: // Ascending north. bd = octree.get(x, cy, z - 1); // Behind. other = Block.get(bd); bd_alt = octree.get(x, cy, z + 1); // In front of. other_alt = Block.get(bd_alt); if (other.isStair() && (bd & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd >> BlockData.OFFSET)) { case 0: // If stair behind ascends east we have outer n-e corner. if (!sameStair(octree, type, x - 1, cy, z)) { type |= BlockData.NORTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 1: // If stair behind ascends west we have outer n-w corner. if (!sameStair(octree, type, x + 1, cy, z)) { type |= BlockData.NORTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } else if (other_alt.isStair() && (bd_alt & BlockData.UPSIDE_DOWN_STAIR) == upsidedown) { switch (3 & (bd_alt >> BlockData.OFFSET)) { case 0: // If stair in front ascends east we have inner n-e corner. if (!sameStair(octree, type, x + 1, cy, z)) { type |= BlockData.INNER_NORTH_EAST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; case 1: // If stair in front ascends west we have inner n-w corner. if (!sameStair(octree, type, x - 1, cy, z)) { type |= BlockData.INNER_NORTH_WEST << BlockData.CORNER_OFFSET; octree.set(type, x, cy, z); } break; } } break; } break; case Block.CHORUSPLANT_ID: other = Block.get(octree.get(x, cy, z - 1)); if (other.isChorusPlant()) { type |= BlockData.CONNECTED_NORTH << BlockData.OFFSET; } other = Block.get(octree.get(x, cy, z + 1)); if (other.isChorusPlant()) { type |= BlockData.CONNECTED_SOUTH << BlockData.OFFSET; } other = Block.get(octree.get(x + 1, cy, z)); if (other.isChorusPlant()) { type |= BlockData.CONNECTED_EAST << BlockData.OFFSET; } other = Block.get(octree.get(x - 1, cy, z)); if (other.isChorusPlant()) { type |= BlockData.CONNECTED_WEST << BlockData.OFFSET; } other = Block.get(octree.get(x, cy + 1, z)); if (other.isChorusPlant()) { type |= BlockData.CONNECTED_ABOVE << BlockData.OFFSET; } other = Block.get(octree.get(x, cy - 1, z)); if (other.isChorusPlant() || other.id == Block.ENDSTONE_ID) { type |= BlockData.CONNECTED_BELOW << BlockData.OFFSET; } octree.set(type, x, cy, z); break; default: break; } } } } } /** * Check if this stair type is the same as the other stair block. */ private static boolean sameStair(Octree octree, int type, int x, int y, int z) { int id = octree.get(x, y, z); return Block.get(id).isStair() && (type & (7 << 8)) == (id & (7 << 8)); } }