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
This commit is contained in:
Jordan 2022-01-14 10:49:23 +01:00 committed by GitHub
parent 9e40b972b1
commit 6a972e7b99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 53 deletions

View File

@ -51,14 +51,14 @@ public class HeightBrush implements Brush {
this.smooth = smooth; this.smooth = smooth;
if (stream != null) { if (stream != null) {
try { try {
heightMap = ScalableHeightMap.fromPNG(stream, minY, maxY); heightMap = ScalableHeightMap.fromPNG(stream);
} catch (IOException e) { } catch (IOException e) {
throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid")); throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid"));
} }
} else if (clipboard != null) { } else if (clipboard != null) {
heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY); heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY);
} else { } else {
heightMap = ScalableHeightMap.fromShape(shape, minY, maxY); heightMap = ScalableHeightMap.fromShape(shape);
} }
} }

View File

@ -3,27 +3,29 @@ package com.fastasyncworldedit.core.math.heightmap;
public class ArrayHeightMap extends ScalableHeightMap { public class ArrayHeightMap extends ScalableHeightMap {
// The heights // The heights
private final byte[][] height; private final char[][] height;
// The height map width/length // The height map width/length
private final int width; private final int width;
private final int length; private final int length;
private final double scale;
// The size to width/length ratio // The size to width/length ratio
private double rx; private double rx;
private double rz; 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 height array of height values
* @param minY min 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
* @param maxY max y value allowed to be set. Inclusive. * for a PNG heightmap)
*/ */
public ArrayHeightMap(byte[][] height, int minY, int maxY) { public ArrayHeightMap(char[][] height, double scale) {
super(minY, maxY);
setSize(5); setSize(5);
this.height = height; this.height = height;
this.width = height.length; this.width = height.length;
this.length = height[0].length; this.length = height[0].length;
this.scale = scale;
} }
@Override @Override
@ -38,7 +40,7 @@ public class ArrayHeightMap extends ScalableHeightMap {
public double getHeight(int x, int z) { public double getHeight(int x, int z) {
x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx)); x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx));
z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz)); 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;
} }

View File

@ -2,24 +2,13 @@ package com.fastasyncworldedit.core.math.heightmap;
public class FlatScalableHeightMap extends ScalableHeightMap { 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 @Override
public double getHeight(int x, int z) { public double getHeight(int x, int z) {
int dx = Math.abs(x); int dx = Math.abs(x);
int dz = Math.abs(z); int dz = Math.abs(z);
int d2 = dx * dx + dz * dz; int d2 = dx * dx + dz * dz;
if (d2 > size2) { if (d2 > size2) {
return minY; return 0;
} }
return size; return size;
} }

View File

@ -115,7 +115,7 @@ public interface HeightMap {
height = tmpY = session.getNearestSurfaceLayer(xx, zz, tmpY, minY, maxY); height = tmpY = session.getNearestSurfaceLayer(xx, zz, tmpY, minY, maxY);
} else { } else {
height = tmpY = session.getNearestSurfaceTerrainBlock(xx, zz, tmpY, minY, maxY); height = tmpY = session.getNearestSurfaceTerrainBlock(xx, zz, tmpY, minY, maxY);
if (height == -1) { if (height < minY) {
continue; continue;
} }
} }
@ -129,9 +129,9 @@ public interface HeightMap {
double raiseScaled = diff * (raisePow * sizePowInv); double raiseScaled = diff * (raisePow * sizePowInv);
double raiseScaledAbs = Math.abs(raiseScaled); double raiseScaledAbs = Math.abs(raiseScaled);
int random = int random =
ThreadLocalRandom (ThreadLocalRandom
.current() .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; raiseScaledAbs)) * (maxY + 1 - minY)) ? (diff > 0 ? 1 : -1) : 0;
int raiseScaledInt = (int) raiseScaled + random; int raiseScaledInt = (int) raiseScaled + random;
newData[index] = height + raiseScaledInt; newData[index] = height + raiseScaledInt;
@ -154,7 +154,7 @@ public interface HeightMap {
height = session.getNearestSurfaceLayer(xx, zz, height, minY, maxY); height = session.getNearestSurfaceLayer(xx, zz, height, minY, maxY);
} else { } else {
height = session.getNearestSurfaceTerrainBlock(xx, zz, height, minY, maxY); height = session.getNearestSurfaceTerrainBlock(xx, zz, height, minY, maxY);
if (height == minY - 1) { if (height < minY) {
continue; continue;
} }
} }
@ -165,9 +165,9 @@ public interface HeightMap {
} }
raise = (yscale * raise); raise = (yscale * raise);
int random = int random =
ThreadLocalRandom (ThreadLocalRandom
.current() .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; ? 1 : 0;
int newHeight = height + (int) raise + random; int newHeight = height + (int) raise + random;
newData[index] = newHeight; newData[index] = newHeight;

View File

@ -9,7 +9,6 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashSet; import java.util.HashSet;
@ -18,8 +17,6 @@ public class ScalableHeightMap implements HeightMap {
public int size2; public int size2;
public int size; public int size;
protected int minY;
protected int maxY;
public enum Shape { public enum Shape {
CONE, CONE,
@ -27,14 +24,9 @@ public class ScalableHeightMap implements HeightMap {
} }
/** /**
* New height map. * New height map. "Normalised" to a min Y of zero.
*
* @param minY min y value allowed to be set. Inclusive.
* @param maxY max y value allowed to be set. Inclusive.
*/ */
public ScalableHeightMap(final int minY, final int maxY) { public ScalableHeightMap() {
this.minY = minY;
this.maxY = maxY;
setSize(5); setSize(5);
} }
@ -50,21 +42,21 @@ public class ScalableHeightMap implements HeightMap {
int dz = Math.abs(z); int dz = Math.abs(z);
int d2 = dx * dx + dz * dz; int d2 = dx * dx + dz * dz;
if (d2 > size2) { 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) { return switch (shape) {
case CONE -> new ScalableHeightMap(minY, maxY); case CONE -> new ScalableHeightMap();
case CYLINDER -> new FlatScalableHeightMap(minY, maxY); case CYLINDER -> new FlatScalableHeightMap();
}; };
} }
public static ScalableHeightMap fromClipboard(Clipboard clipboard, int minY, int maxY) { public static ScalableHeightMap fromClipboard(Clipboard clipboard, int minY, int maxY) {
BlockVector3 dim = clipboard.getDimensions(); 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 clipMinX = clipboard.getMinimumPoint().getBlockX();
int clipMinZ = clipboard.getMinimumPoint().getBlockZ(); int clipMinZ = clipboard.getMinimumPoint().getBlockZ();
int clipMinY = clipboard.getMinimumPoint().getBlockY(); int clipMinY = clipboard.getMinimumPoint().getBlockY();
@ -89,20 +81,18 @@ public class ScalableHeightMap implements HeightMap {
highestY = y + 1; highestY = y + 1;
} }
} }
int pointHeight = Math.min(clipMaxY, ((maxY - minY + 1) * (highestY - clipMinY)) / clipHeight);
int x = xx - clipMinX; int x = xx - clipMinX;
int z = zz - clipMinZ; 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); BufferedImage heightFile = MainUtil.readImage(stream);
int width = heightFile.getWidth(); int width = heightFile.getWidth();
int length = heightFile.getHeight(); int length = heightFile.getHeight();
Raster data = heightFile.getData(); char[][] array = new char[width][length];
byte[][] array = new byte[width][length];
double third = 1 / 3.0; double third = 1 / 3.0;
double alphaInverse = 1 / 255.0; double alphaInverse = 1 / 255.0;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
@ -110,13 +100,12 @@ public class ScalableHeightMap implements HeightMap {
int pixel = heightFile.getRGB(x, z); int pixel = heightFile.getRGB(x, z);
int red = pixel >> 16 & 0xFF; int red = pixel >> 16 & 0xFF;
int green = pixel >> 8 & 0xFF; int green = pixel >> 8 & 0xFF;
int blue = pixel >> 0 & 0xFF; int blue = pixel & 0xFF;
int alpha = pixel >> 24 & 0xFF; int alpha = pixel >> 24 & 0xFF;
int intensity = (int) (alpha * ((red + green + blue) * third) * alphaInverse); array[x][z] = (char) (alpha * ((red + green + blue) * third) * alphaInverse);
array[x][z] = (byte) intensity;
} }
} }
return new ArrayHeightMap(array, minY, maxY); return new ArrayHeightMap(array, 256d);
} }
} }