/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.pepsoft.worldpainter.exporting; import org.pepsoft.minecraft.ChunkFactory; import org.pepsoft.minecraft.ChunkImpl; import org.pepsoft.minecraft.ChunkImpl2; import org.pepsoft.minecraft.Material; import org.pepsoft.util.PerlinNoise; import org.pepsoft.worldpainter.Dimension; import org.pepsoft.worldpainter.Terrain; import org.pepsoft.worldpainter.Tile; import org.pepsoft.worldpainter.layers.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Random; import java.util.Set; import static org.pepsoft.minecraft.Constants.*; import static org.pepsoft.worldpainter.Constants.*; import static org.pepsoft.worldpainter.biomeschemes.Minecraft1_7Biomes.*; /** * * @author pepijn */ public class WorldPainterChunkFactory implements ChunkFactory { public WorldPainterChunkFactory(Dimension dimension, Map<Layer, LayerExporter> exporters, int version, int maxHeight) { if ((version != SUPPORTED_VERSION_1) && (version != SUPPORTED_VERSION_2)) { throw new IllegalArgumentException("Not a supported version: 0x" + Integer.toHexString(version)); } this.dimension = dimension; this.exporters = exporters; this.version = version; this.maxHeight = maxHeight; minimumLayers = dimension.getMinimumLayers(); } @Override public int getMaxHeight() { return maxHeight; } @Override public ChunkCreationResult createChunk(int chunkX, int chunkZ) { Tile tile = dimension.getTile(chunkX >> 3, chunkZ >> 3); if (tile != null) { return createChunk(tile, chunkX, chunkZ); } else { return null; } } public ChunkCreationResult createChunk(Tile tile, int chunkX, int chunkZ) { if (tile.getBitLayerValue(ReadOnly.INSTANCE, (chunkX << 4) & TILE_SIZE_MASK, (chunkZ << 4) & TILE_SIZE_MASK) || tile.getBitLayerValue(NotPresent.INSTANCE, (chunkX << 4) & TILE_SIZE_MASK, (chunkZ << 4) & TILE_SIZE_MASK)) { // Chunk is marked as read-only or not present; don't export or // merge it return null; } final long seed = dimension.getSeed(); if (sugarCaneNoise.getSeed() != (seed + SUGAR_CANE_SEED_OFFSET)) { sugarCaneNoise.setSeed(seed + SUGAR_CANE_SEED_OFFSET); } final Terrain subsurfaceMaterial = dimension.getSubsurfaceMaterial(); final boolean dark = dimension.isDarkLevel(); final boolean bedrock = ! dimension.isBottomless(); final boolean coverSteepTerrain = dimension.isCoverSteepTerrain(); final int tileX = tile.getX(); final int tileY = tile.getY(); final int xOffsetInTile = (chunkX & 7) << 4; final int yOffsetInTile = (chunkZ & 7) << 4; final Random random = new Random(seed + xOffsetInTile * 3 + yOffsetInTile * 5); final boolean populate = dimension.isPopulate() || tile.getBitLayerValue(Populate.INSTANCE, xOffsetInTile, yOffsetInTile); final ChunkCreationResult result = new ChunkCreationResult(); result.chunk = (version == SUPPORTED_VERSION_1) ? new ChunkImpl(chunkX, chunkZ, maxHeight) : new ChunkImpl2(chunkX, chunkZ, maxHeight); final int maxY = maxHeight - 1; final boolean copyBiomes = (version == SUPPORTED_VERSION_2) && (dimension.getDim() == DIM_NORMAL); final int defaultBiome; switch (dimension.getDim()) { case DIM_NORMAL: defaultBiome = BIOME_PLAINS; break; case DIM_NETHER: defaultBiome = BIOME_HELL; break; case DIM_END: defaultBiome = BIOME_SKY; break; case DIM_NORMAL_CEILING: case DIM_NETHER_CEILING: case DIM_END_CEILING: // Biome is ignored for ceilings defaultBiome = 0; break; default: throw new InternalError(); } for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { final int xInTile = xOffsetInTile + x; final int yInTile = yOffsetInTile + z; final int worldX = tileX * TILE_SIZE + xInTile; final int worldY = tileY * TILE_SIZE + yInTile; if (copyBiomes) { int biome = dimension.getLayerValueAt(Biome.INSTANCE, worldX, worldY); if (biome == 255) { biome = dimension.getAutoBiome(tile, xInTile, yInTile); if ((biome < 0) || (biome > 255)) { biome = defaultBiome; } } result.chunk.setBiome(x, z, biome); } else if (result.chunk instanceof ChunkImpl2) { result.chunk.setBiome(x, z, defaultBiome); } final float height = tile.getHeight(xInTile, yInTile); final int intHeight = (int) (height + 0.5f); final int waterLevel = tile.getWaterLevel(xInTile, yInTile); final boolean _void = tile.getBitLayerValue(org.pepsoft.worldpainter.layers.Void.INSTANCE, xInTile, yInTile); if (! _void) { final Terrain terrain = tile.getTerrain(xInTile, yInTile); final int topLayerDepth = dimension.getTopLayerDepth(worldX, worldY, intHeight); final boolean floodWithLava; final boolean underWater = waterLevel > intHeight; if (underWater) { floodWithLava = tile.getBitLayerValue(FloodWithLava.INSTANCE, xInTile, yInTile); result.stats.waterArea++; } else { floodWithLava = false; result.stats.landArea++; } if (bedrock) { result.chunk.setBlockType(x, 0, z, BLK_BEDROCK); } int topLayerMinimumHeight = intHeight - topLayerDepth; if (coverSteepTerrain) { topLayerMinimumHeight = Math.min(topLayerMinimumHeight, Math.min(Math.min(dimension.getIntHeightAt(worldX - 1, worldY, Integer.MAX_VALUE), dimension.getIntHeightAt(worldX + 1, worldY, Integer.MAX_VALUE)), Math.min(dimension.getIntHeightAt(worldX, worldY - 1, Integer.MAX_VALUE), dimension.getIntHeightAt(worldX, worldY + 1, Integer.MAX_VALUE)))); } for (int y = bedrock ? 1 : 0; y <= maxY; y++) { if (y <= topLayerMinimumHeight) { result.chunk.setMaterial(x, y, z, subsurfaceMaterial.getMaterial(seed, worldX, worldY, y, intHeight)); } else if (y < intHeight) { result.chunk.setMaterial(x, y, z, terrain.getMaterial(seed, worldX, worldY, y, intHeight)); } else if (y == intHeight) { final Material material = terrain.getMaterial(seed, worldX, worldY, height, intHeight); final int blockType = material.blockType; if (((blockType == BLK_WOODEN_SLAB) || (blockType == BLK_SLAB) || (blockType == BLK_RED_SANDSTONE_SLAB)) && (! underWater) && (height > intHeight)) { result.chunk.setMaterial(x, y, z, Material.get(blockType - 1, material.data)); } else { result.chunk.setMaterial(x, y, z, material); } } else if (y <= waterLevel) { if (floodWithLava) { result.chunk.setBlockType(x, y, z, BLK_STATIONARY_LAVA); } else { result.chunk.setBlockType(x, y, z, BLK_STATIONARY_WATER); } } else if (! underWater) { if ((y > 0) && ((y - intHeight) <= 3) && ((terrain == Terrain.GRASS) || (terrain == Terrain.DESERT) || (terrain == Terrain.RED_DESERT) || (terrain == Terrain.BEACHES)) && ((sugarCaneNoise.getPerlinNoise(worldX / TINY_BLOBS, worldY / TINY_BLOBS, z / TINY_BLOBS) * sugarCaneNoise.getPerlinNoise(worldX / SMALL_BLOBS, worldY / SMALL_BLOBS, z / SMALL_BLOBS)) > SUGAR_CANE_CHANCE) && (isAdjacentWater(tile, intHeight, xInTile - 1, yInTile) || isAdjacentWater(tile, intHeight, xInTile + 1, yInTile) || isAdjacentWater(tile, intHeight, xInTile, yInTile - 1) || isAdjacentWater(tile, intHeight, xInTile, yInTile + 1))) { int blockTypeBelow = result.chunk.getBlockType(x, y - 1, z); if ((random.nextInt(5) > 0) && ((blockTypeBelow == BLK_GRASS) || (blockTypeBelow == BLK_DIRT) || (blockTypeBelow == BLK_SAND) || (blockTypeBelow == BLK_SUGAR_CANE))) { result.chunk.setMaterial(x, y, z, Material.SUGAR_CANE); } else { result.chunk.setMaterial(x, y, z, terrain.getMaterial(seed, worldX, worldY, y, intHeight)); } } else { result.chunk.setMaterial(x, y, z, terrain.getMaterial(seed, worldX, worldY, y, intHeight)); } } } } if (dark) { result.chunk.setBlockType(x, maxY, z, BLK_BEDROCK); result.chunk.setHeight(x, z, maxY); } else if (_void) { result.chunk.setHeight(x, z, 0); } else if (waterLevel > intHeight) { result.chunk.setHeight(x, z, (waterLevel < maxY) ? (waterLevel + 1): maxY); } else { result.chunk.setHeight(x, z, (intHeight < maxY) ? (intHeight + 1): maxY); } } } if (! populate) { result.chunk.setTerrainPopulated(true); } for (Layer layer: tile.getLayers(minimumLayers)) { LayerExporter layerExporter = exporters.get(layer); if (layerExporter instanceof FirstPassLayerExporter) { if (logger.isTraceEnabled()) { logger.trace("Exporting layer {} for chunk {},{}", layer, chunkX, chunkZ); } ((FirstPassLayerExporter) layerExporter).render(dimension, tile, result.chunk); } } result.stats.surfaceArea = 256; return result; } private boolean isAdjacentWater(Tile tile, int height, int x, int y) { if ((x < 0) || (x >= TILE_SIZE) || (y < 0) || (y >= TILE_SIZE)) { return false; } return (tile.getWaterLevel(x, y) == height) && (! tile.getBitLayerValue(FloodWithLava.INSTANCE, x, y)) && (! tile.getBitLayerValue(Frost.INSTANCE, x, y)) && (tile.getIntHeight(x, y) < height); } private final int version, maxHeight; private final Dimension dimension; private final Set<Layer> minimumLayers; private final PerlinNoise sugarCaneNoise = new PerlinNoise(0); private final Map<Layer, LayerExporter> exporters; private static final long SUGAR_CANE_SEED_OFFSET = 127411424; private static final float SUGAR_CANE_CHANCE = PerlinNoise.getLevelForPromillage(325); private static final Logger logger = LoggerFactory.getLogger(WorldPainterChunkFactory.class); }