From 49fc44bb4d19db949687e3f14373df2d377d29da Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 8 Feb 2017 15:44:21 +1100 Subject: [PATCH] Add flatten brush (needs testing) --- .../fawe/object/brush/FlattenBrush.java | 28 +++++ .../boydti/fawe/object/brush/HeightBrush.java | 8 +- .../brush/heightmap/ScalableHeightMap.java | 103 ++++++++++++------ .../worldedit/command/BrushCommands.java | 53 +++++++++ 4 files changed, 152 insertions(+), 40 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java diff --git a/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java new file mode 100644 index 00000000..0c5bda30 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/FlattenBrush.java @@ -0,0 +1,28 @@ +package com.boydti.fawe.object.brush; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.pattern.Pattern; +import java.io.InputStream; + +public class FlattenBrush extends HeightBrush { + + public FlattenBrush(InputStream stream, int rotation, double yscale, DoubleActionBrushTool tool, Clipboard clipboard) { + super(stream, rotation, yscale, tool, clipboard); + } + + @Override + public void build(DoubleActionBrushTool.BrushAction action, EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { + int size = (int) sizeDouble; + Mask mask = tool.getMask(); + if (mask == Masks.alwaysTrue() || mask == Masks.alwaysTrue2D()) { + mask = null; + } + heightMap.setSize(size); + heightMap.apply(editSession, mask, position, size, rotation, action == DoubleActionBrushTool.BrushAction.PRIMARY ? yscale : -yscale, true, true); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java index 3c736978..4814e2ec 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -16,9 +16,9 @@ import java.io.InputStream; public class HeightBrush implements DoubleActionBrush { public final ScalableHeightMap heightMap; - private final int rotation; - double yscale = 1; - private final DoubleActionBrushTool tool; + public final int rotation; + public final double yscale; + public final DoubleActionBrushTool tool; public HeightBrush(InputStream stream, int rotation, double yscale, DoubleActionBrushTool tool, Clipboard clipboard) { this.tool = tool; @@ -45,6 +45,6 @@ public class HeightBrush implements DoubleActionBrush { mask = null; } heightMap.setSize(size); - heightMap.apply(editSession, mask, position, size, rotation, action == DoubleActionBrushTool.BrushAction.PRIMARY ? yscale : -yscale, true); + heightMap.apply(editSession, mask, position, size, rotation, action == DoubleActionBrushTool.BrushAction.PRIMARY ? yscale : -yscale, true, false); } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java index 56e1109b..a26e3ccb 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -2,7 +2,6 @@ package com.boydti.fawe.object.brush.heightmap; import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.PseudoRandom; -import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -12,9 +11,7 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.LocalWorldAdapter; -import com.sk89q.worldedit.math.convolution.GaussianKernel; import com.sk89q.worldedit.math.convolution.HeightMap; -import com.sk89q.worldedit.math.convolution.HeightMapFilter; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import java.awt.image.BufferedImage; @@ -103,7 +100,7 @@ public class ScalableHeightMap { return new ArrayHeightMap(array); } - public void apply(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth) throws MaxChangedBlocksException { + public void apply(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException { Vector top = session.getMaximumPoint(); int maxY = top.getBlockY(); int diameter = 2 * size + 1; @@ -113,31 +110,65 @@ public class ScalableHeightMap { int startY = pos.getBlockY() - size; int[] newData = new int[diameter * diameter]; Vector mutablePos = new Vector(0, 0, 0); - for (int x = -size; x <= size; x++) { - int xx = centerX + x; - mutablePos.mutX(xx); - for (int z = -size; z <= size; z++) { - int index = (z + size) * diameter + (x + size); - int zz = centerZ + z; - double raise; - switch (rotationMode) { - default: - raise = getHeight(x, z); - break; - case 1: - raise = getHeight(z, x); - break; - case 2: - raise = getHeight(-x, -z); - break; - case 3: - raise = getHeight(-z, -x); - break; + if (towards) { + int targetY = pos.getBlockY(); + for (int x = -size; x <= size; x++) { + int xx = centerX + x; + mutablePos.mutX(xx); + for (int z = -size; z <= size; z++) { + int index = (z + size) * diameter + (x + size); + int zz = centerZ + z; + double raise; + switch (rotationMode) { + default: + raise = getHeight(x, z); + break; + case 1: + raise = getHeight(z, x); + break; + case 2: + raise = getHeight(-x, -z); + break; + case 3: + raise = getHeight(-z, -x); + break; + } + raise = (yscale * raise); + int height = session.getHighestTerrainBlock(xx, zz, 0, 255, true); + int diff = targetY - height; + double raiseScaled = 1 + diff * (raise / (double) size); + int random = PseudoRandom.random.random(maxY + 1) < (int) ((raiseScaled - (int) raiseScaled) * (maxY + 1)) ? 1 : 0; + int raiseScaledInt = (int) raiseScaled + random; + newData[index] = height + raiseScaledInt; + } + } + } else { + for (int x = -size; x <= size; x++) { + int xx = centerX + x; + mutablePos.mutX(xx); + for (int z = -size; z <= size; z++) { + int index = (z + size) * diameter + (x + size); + int zz = centerZ + z; + double raise; + switch (rotationMode) { + default: + raise = getHeight(x, z); + break; + case 1: + raise = getHeight(z, x); + break; + case 2: + raise = getHeight(-x, -z); + break; + case 3: + raise = getHeight(-z, -x); + break; + } + raise = (yscale * raise); + int random = PseudoRandom.random.random(maxY + 1) < (int) ((raise - (int) raise) * (maxY + 1)) ? 1 : 0; + int height = session.getHighestTerrainBlock(xx, zz, 0, maxY, true) + (int) raise + random; + newData[index] = height; } - raise = (yscale * raise); - int random = PseudoRandom.random.random(maxY + 1) < (int) ((raise - (int) raise) * (maxY + 1)) ? 1 : 0; - int height = session.getHighestTerrainBlock(xx, zz, 0, maxY, true) + (int) raise + random; - newData[index] = height; } } int iterations = 1; @@ -145,14 +176,14 @@ public class ScalableHeightMap { Vector max = pos.add(size, maxY, size); Region region = new CuboidRegion(session.getWorld(), min, max); HeightMap heightMap = new HeightMap(session, region, true); - if (smooth) { - try { - HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); - newData = filter.filter(newData, diameter, diameter); - } catch (Throwable e) { - MainUtil.handleError(e); - } - } +// if (smooth) { +// try { +// HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1)); +// newData = filter.filter(newData, diameter, diameter); +// } catch (Throwable e) { +// MainUtil.handleError(e); +// } +// } heightMap.apply(newData); } } diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 5e0c22df..bee8559c 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -29,6 +29,7 @@ import com.boydti.fawe.object.brush.CommandBrush; import com.boydti.fawe.object.brush.CopyPastaBrush; import com.boydti.fawe.object.brush.DoubleActionBrushTool; import com.boydti.fawe.object.brush.ErodeBrush; +import com.boydti.fawe.object.brush.FlattenBrush; import com.boydti.fawe.object.brush.HeightBrush; import com.boydti.fawe.object.brush.LineBrush; import com.boydti.fawe.object.brush.RecurseBrush; @@ -405,6 +406,58 @@ public class BrushCommands { player.print(BBC.getPrefix() + BBC.BRUSH_HEIGHT.f(radius)); } + @Command( + aliases = { "flatten", "flatmap", "flat" }, + usage = "[radius] [file|#clipboard|null] [rotation] [yscale]", + flags = "h", + desc = "Flatten brush", + help = + "This brush raises and lowers land towards the clicked point\n", + min = 1, + max = 4 + ) + @CommandPermissions("worldedit.brush.height") + public void flattenBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + String filenamePng = (filename.endsWith(".png") ? filename : filename + ".png"); + File file = new File(Fawe.imp().getDirectory(), "heightmap" + File.separator + filenamePng); + InputStream stream = null; + if (!file.exists()) { + if (!filename.equals("#clipboard") && filename.length() >= 7) { + try { + URL url; + if (filename.startsWith("http")) { + url = new URL(filename); + if (!url.getHost().equals("i.imgur.com")) { + throw new FileNotFoundException(filename); + } + } else { + url = new URL("https://i.imgur.com/" + filenamePng); + } + ReadableByteChannel rbc = Channels.newChannel(url.openStream()); + stream = Channels.newInputStream(rbc); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } else if (!filename.equalsIgnoreCase("#clipboard")){ + try { + stream = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + DoubleActionBrushTool tool = session.getDoubleActionBrushTool(player.getItemInHand()); + tool.setSize(radius); + try { + tool.setBrush(new FlattenBrush(stream, rotation, yscale, tool, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height"); + } catch (EmptyClipboardException ignore) { + tool.setBrush(new FlattenBrush(stream, rotation, yscale, tool, null), "worldedit.brush.height"); + } + player.print(BBC.getPrefix() + BBC.BRUSH_HEIGHT.f(radius)); + } + @Command( aliases = { "copypaste", "copy", "paste", "cp", "copypasta" }, usage = "[depth]",