use of org.pepsoft.util.Box in project WorldPainter by Captain-Chaos.
the class DynMapPreviewer method createImage.
public BufferedImage createImage() {
Point3i offset = object.getOffset();
Point3i dimensions = object.getDimensions();
Rectangle tileCoords = tileProvider.getBounds(new Box(offset.x, offset.x + dimensions.x - 1, offset.y, offset.y + dimensions.y - 1, offset.z, offset.z + dimensions.z - 1));
BufferedImage image = new BufferedImage(tileCoords.width * 128, tileCoords.height * 128, BufferedImage.TYPE_INT_ARGB);
for (int dx = 0; dx < tileCoords.width; dx++) {
for (int dy = 0; dy < tileCoords.height; dy++) {
tileProvider.paintTile(image, tileCoords.x + dx, tileCoords.y + dy, dx * 128, dy * 128);
}
}
return image;
}
use of org.pepsoft.util.Box in project WorldPainter by Captain-Chaos.
the class LayerPreviewCreator method renderPreview.
public MinecraftWorldObject renderPreview() {
// Phase one: setup
long timestamp = System.currentTimeMillis();
long seed = 0L;
TileFactory tileFactory = subterranean ? TileFactoryFactory.createNoiseTileFactory(seed, Terrain.BARE_GRASS, previewHeight, 56, 62, false, true, 20f, 0.5) : TileFactoryFactory.createNoiseTileFactory(seed, Terrain.BARE_GRASS, previewHeight, 8, 14, false, true, 20f, 0.5);
Dimension dimension = new Dimension(seed, tileFactory, DIM_NORMAL, previewHeight);
dimension.setSubsurfaceMaterial(Terrain.STONE);
MinecraftWorldObject minecraftWorldObject = new MinecraftWorldObject(layer.getName() + " Preview", new Box(-8, 136, -8, 136, 0, previewHeight), previewHeight, null, new Point3i(-64, -64, 0));
long now = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Creating data structures took " + (now - timestamp) + " ms");
}
// Phase two: apply layer to dimension
timestamp = now;
Tile tile = tileFactory.createTile(0, 0);
switch(layer.getDataSize()) {
case BIT:
Random random = new Random(seed);
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
if (random.nextFloat() < pattern.getStrength(x, y)) {
tile.setBitLayerValue(layer, x, y, true);
}
}
}
break;
case BIT_PER_CHUNK:
random = new Random(seed);
for (int x = 0; x < 128; x += 16) {
for (int y = 0; y < 128; y += 16) {
if (random.nextFloat() < pattern.getStrength(x, y)) {
tile.setBitLayerValue(layer, x, y, true);
}
}
}
break;
case BYTE:
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
tile.setLayerValue(layer, x, y, Math.min((int) (pattern.getStrength(x, y) * 256), 255));
}
}
break;
case NIBBLE:
// any
if (layer instanceof CombinedLayer) {
final Terrain terrain = ((CombinedLayer) layer).getTerrain();
final int biome = ((CombinedLayer) layer).getBiome();
final boolean terrainConfigured = terrain != null;
final boolean biomeConfigured = biome != -1;
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
float strength = pattern.getStrength(x, y);
tile.setLayerValue(layer, x, y, Math.min((int) (strength * 16), 15));
// Double the strength so that 50% intensity results
// in full coverage for terrain and biome, which is
// inaccurate but probably more closely resembles
// practical usage
strength = Math.min(strength * 2, 1.0f);
if (terrainConfigured && ((strength > 0.95f) || (Math.random() < strength))) {
tile.setTerrain(x, y, terrain);
}
if (biomeConfigured && ((strength > 0.95f) || (Math.random() < strength))) {
tile.setLayerValue(Biome.INSTANCE, x, y, biome);
}
}
}
} else {
for (int x = 0; x < 128; x++) {
for (int y = 0; y < 128; y++) {
tile.setLayerValue(layer, x, y, Math.min((int) (pattern.getStrength(x, y) * 16), 15));
}
}
}
break;
default:
throw new IllegalArgumentException("Unsupported data size " + layer.getDataSize() + " encountered");
}
// If the layer is a combined layer, apply it recursively and collect
// the added layers
List<Layer> layers;
if (layer instanceof CombinedLayer) {
layers = new ArrayList<>();
layers.add(layer);
while (true) {
List<Layer> addedLayers = new ArrayList<>();
for (Iterator<Layer> i = layers.iterator(); i.hasNext(); ) {
Layer tmpLayer = i.next();
if (tmpLayer instanceof CombinedLayer) {
i.remove();
addedLayers.addAll(((CombinedLayer) tmpLayer).apply(tile));
}
}
if (!addedLayers.isEmpty()) {
layers.addAll(addedLayers);
} else {
break;
}
}
} else {
layers = Collections.singletonList(layer);
}
dimension.addTile(tile);
now = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Applying layer(s) took " + (now - timestamp) + " ms");
}
// Collect the exporters (could be multiple if the layer was a combined
// layer)
Map<Layer, LayerExporter> pass1Exporters = new HashMap<>();
Map<Layer, SecondPassLayerExporter> pass2Exporters = new HashMap<>();
for (Layer tmpLayer : layers) {
LayerExporter exporter = tmpLayer.getExporter();
if (tmpLayer.equals(layer)) {
exporter.setSettings(settings);
}
if (exporter instanceof FirstPassLayerExporter) {
pass1Exporters.put(layer, exporter);
}
if (exporter instanceof SecondPassLayerExporter) {
pass2Exporters.put(layer, (SecondPassLayerExporter) exporter);
}
}
// Phase three: generate terrain and render first pass layers, if any
timestamp = now;
WorldPainterChunkFactory chunkFactory = new WorldPainterChunkFactory(dimension, pass1Exporters, DefaultPlugin.JAVA_ANVIL, previewHeight);
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
Chunk chunk = chunkFactory.createChunk(x, y).chunk;
minecraftWorldObject.addChunk(chunk);
}
}
now = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Generating terrain and rendering first pass layer(s) (if any) took " + (now - timestamp) + " ms");
}
if (!pass2Exporters.isEmpty()) {
// Phase four: render the second pass layers, if any
timestamp = now;
Rectangle area = new Rectangle(128, 128);
for (SecondPassLayerExporter exporter : pass2Exporters.values()) {
exporter.render(dimension, area, area, minecraftWorldObject);
}
now = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Rendering second pass layer(s) took " + (now - timestamp) + " ms");
}
}
// Final phase: post processing
timestamp = now;
now = System.currentTimeMillis();
try {
new JavaPostProcessor().postProcess(minecraftWorldObject, new Rectangle(-8, -8, 136, 136), null);
} catch (ProgressReceiver.OperationCancelled e) {
// Can't happen since we didn't pass in a progress receiver
throw new InternalError();
}
if (logger.isDebugEnabled()) {
logger.debug("Post processing took " + (now - timestamp) + " ms");
}
return minecraftWorldObject;
}
use of org.pepsoft.util.Box in project WorldPainter by Captain-Chaos.
the class JavaPostProcessor method postProcess.
/**
* Post process (part of) a {@link MinecraftWorld} to make sure it conforms
* to Minecraft's rules. For instance:
*
* <ul><li>Remove plants that are on the wrong underground or floating
* in air.
* <li>Change the lowest block of a column of Sand to Sandstone.
* <li>Remove snow on top of blocks which don't support snow, or floating in
* air.
* <li>Change covered grass and mycelium blocks to dirt.
* </ul>
*
* @param minecraftWorld The <code>MinecraftWorld</code> to post process.
* @param volume The three dimensional area of the world to post process.
* @param progressReceiver The optional progress receiver to which to report
* progress. May be <code>null</code>.
* @throws ProgressReceiver.OperationCancelled If the progress receiver
* threw an <code>OperationCancelled</code> exception.
*/
@Override
public void postProcess(MinecraftWorld minecraftWorld, Box volume, ProgressReceiver progressReceiver) throws ProgressReceiver.OperationCancelled {
if (!enabled) {
return;
}
if (progressReceiver != null) {
progressReceiver.setMessage("Enforcing Minecraft rules on exported blocks");
}
final int worldMaxZ = minecraftWorld.getMaxHeight() - 1;
final int x1, y1, x2, y2, minZ, maxZ;
// TODO: make these configurable:
final FloatMode sandMode = "false".equalsIgnoreCase(System.getProperty("org.pepsoft.worldpainter.supportSand")) ? FloatMode.LEAVE_FLOATING : FloatMode.SUPPORT;
final FloatMode gravelMode = FloatMode.LEAVE_FLOATING;
final FloatMode cementMode = FloatMode.LEAVE_FLOATING;
if (minecraftWorld instanceof MinecraftWorldObject) {
// Special support for MinecraftWorldObjects to constrain the area
// further
Box objectVolume = ((MinecraftWorldObject) minecraftWorld).getVolume();
objectVolume.intersect(volume);
if (objectVolume.isEmpty()) {
// do
return;
} else {
x1 = objectVolume.getX1();
x2 = objectVolume.getX2() - 1;
y1 = objectVolume.getY1();
y2 = objectVolume.getY2() - 1;
minZ = objectVolume.getZ1();
maxZ = objectVolume.getZ2() - 1;
}
} else {
x1 = volume.getX1();
y1 = volume.getY1();
x2 = volume.getX2() - 1;
y2 = volume.getY2() - 1;
minZ = volume.getZ1();
maxZ = volume.getZ2() - 1;
}
final boolean traceEnabled = logger.isTraceEnabled();
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y++) {
int blockTypeBelow = BLK_AIR;
int blockTypeAbove = minecraftWorld.getBlockTypeAt(x, y, minZ);
// }
for (int z = minZ; z <= maxZ; z++) {
int blockType = blockTypeAbove;
blockTypeAbove = (z < worldMaxZ) ? minecraftWorld.getBlockTypeAt(x, y, z + 1) : BLK_AIR;
if (((blockTypeBelow == BLK_GRASS) || (blockTypeBelow == BLK_MYCELIUM) || (blockTypeBelow == BLK_TILLED_DIRT)) && ((blockType == BLK_WATER) || (blockType == BLK_STATIONARY_WATER) || (blockType == BLK_ICE) || ((blockType <= HIGHEST_KNOWN_BLOCK_ID) && (BLOCK_TRANSPARENCY[blockType] == 15)))) {
// Covered grass, mycelium or tilled earth block, should
// be dirt. Note that unknown blocks are treated as
// transparent for this check so that grass underneath
// custom plants doesn't turn to dirt, for instance
minecraftWorld.setMaterialAt(x, y, z - 1, Material.DIRT);
blockTypeBelow = BLK_DIRT;
}
switch(blockType) {
case BLK_SAND:
if (BLOCKS[blockTypeBelow].veryInsubstantial) {
switch(sandMode) {
case DROP:
dropBlock(minecraftWorld, x, y, z);
blockType = BLK_AIR;
break;
case SUPPORT:
// All unsupported sand should be supported by sandstone
minecraftWorld.setMaterialAt(x, y, z, (minecraftWorld.getDataAt(x, y, z) == 1) ? Material.RED_SANDSTONE : Material.SANDSTONE);
blockType = minecraftWorld.getBlockTypeAt(x, y, z);
break;
default:
// Do nothing
break;
}
}
break;
case BLK_GRAVEL:
if (BLOCKS[blockTypeBelow].veryInsubstantial) {
switch(gravelMode) {
case DROP:
dropBlock(minecraftWorld, x, y, z);
blockType = BLK_AIR;
break;
case SUPPORT:
// All unsupported gravel should be supported by stone
minecraftWorld.setMaterialAt(x, y, z, Material.STONE);
blockType = BLK_STONE;
break;
default:
// Do nothing
break;
}
}
break;
case BLK_CEMENT:
if (BLOCKS[blockTypeBelow].veryInsubstantial) {
switch(cementMode) {
case DROP:
dropBlock(minecraftWorld, x, y, z);
blockType = BLK_AIR;
break;
case SUPPORT:
throw new UnsupportedOperationException("Don't know how to support cement yet");
default:
// Do nothing
break;
}
}
break;
case BLK_DEAD_SHRUBS:
if ((blockTypeBelow != BLK_SAND) && (blockTypeBelow != BLK_DIRT) && (blockTypeBelow != BLK_STAINED_CLAY) && (blockTypeBelow != BLK_HARDENED_CLAY)) {
// Dead shrubs can only exist on Sand
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_TALL_GRASS:
case BLK_ROSE:
case BLK_DANDELION:
if ((blockTypeBelow != BLK_GRASS) && (blockTypeBelow != BLK_DIRT)) {
// Tall grass and flowers can only exist on Grass or Dirt blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_RED_MUSHROOM:
case BLK_BROWN_MUSHROOM:
if ((blockTypeBelow != BLK_GRASS) && (blockTypeBelow != BLK_DIRT) && (blockTypeBelow != BLK_MYCELIUM) && (blockTypeBelow != BLK_STONE)) {
// Mushrooms can only exist on Grass, Dirt, Mycelium or Stone (in caves) blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_SNOW:
if ((blockTypeBelow == BLK_ICE) || (blockTypeBelow == BLK_SNOW) || (blockTypeBelow == BLK_AIR) || (blockTypeBelow == BLK_PACKED_ICE)) {
// Snow can't be on ice, or another snow block, or air
// (well it could be, but it makes no sense, would
// disappear when touched, and it makes this algorithm
// remove stacks of snow blocks correctly)
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_WHEAT:
if (blockTypeBelow != BLK_TILLED_DIRT) {
// Wheat can only exist on Tilled Dirt blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_LARGE_FLOWERS:
int data = minecraftWorld.getDataAt(x, y, z);
if ((data & 0x8) == 0x8) {
// in the previous iteration
if (blockTypeBelow != BLK_LARGE_FLOWERS) {
// replace this block with air
if (traceEnabled) {
logger.trace("Block @ " + x + "," + z + "," + y + " is upper large flower block; block below is " + BLOCK_TYPE_NAMES[blockTypeBelow] + "; removing block");
}
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
} else {
// there's a top half above and grass or dirt below
if (blockTypeAbove == BLK_LARGE_FLOWERS) {
if ((minecraftWorld.getDataAt(x, y, z + 1) & 0x8) == 0) {
// this block with air
if (traceEnabled) {
logger.trace("Block @ " + x + "," + z + "," + y + " is lower large flower block; block above is another lower large flower block; removing block");
}
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
} else if ((blockTypeBelow != BLK_GRASS) && (blockTypeBelow != BLK_DIRT)) {
// check) only exist on grass or dirt
if (traceEnabled) {
logger.trace("Block @ " + x + "," + z + "," + y + " is lower large flower block; block above is " + BLOCK_TYPE_NAMES[blockTypeBelow] + "; removing block");
}
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
} else {
// replace this block with air
if (traceEnabled) {
logger.trace("Block @ " + x + "," + z + "," + y + " is lower large flower block; block above is " + BLOCK_TYPE_NAMES[blockTypeBelow] + "; removing block");
}
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
}
break;
case BLK_CACTUS:
if ((blockTypeBelow != BLK_SAND) && (blockTypeBelow != BLK_CACTUS)) {
// Cactus blocks can only be on top of sand or other cactus blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_SUGAR_CANE:
if ((blockTypeBelow != BLK_GRASS) && (blockTypeBelow != BLK_DIRT) && (blockTypeBelow != BLK_SAND) && (blockTypeBelow != BLK_SUGAR_CANE)) {
// Sugar cane blocks can only be on top of grass, dirt, sand or other sugar cane blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_NETHER_WART:
if (blockTypeBelow != BLK_SOUL_SAND) {
// Nether wart blocks can only be on top of soul sand
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_CHORUS_FLOWER:
case BLK_CHORUS_PLANT:
if ((blockTypeBelow != BLK_END_STONE) && (blockTypeBelow != BLK_CHORUS_PLANT)) {
// Chorus flower and plant blocks can only be on top of end stone or other chorus plant blocks
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
case BLK_FIRE:
// the air
if ((blockTypeBelow == BLK_AIR) && (blockTypeAbove == BLK_AIR) && (minecraftWorld.getBlockTypeAt(x - 1, y, z) == BLK_AIR) && (minecraftWorld.getBlockTypeAt(x + 1, y, z) == BLK_AIR) && (minecraftWorld.getBlockTypeAt(x, y - 1, z) == BLK_AIR) && (minecraftWorld.getBlockTypeAt(x, y + 1, z) == BLK_AIR)) {
minecraftWorld.setMaterialAt(x, y, z, Material.AIR);
blockType = BLK_AIR;
}
break;
}
blockTypeBelow = blockType;
}
}
if (progressReceiver != null) {
progressReceiver.setProgress((float) (x - x1 + 1) / (x2 - x1 + 1));
}
}
}
use of org.pepsoft.util.Box in project WorldPainter by Captain-Chaos.
the class WPObjectExporter method getBounds.
private static Box getBounds(WPObject object, int x, int y, int z) {
Point3i dimensions = object.getDimensions();
Point3i offset = object.getOffset();
return new Box(x + offset.x, x + offset.x + dimensions.x - 1, y + offset.y, y + offset.y + dimensions.y - 1, z + offset.z, z + offset.z + dimensions.z - 1);
}
use of org.pepsoft.util.Box in project WorldPainter by Captain-Chaos.
the class AbstractWorldExporter method lightingPass.
protected void lightingPass(MinecraftWorld minecraftWorld, Point regionCoords, ProgressReceiver progressReceiver) throws OperationCancelled {
if (progressReceiver != null) {
progressReceiver.setMessage("Calculating primary light");
}
LightingCalculator lightingVolume = new LightingCalculator(minecraftWorld);
// Calculate primary light
int lightingLowMark = Integer.MAX_VALUE, lightingHighMark = Integer.MIN_VALUE;
int lowestChunkX = (regionCoords.x << 5) - 1;
int highestChunkX = (regionCoords.x << 5) + 32;
int lowestChunkY = (regionCoords.y << 5) - 1;
int highestChunkY = (regionCoords.y << 5) + 32;
int total = highestChunkX - lowestChunkX + 1, count = 0;
for (int chunkX = lowestChunkX; chunkX <= highestChunkX; chunkX++) {
for (int chunkY = lowestChunkY; chunkY <= highestChunkY; chunkY++) {
Chunk chunk = minecraftWorld.getChunk(chunkX, chunkY);
if (chunk != null) {
int[] levels = lightingVolume.calculatePrimaryLight(chunk);
if (levels[0] < lightingLowMark) {
lightingLowMark = levels[0];
}
if (levels[1] > lightingHighMark) {
lightingHighMark = levels[1];
}
}
}
if (progressReceiver != null) {
progressReceiver.setProgress(0.2f * ++count / total);
}
}
if (lightingLowMark != Integer.MAX_VALUE) {
if (progressReceiver != null) {
progressReceiver.setMessage("Propagating light");
}
// Calculate secondary light
Box originalDirtyArea = new Box((regionCoords.x << 9) - 16, ((regionCoords.x + 1) << 9) + 15, lightingLowMark, lightingHighMark, (regionCoords.y << 9) - 16, ((regionCoords.y + 1) << 9) + 15);
int originalVolume = originalDirtyArea.getVolume();
Box dirtyArea = originalDirtyArea.clone();
lightingVolume.setDirtyArea(dirtyArea);
while (lightingVolume.calculateSecondaryLight()) {
if (progressReceiver != null) {
progressReceiver.setProgress(0.2f + 0.8f * (originalVolume - dirtyArea.getVolume()) / originalVolume);
}
}
}
if (progressReceiver != null) {
progressReceiver.setProgress(1.0f);
}
}
Aggregations