diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index c36f2625f..fef75da83 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -592,10 +592,25 @@ public class EditSession implements Extent, AutoCloseable { * @return height of highest block found or 'minY' */ public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { + return getHighestTerrainBlock(x, z, minY, maxY, null); + } + + /** + * Returns the highest solid 'terrain' block. + * + * @param x the X coordinate + * @param z the Z coordinate + * @param minY minimal height + * @param maxY maximal height + * @param filter a mask of blocks to consider, or null to consider any solid (movement-blocking) block + * @return height of highest block found or 'minY' + */ + public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { for (int y = maxY; y >= minY; --y) { BlockVector3 pt = BlockVector3.at(x, y, z); - BlockState block = getBlock(pt); - if (block.getBlockType().getMaterial().isMovementBlocker()) { + if (filter == null + ? getBlock(pt).getBlockType().getMaterial().isMovementBlocker() + : filter.test(pt)) { return y; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index a53178c0d..0d1adc1d3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -42,6 +42,7 @@ import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; @@ -158,24 +159,24 @@ public class BrushCommands { @Command( aliases = { "smooth" }, - usage = "[size] [iterations]", + usage = "[size] [iterations] [filter]", desc = "Choose the terrain softener brush", help = - "Chooses the terrain softener brush.", + "Chooses the terrain softener brush. Optionally, specify a mask of blocks to be used for the heightmap.\n" + + "For example, '/brush smooth 2 4 grass_block,dirt,stone'.", min = 0, - max = 2 + max = 3 ) @CommandPermissions("worldedit.brush.smooth") public void smoothBrush(Player player, LocalSession session, EditSession editSession, - @Optional("2") double radius, @Optional("4") int iterations) throws WorldEditException { - + @Optional("2") double radius, @Optional("4") int iterations, @Optional Mask mask) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); tool.setSize(radius); - tool.setBrush(new SmoothBrush(iterations), "worldedit.brush.smooth"); + tool.setBrush(new SmoothBrush(iterations, mask), "worldedit.brush.smooth"); - player.print(String.format("Smooth brush equipped (%.0f x %dx, using any block).", radius, iterations)); + player.print(String.format("Smooth brush equipped (%.0f x %dx, using %s).", radius, iterations, mask == null ? "any block" : "filter")); } @Command( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index c12d897b8..704025a3f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -240,17 +240,19 @@ public class RegionCommands { @Command( aliases = { "/smooth" }, - usage = "[iterations]", + usage = "[iterations] [filter]", desc = "Smooth the elevation in the selection", help = - "Smooths the elevation in the selection.", + "Smooths the elevation in the selection.\n" + + "Optionally, restricts the height map to a set of blocks specified with mask syntax.\n" + + "For example, '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain.", min = 0, - max = 1 + max = 2 ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) - public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations) throws WorldEditException { - HeightMap heightMap = new HeightMap(editSession, region); + public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Optional Mask mask) throws WorldEditException { + HeightMap heightMap = new HeightMap(editSession, region, mask); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); int affected = heightMap.applyFilter(filter, iterations); player.print("Terrain's height map smoothed. " + affected + " block(s) changed."); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java index d46b1ec3a..2ecf245d1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -31,12 +32,20 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import javax.annotation.Nullable; + public class SmoothBrush implements Brush { + private final Mask mask; private int iterations; public SmoothBrush(int iterations) { + this(iterations, null); + } + + public SmoothBrush(int iterations, @Nullable Mask mask) { this.iterations = iterations; + this.mask = mask; } @Override @@ -45,7 +54,7 @@ public class SmoothBrush implements Brush { Location min = new Location(editSession.getWorld(), posDouble.subtract(size, size, size)); BlockVector3 max = posDouble.add(size, size + 10, size).toBlockPoint(); Region region = new CuboidRegion(editSession.getWorld(), min.toVector().toBlockPoint(), max); - HeightMap heightMap = new HeightMap(editSession, region); + HeightMap heightMap = new HeightMap(editSession, region, mask); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); heightMap.applyFilter(filter, iterations); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index ff2dae979..c075bbf7f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -23,11 +23,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import javax.annotation.Nullable; + /** * Allows applications of Kernels onto the region's height map. * @@ -48,7 +51,7 @@ public class HeightMap { * @param session an edit session * @param region the region */ - public HeightMap(EditSession session, Region region) { + public HeightMap(EditSession session, Region region, @Nullable Mask mask) { checkNotNull(session); checkNotNull(region); @@ -67,7 +70,7 @@ public class HeightMap { data = new int[width * height]; for (int z = 0; z < height; ++z) { for (int x = 0; x < width; ++x) { - data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY); + data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY, mask); } } }