From 6a972e7b99288fc55d3a7d8ba8183d9646ff40f8 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 14 Jan 2022 10:49:23 +0100 Subject: [PATCH] Heightmaps used for brushes etc. should be normalised to zero. (#1522) * Heightmaps used for brushes etc. should be normalised to zero. Fixes #1508 * chars are unsigned * Add scale paramer to javadoc --- .../core/command/tool/brush/HeightBrush.java | 4 +- .../core/math/heightmap/ArrayHeightMap.java | 16 ++++---- .../math/heightmap/FlatScalableHeightMap.java | 13 +----- .../core/math/heightmap/HeightMap.java | 12 +++--- .../math/heightmap/ScalableHeightMap.java | 41 +++++++------------ 5 files changed, 33 insertions(+), 53 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java index 935617bad..fea4884f1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java @@ -51,14 +51,14 @@ public class HeightBrush implements Brush { this.smooth = smooth; if (stream != null) { try { - heightMap = ScalableHeightMap.fromPNG(stream, minY, maxY); + heightMap = ScalableHeightMap.fromPNG(stream); } catch (IOException e) { throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid")); } } else if (clipboard != null) { heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY); } else { - heightMap = ScalableHeightMap.fromShape(shape, minY, maxY); + heightMap = ScalableHeightMap.fromShape(shape); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ArrayHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ArrayHeightMap.java index 6d064f054..f73cce04b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ArrayHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ArrayHeightMap.java @@ -3,27 +3,29 @@ package com.fastasyncworldedit.core.math.heightmap; public class ArrayHeightMap extends ScalableHeightMap { // The heights - private final byte[][] height; + private final char[][] height; // The height map width/length private final int width; private final int length; + private final double scale; // The size to width/length ratio private double rx; private double rz; /** - * New height map represented by byte array[][] of values x*z to be scaled given a set size + * New height map represented by char array[][] of values x*z to be scaled given a set size. + * Limited 0->65535 * * @param height array of height values - * @param minY min y value allowed to be set. Inclusive. - * @param maxY max y value allowed to be set. Inclusive. + * @param scale "scale" of the heightmap. Typically the normalised height of the world, or the maximum possible value (256 + * for a PNG heightmap) */ - public ArrayHeightMap(byte[][] height, int minY, int maxY) { - super(minY, maxY); + public ArrayHeightMap(char[][] height, double scale) { setSize(5); this.height = height; this.width = height.length; this.length = height[0].length; + this.scale = scale; } @Override @@ -38,7 +40,7 @@ public class ArrayHeightMap extends ScalableHeightMap { public double getHeight(int x, int z) { x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx)); z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz)); - return ((height[x][z] & 0xFF) * size) / 256d; + return (height[x][z] * size) / scale; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/FlatScalableHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/FlatScalableHeightMap.java index 08f9991de..979f57cda 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/FlatScalableHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/FlatScalableHeightMap.java @@ -2,24 +2,13 @@ package com.fastasyncworldedit.core.math.heightmap; public class FlatScalableHeightMap extends ScalableHeightMap { - /** - * New height map where the returned height is the minmum height value if outside the size, otherwise returns height equal - * to size. - * - * @param minY min y value allowed to be set. Inclusive. - * @param maxY max y value allowed to be set. Inclusive. - */ - public FlatScalableHeightMap(int minY, int maxY) { - super(minY, maxY); - } - @Override public double getHeight(int x, int z) { int dx = Math.abs(x); int dz = Math.abs(z); int d2 = dx * dx + dz * dz; if (d2 > size2) { - return minY; + return 0; } return size; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/HeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/HeightMap.java index b4ed72d11..8fb91d91c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/HeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/HeightMap.java @@ -115,7 +115,7 @@ public interface HeightMap { height = tmpY = session.getNearestSurfaceLayer(xx, zz, tmpY, minY, maxY); } else { height = tmpY = session.getNearestSurfaceTerrainBlock(xx, zz, tmpY, minY, maxY); - if (height == -1) { + if (height < minY) { continue; } } @@ -129,9 +129,9 @@ public interface HeightMap { double raiseScaled = diff * (raisePow * sizePowInv); double raiseScaledAbs = Math.abs(raiseScaled); int random = - ThreadLocalRandom + (ThreadLocalRandom .current() - .nextInt(maxY + 1 - minY) - minY < (int) ((Math.ceil(raiseScaledAbs) - Math.floor( + .nextInt(maxY + 1 - minY) - minY) < (int) ((Math.ceil(raiseScaledAbs) - Math.floor( raiseScaledAbs)) * (maxY + 1 - minY)) ? (diff > 0 ? 1 : -1) : 0; int raiseScaledInt = (int) raiseScaled + random; newData[index] = height + raiseScaledInt; @@ -154,7 +154,7 @@ public interface HeightMap { height = session.getNearestSurfaceLayer(xx, zz, height, minY, maxY); } else { height = session.getNearestSurfaceTerrainBlock(xx, zz, height, minY, maxY); - if (height == minY - 1) { + if (height < minY) { continue; } } @@ -165,9 +165,9 @@ public interface HeightMap { } raise = (yscale * raise); int random = - ThreadLocalRandom + (ThreadLocalRandom .current() - .nextInt(maxY + 1 - minY) - minY < (int) ((raise - (int) raise) * (maxY - minY + 1)) + .nextInt(maxY + 1 - minY) - minY) < (int) ((raise - (int) raise) * (maxY - minY + 1)) ? 1 : 0; int newHeight = height + (int) raise + random; newData[index] = newHeight; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ScalableHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ScalableHeightMap.java index d6ce8f682..c4481da46 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ScalableHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/heightmap/ScalableHeightMap.java @@ -9,7 +9,6 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; import java.awt.image.BufferedImage; -import java.awt.image.Raster; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; @@ -18,8 +17,6 @@ public class ScalableHeightMap implements HeightMap { public int size2; public int size; - protected int minY; - protected int maxY; public enum Shape { CONE, @@ -27,14 +24,9 @@ public class ScalableHeightMap implements HeightMap { } /** - * New height map. - * - * @param minY min y value allowed to be set. Inclusive. - * @param maxY max y value allowed to be set. Inclusive. + * New height map. "Normalised" to a min Y of zero. */ - public ScalableHeightMap(final int minY, final int maxY) { - this.minY = minY; - this.maxY = maxY; + public ScalableHeightMap() { setSize(5); } @@ -50,21 +42,21 @@ public class ScalableHeightMap implements HeightMap { int dz = Math.abs(z); int d2 = dx * dx + dz * dz; if (d2 > size2) { - return minY; + return 0; } - return Math.max(minY, size - MathMan.sqrtApprox(d2)); + return Math.max(0, size - MathMan.sqrtApprox(d2)); } - public static ScalableHeightMap fromShape(Shape shape, int minY, int maxY) { + public static ScalableHeightMap fromShape(Shape shape) { return switch (shape) { - case CONE -> new ScalableHeightMap(minY, maxY); - case CYLINDER -> new FlatScalableHeightMap(minY, maxY); + case CONE -> new ScalableHeightMap(); + case CYLINDER -> new FlatScalableHeightMap(); }; } public static ScalableHeightMap fromClipboard(Clipboard clipboard, int minY, int maxY) { BlockVector3 dim = clipboard.getDimensions(); - byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()]; + char[][] heightArray = new char[dim.getBlockX()][dim.getBlockZ()]; int clipMinX = clipboard.getMinimumPoint().getBlockX(); int clipMinZ = clipboard.getMinimumPoint().getBlockZ(); int clipMinY = clipboard.getMinimumPoint().getBlockY(); @@ -89,20 +81,18 @@ public class ScalableHeightMap implements HeightMap { highestY = y + 1; } } - int pointHeight = Math.min(clipMaxY, ((maxY - minY + 1) * (highestY - clipMinY)) / clipHeight); int x = xx - clipMinX; int z = zz - clipMinZ; - heightArray[x][z] = (byte) pointHeight; + heightArray[x][z] = (char) Math.min(clipMaxY, ((maxY - minY + 1) * (highestY - clipMinY)) / clipHeight); } - return new ArrayHeightMap(heightArray, minY, maxY); + return new ArrayHeightMap(heightArray, maxY - minY + 1); } - public static ScalableHeightMap fromPNG(InputStream stream, int minY, int maxY) throws IOException { + public static ScalableHeightMap fromPNG(InputStream stream) throws IOException { BufferedImage heightFile = MainUtil.readImage(stream); int width = heightFile.getWidth(); int length = heightFile.getHeight(); - Raster data = heightFile.getData(); - byte[][] array = new byte[width][length]; + char[][] array = new char[width][length]; double third = 1 / 3.0; double alphaInverse = 1 / 255.0; for (int x = 0; x < width; x++) { @@ -110,13 +100,12 @@ public class ScalableHeightMap implements HeightMap { int pixel = heightFile.getRGB(x, z); int red = pixel >> 16 & 0xFF; int green = pixel >> 8 & 0xFF; - int blue = pixel >> 0 & 0xFF; + int blue = pixel & 0xFF; int alpha = pixel >> 24 & 0xFF; - int intensity = (int) (alpha * ((red + green + blue) * third) * alphaInverse); - array[x][z] = (byte) intensity; + array[x][z] = (char) (alpha * ((red + green + blue) * third) * alphaInverse); } } - return new ArrayHeightMap(array, minY, maxY); + return new ArrayHeightMap(array, 256d); } }