mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2024-11-25 12:16:00 +01:00
Snow heightmap!
This commit is contained in:
parent
1b71bcd4a1
commit
eedc3f4069
@ -99,6 +99,7 @@ import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
|||||||
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
import com.sk89q.worldedit.function.visitor.RegionVisitor;
|
||||||
import com.sk89q.worldedit.history.change.EntityCreate;
|
import com.sk89q.worldedit.history.change.EntityCreate;
|
||||||
import com.sk89q.worldedit.history.change.EntityRemove;
|
import com.sk89q.worldedit.history.change.EntityRemove;
|
||||||
|
import com.sk89q.worldedit.math.convolution.HeightMap;
|
||||||
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation;
|
||||||
import com.sk89q.worldedit.math.transform.AffineTransform;
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
@ -432,6 +433,7 @@ public class Fawe {
|
|||||||
ExtentEntityCopy.inject(); // Async entity create fix
|
ExtentEntityCopy.inject(); // Async entity create fix
|
||||||
// Transforms
|
// Transforms
|
||||||
FlattenedClipboardTransform.inject(); // public access
|
FlattenedClipboardTransform.inject(); // public access
|
||||||
|
HeightMap.inject(); // Optimizations + Features
|
||||||
// Entity create/remove
|
// Entity create/remove
|
||||||
EntityCreate.inject(); // Optimizations
|
EntityCreate.inject(); // Optimizations
|
||||||
EntityRemove.inject(); // Optimizations
|
EntityRemove.inject(); // Optimizations
|
||||||
|
@ -13,8 +13,8 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
public class FlattenBrush extends HeightBrush {
|
public class FlattenBrush extends HeightBrush {
|
||||||
|
|
||||||
public FlattenBrush(InputStream stream, int rotation, double yscale, Clipboard clipboard, ScalableHeightMap.Shape shape) {
|
public FlattenBrush(InputStream stream, int rotation, double yscale, boolean layers, Clipboard clipboard, ScalableHeightMap.Shape shape) {
|
||||||
super(stream, rotation, yscale, clipboard, shape);
|
super(stream, rotation, yscale, layers, clipboard, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -26,6 +26,6 @@ public class FlattenBrush extends HeightBrush {
|
|||||||
}
|
}
|
||||||
HeightMap map = getHeightMap();
|
HeightMap map = getHeightMap();
|
||||||
map.setSize(size);
|
map.setSize(size);
|
||||||
map.perform(editSession, mask, position, size, rotation, yscale, true, true);
|
map.perform(editSession, mask, position, size, rotation, yscale, true, true, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,16 @@ public class HeightBrush implements Brush {
|
|||||||
private boolean randomRotate;
|
private boolean randomRotate;
|
||||||
public final int rotation;
|
public final int rotation;
|
||||||
public final double yscale;
|
public final double yscale;
|
||||||
|
public final boolean layers;
|
||||||
|
|
||||||
public HeightBrush(InputStream stream, int rotation, double yscale, Clipboard clipboard) {
|
public HeightBrush(InputStream stream, int rotation, double yscale, boolean layers, Clipboard clipboard) {
|
||||||
this(stream, rotation, yscale, clipboard, ScalableHeightMap.Shape.CONE);
|
this(stream, rotation, yscale, layers, clipboard, ScalableHeightMap.Shape.CONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeightBrush(InputStream stream, int rotation, double yscale, Clipboard clipboard, ScalableHeightMap.Shape shape) {
|
public HeightBrush(InputStream stream, int rotation, double yscale, boolean layers, Clipboard clipboard, ScalableHeightMap.Shape shape) {
|
||||||
this.rotation = (rotation / 90) % 4;
|
this.rotation = (rotation / 90) % 4;
|
||||||
this.yscale = yscale;
|
this.yscale = yscale;
|
||||||
|
this.layers = layers;
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
try {
|
try {
|
||||||
heightMap = ScalableHeightMap.fromPNG(stream);
|
heightMap = ScalableHeightMap.fromPNG(stream);
|
||||||
@ -68,6 +70,6 @@ public class HeightBrush implements Brush {
|
|||||||
}
|
}
|
||||||
HeightMap map = getHeightMap();
|
HeightMap map = getHeightMap();
|
||||||
map.setSize(size);
|
map.setSize(size);
|
||||||
map.perform(editSession, mask, position, size, rotation, yscale, true, false);
|
map.perform(editSession, mask, position, size, rotation, yscale, true, false, layers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public class StencilBrush extends HeightBrush {
|
|||||||
private final boolean onlyWhite;
|
private final boolean onlyWhite;
|
||||||
|
|
||||||
public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) {
|
public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) {
|
||||||
super(stream, rotation, yscale, clipboard);
|
super(stream, rotation, yscale, false, clipboard);
|
||||||
this.onlyWhite = onlyWhite;
|
this.onlyWhite = onlyWhite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.boydti.fawe.util.MainUtil;
|
|||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.Vector;
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.WorldVector;
|
import com.sk89q.worldedit.WorldVector;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.internal.LocalWorldAdapter;
|
import com.sk89q.worldedit.internal.LocalWorldAdapter;
|
||||||
@ -19,12 +20,12 @@ public interface HeightMap {
|
|||||||
public void setSize(int size);
|
public void setSize(int size);
|
||||||
|
|
||||||
|
|
||||||
default void perform(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException {
|
default void perform(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards, boolean layers) throws MaxChangedBlocksException {
|
||||||
int[] data = generateHeightData(session, mask, pos, size, rotationMode, yscale, smooth, towards);
|
int[][] data = generateHeightData(session, mask, pos, size, rotationMode, yscale, smooth, towards, layers);
|
||||||
applyHeightMapData(data, session, mask, pos, size, rotationMode, yscale, smooth, towards);
|
applyHeightMapData(data, session, mask, pos, size, rotationMode, yscale, smooth, towards, layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
default void applyHeightMapData(int[] data, EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) throws MaxChangedBlocksException {
|
default void applyHeightMapData(int[][] data, EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards, boolean layers) throws MaxChangedBlocksException {
|
||||||
Vector top = session.getMaximumPoint();
|
Vector top = session.getMaximumPoint();
|
||||||
int maxY = top.getBlockY();
|
int maxY = top.getBlockY();
|
||||||
int diameter = 2 * size + 1;
|
int diameter = 2 * size + 1;
|
||||||
@ -32,19 +33,29 @@ public interface HeightMap {
|
|||||||
WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size));
|
WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, maxY, size));
|
||||||
Vector max = pos.add(size, maxY, size);
|
Vector max = pos.add(size, maxY, size);
|
||||||
Region region = new CuboidRegion(session.getWorld(), min, max);
|
Region region = new CuboidRegion(session.getWorld(), min, max);
|
||||||
com.sk89q.worldedit.math.convolution.HeightMap heightMap = new com.sk89q.worldedit.math.convolution.HeightMap(session, region, false);
|
com.sk89q.worldedit.math.convolution.HeightMap heightMap = new com.sk89q.worldedit.math.convolution.HeightMap(session, region, data[0]);
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
try {
|
try {
|
||||||
HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1));
|
HeightMapFilter filter = (HeightMapFilter) HeightMapFilter.class.getConstructors()[0].newInstance(GaussianKernel.class.getConstructors()[0].newInstance(5, 1));
|
||||||
data = filter.filter(data, diameter, diameter);
|
data[1] = filter.filter(data[1], diameter, diameter);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
MainUtil.handleError(e);
|
MainUtil.handleError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
heightMap.apply(data);
|
try {
|
||||||
|
if (layers) {
|
||||||
|
heightMap.applyLayers(data[1]);
|
||||||
|
} else {
|
||||||
|
heightMap.apply(data[1]);
|
||||||
|
}
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (WorldEditException e2) {
|
||||||
|
throw new RuntimeException(e2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default int[] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards) {
|
default int[][] generateHeightData(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth, boolean towards, final boolean layers) {
|
||||||
Vector top = session.getMaximumPoint();
|
Vector top = session.getMaximumPoint();
|
||||||
int maxY = top.getBlockY();
|
int maxY = top.getBlockY();
|
||||||
int diameter = 2 * size + 1;
|
int diameter = 2 * size + 1;
|
||||||
@ -53,7 +64,12 @@ public interface HeightMap {
|
|||||||
int centerY = pos.getBlockY();
|
int centerY = pos.getBlockY();
|
||||||
int endY = pos.getBlockY() + size;
|
int endY = pos.getBlockY() + size;
|
||||||
int startY = pos.getBlockY() - size;
|
int startY = pos.getBlockY() - size;
|
||||||
int[] newData = new int[diameter * diameter];
|
int[] oldData = new int[diameter * diameter];
|
||||||
|
int[] newData = new int[oldData.length];
|
||||||
|
if (layers) { // Pixel accuracy
|
||||||
|
centerY <<= 3;
|
||||||
|
maxY <<= 3;
|
||||||
|
}
|
||||||
Vector mutablePos = new Vector(0, 0, 0);
|
Vector mutablePos = new Vector(0, 0, 0);
|
||||||
if (towards) {
|
if (towards) {
|
||||||
double sizePow = Math.pow(size, yscale);
|
double sizePow = Math.pow(size, yscale);
|
||||||
@ -79,7 +95,13 @@ public interface HeightMap {
|
|||||||
raise = getHeight(-z, -x);
|
raise = getHeight(-z, -x);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, 255);
|
int height;
|
||||||
|
if (layers) {
|
||||||
|
height = session.getNearestSurfaceLayer(xx, zz, pos.getBlockY(), 0, maxY);
|
||||||
|
} else {
|
||||||
|
height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, maxY);
|
||||||
|
}
|
||||||
|
oldData[index] = height;
|
||||||
if (height == 0) {
|
if (height == 0) {
|
||||||
newData[index] = centerY;
|
newData[index] = centerY;
|
||||||
continue;
|
continue;
|
||||||
@ -115,18 +137,24 @@ public interface HeightMap {
|
|||||||
raise = getHeight(-z, -x);
|
raise = getHeight(-z, -x);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, maxY);
|
int height;
|
||||||
|
if (layers) {
|
||||||
|
height = session.getNearestSurfaceLayer(xx, zz, pos.getBlockY(), 0, maxY);
|
||||||
|
} else {
|
||||||
|
height = session.getNearestSurfaceTerrainBlock(xx, zz, pos.getBlockY(), 0, 255);
|
||||||
|
}
|
||||||
|
oldData[index] = height;
|
||||||
if (height == 0) {
|
if (height == 0) {
|
||||||
newData[index] = centerY;
|
newData[index] = centerY;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
raise = (yscale * raise);
|
raise = (yscale * raise);
|
||||||
int random = PseudoRandom.random.random(maxY + 1) < (int) ((raise - (int) raise) * (maxY + 1)) ? 1 : 0;
|
int random = PseudoRandom.random.random(256) < (int) ((raise - (int) raise) * (256)) ? 1 : 0;
|
||||||
int newHeight = height + (int) raise + random;
|
int newHeight = height + (int) raise + random;
|
||||||
newData[index] = newHeight;
|
newData[index] = newHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newData;
|
return new int[][] {oldData, newData};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -981,6 +981,52 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
|||||||
return this.getHighestTerrainBlock(x, z, minY, maxY, false);
|
return this.getHighestTerrainBlock(x, z, minY, maxY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
|
||||||
|
int clearanceAbove = maxY - y;
|
||||||
|
int clearanceBelow = y - minY;
|
||||||
|
int clearance = Math.min(clearanceAbove, clearanceBelow);
|
||||||
|
|
||||||
|
BaseBlock block = getBlock(x, y, z);
|
||||||
|
boolean state = FaweCache.isLiquidOrGas(block.getId());
|
||||||
|
int data1 = block.getData();
|
||||||
|
int data2 = block.getData();
|
||||||
|
int offset = state ? 0 : 1;
|
||||||
|
for (int d = 0; d <= clearance; d++) {
|
||||||
|
int y1 = y + d;
|
||||||
|
block = getLazyBlock(x, y1, z);
|
||||||
|
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
|
||||||
|
return ((y1 - offset) << 3) - (7 - (state ? block.getData() : data1));
|
||||||
|
}
|
||||||
|
data1 = block.getData();
|
||||||
|
int y2 = y - d;
|
||||||
|
block = getLazyBlock(x, y2, z);
|
||||||
|
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
|
||||||
|
return ((y2 + offset) << 3) - (7 - (state ? block.getData() : data2));
|
||||||
|
}
|
||||||
|
data2 = block.getData();
|
||||||
|
}
|
||||||
|
if (clearanceAbove != clearanceBelow) {
|
||||||
|
if (clearanceAbove < clearanceBelow) {
|
||||||
|
for (int layer = y - clearance - 1; layer >= minY; layer--) {
|
||||||
|
block = getLazyBlock(x, layer, z);
|
||||||
|
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
|
||||||
|
return ((layer + offset) << 3) - (7 - (state ? block.getData() : data1));
|
||||||
|
}
|
||||||
|
data1 = block.getData();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
|
||||||
|
block = getLazyBlock(x, layer, z);
|
||||||
|
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
|
||||||
|
return ((layer - offset) << 3) - (7 - (state ? block.getData() : data2));
|
||||||
|
}
|
||||||
|
data2 = block.getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxY << 4;
|
||||||
|
}
|
||||||
|
|
||||||
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
|
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
|
||||||
int clearanceAbove = maxY - y;
|
int clearanceAbove = maxY - y;
|
||||||
int clearanceBelow = y - minY;
|
int clearanceBelow = y - minY;
|
||||||
|
@ -714,13 +714,15 @@ public class BrushCommands {
|
|||||||
flags = "h",
|
flags = "h",
|
||||||
desc = "Height brush",
|
desc = "Height brush",
|
||||||
help =
|
help =
|
||||||
"This brush raises and lowers land.\n",
|
"This brush raises and lowers land.\n" +
|
||||||
|
"The -r flag enables random off-axis rotation\n" +
|
||||||
|
"The -l flag will work on snow layers",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 4
|
max = 4
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.height")
|
@CommandPermissions("worldedit.brush.height")
|
||||||
public void heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate) throws WorldEditException {
|
public void heightBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers) throws WorldEditException {
|
||||||
terrainBrush(player, session, radius, filename, rotation, yscale, false, randomRotate, ScalableHeightMap.Shape.CONE);
|
terrainBrush(player, session, radius, filename, rotation, yscale, false, randomRotate, layers, ScalableHeightMap.Shape.CONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
@ -729,28 +731,32 @@ public class BrushCommands {
|
|||||||
flags = "h",
|
flags = "h",
|
||||||
desc = "Cliff brush",
|
desc = "Cliff brush",
|
||||||
help =
|
help =
|
||||||
"This brush flattens terrain and creates cliffs.\n",
|
"This brush flattens terrain and creates cliffs.\n" +
|
||||||
|
"The -r flag enables random off-axis rotation\n" +
|
||||||
|
"The -l flag will work on snow layers",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 4
|
max = 4
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.height")
|
@CommandPermissions("worldedit.brush.height")
|
||||||
public void cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate) throws WorldEditException {
|
public void cliffBrush(Player player, LocalSession session, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('r') boolean randomRotate, @Switch('l') boolean layers) throws WorldEditException {
|
||||||
terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, ScalableHeightMap.Shape.CYLINDER);
|
terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, layers, ScalableHeightMap.Shape.CYLINDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
aliases = { "flatten", "flatmap", "flat" },
|
aliases = { "flatten", "flatmap", "flat" },
|
||||||
usage = "[radius] [file|#clipboard|null] [rotation] [yscale]",
|
usage = "[radius] [file|#clipboard|null] [rotation] [yscale]",
|
||||||
flags = "h",
|
flags = "h",
|
||||||
desc = "Flatten brush",
|
desc = "Flatten brush makes terrain flatter\n" +
|
||||||
|
"The -r flag enables random off-axis rotation\n" +
|
||||||
|
"The -l flag will work on snow layers",
|
||||||
help =
|
help =
|
||||||
"This brush raises and lowers land towards the clicked point\n",
|
"This brush raises and lowers land towards the clicked point\n",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 4
|
max = 4
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.height")
|
@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, @Switch('r') boolean randomRotate) throws WorldEditException {
|
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, @Switch('r') boolean randomRotate, @Switch('l') boolean layers) throws WorldEditException {
|
||||||
terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, ScalableHeightMap.Shape.CONE);
|
terrainBrush(player, session, radius, filename, rotation, yscale, true, randomRotate, layers, ScalableHeightMap.Shape.CONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getHeightmapStream(String filename) {
|
private InputStream getHeightmapStream(String filename) {
|
||||||
@ -784,7 +790,7 @@ public class BrushCommands {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, boolean randomRotate, ScalableHeightMap.Shape shape) throws WorldEditException {
|
private void terrainBrush(Player player, LocalSession session, double radius, String filename, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, ScalableHeightMap.Shape shape) throws WorldEditException {
|
||||||
worldEdit.checkMaxBrushRadius(radius);
|
worldEdit.checkMaxBrushRadius(radius);
|
||||||
InputStream stream = getHeightmapStream(filename);
|
InputStream stream = getHeightmapStream(filename);
|
||||||
BrushTool tool = session.getBrushTool(player);
|
BrushTool tool = session.getBrushTool(player);
|
||||||
@ -792,15 +798,15 @@ public class BrushCommands {
|
|||||||
HeightBrush brush;
|
HeightBrush brush;
|
||||||
if (flat) {
|
if (flat) {
|
||||||
try {
|
try {
|
||||||
brush = new FlattenBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape);
|
brush = new FlattenBrush(stream, rotation, yscale, layers, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null, shape);
|
||||||
} catch (EmptyClipboardException ignore) {
|
} catch (EmptyClipboardException ignore) {
|
||||||
brush = new FlattenBrush(stream, rotation, yscale, null, shape);
|
brush = new FlattenBrush(stream, rotation, yscale, layers, null, shape);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
brush = new HeightBrush(stream, rotation, yscale, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null);
|
brush = new HeightBrush(stream, rotation, yscale, layers, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null);
|
||||||
} catch (EmptyClipboardException ignore) {
|
} catch (EmptyClipboardException ignore) {
|
||||||
brush = new HeightBrush(stream, rotation, yscale, null);
|
brush = new HeightBrush(stream, rotation, yscale, layers, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tool.setBrush(brush, "worldedit.brush.height", player);
|
tool.setBrush(brush, "worldedit.brush.height", player);
|
||||||
|
@ -0,0 +1,242 @@
|
|||||||
|
package com.sk89q.worldedit.math.convolution;
|
||||||
|
|
||||||
|
import com.boydti.fawe.FaweCache;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows applications of Kernels onto the region's height map.
|
||||||
|
*
|
||||||
|
* <p>Currently only used for smoothing (with a GaussianKernel)</p>.
|
||||||
|
*/
|
||||||
|
public class HeightMap {
|
||||||
|
|
||||||
|
private int[] data;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
private Region region;
|
||||||
|
private EditSession session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the HeightMap
|
||||||
|
*
|
||||||
|
* @param session an edit session
|
||||||
|
* @param region the region
|
||||||
|
*/
|
||||||
|
public HeightMap(EditSession session, Region region) {
|
||||||
|
this(session, region, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeightMap(EditSession session, Region region, boolean naturalOnly) {
|
||||||
|
checkNotNull(session);
|
||||||
|
checkNotNull(region);
|
||||||
|
|
||||||
|
this.session = session;
|
||||||
|
this.region = region;
|
||||||
|
|
||||||
|
this.width = region.getWidth();
|
||||||
|
this.height = region.getLength();
|
||||||
|
|
||||||
|
int minX = region.getMinimumPoint().getBlockX();
|
||||||
|
int minY = region.getMinimumPoint().getBlockY();
|
||||||
|
int minZ = region.getMinimumPoint().getBlockZ();
|
||||||
|
int maxY = region.getMaximumPoint().getBlockY();
|
||||||
|
|
||||||
|
// Store current heightmap data
|
||||||
|
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, naturalOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeightMap(EditSession session, Region region, int[] data) {
|
||||||
|
this.session = session;
|
||||||
|
this.region = region;
|
||||||
|
|
||||||
|
this.width = region.getWidth();
|
||||||
|
this.height = region.getLength();
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the filter 'iterations' amount times.
|
||||||
|
*
|
||||||
|
* @param filter the filter
|
||||||
|
* @param iterations the number of iterations
|
||||||
|
* @return number of blocks affected
|
||||||
|
* @throws MaxChangedBlocksException
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int applyFilter(HeightMapFilter filter, int iterations) throws WorldEditException {
|
||||||
|
checkNotNull(filter);
|
||||||
|
|
||||||
|
int[] newData = new int[data.length];
|
||||||
|
System.arraycopy(data, 0, newData, 0, data.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < iterations; ++i) {
|
||||||
|
newData = filter.filter(newData, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply(newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int applyLayers(int[] data) throws WorldEditException {
|
||||||
|
checkNotNull(data);
|
||||||
|
|
||||||
|
Vector minY = region.getMinimumPoint();
|
||||||
|
int originX = minY.getBlockX();
|
||||||
|
int originY = minY.getBlockY();
|
||||||
|
int originZ = minY.getBlockZ();
|
||||||
|
|
||||||
|
int maxY = region.getMaximumPoint().getBlockY();
|
||||||
|
BaseBlock fillerAir = EditSession.nullBlock;
|
||||||
|
|
||||||
|
int blocksChanged = 0;
|
||||||
|
|
||||||
|
// Apply heightmap
|
||||||
|
int maxY4 = maxY << 4;
|
||||||
|
int index = 0;
|
||||||
|
for (int z = 0; z < height; ++z) {
|
||||||
|
int zr = z + originZ;
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
int curHeight = this.data[index];
|
||||||
|
int newHeight = Math.min(maxY4, data[index++]);
|
||||||
|
int curBlock = (curHeight) >> 3;
|
||||||
|
int newBlock = (newHeight + 7) >> 3;
|
||||||
|
int xr = x + originX;
|
||||||
|
|
||||||
|
// We are keeping the topmost blocks so take that in account for the scale
|
||||||
|
double scale = (double) (curHeight - originY) / (double) (newHeight - originY);
|
||||||
|
|
||||||
|
// Depending on growing or shrinking we need to start at the bottom or top
|
||||||
|
if (newHeight > curHeight) {
|
||||||
|
// Set the top block of the column to be the same type (this might go wrong with rounding)
|
||||||
|
BaseBlock existing = session.getBlock(xr, curBlock, zr);
|
||||||
|
|
||||||
|
// Skip water/lava
|
||||||
|
if (!FaweCache.isLiquidOrGas(existing.getId())) {
|
||||||
|
// Grow -- start from 1 below top replacing airblocks
|
||||||
|
for (int y = newBlock - 1 - originY; y >= curBlock; --y) {
|
||||||
|
int copyFrom = (int) (y * scale);
|
||||||
|
session.setBlock(xr, originY + y, zr, session.getBlock(xr, originY + copyFrom, zr));
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
int setData = newHeight & 7;
|
||||||
|
if (setData != 0) {
|
||||||
|
existing = FaweCache.getBlock(existing.getId(), setData - 1);
|
||||||
|
session.setBlock(xr, newBlock, zr, existing);
|
||||||
|
++blocksChanged;
|
||||||
|
} else {
|
||||||
|
existing = FaweCache.getBlock(existing.getId(), 7);
|
||||||
|
session.setBlock(xr, newBlock, zr, existing);
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (curHeight > newHeight) {
|
||||||
|
// Fill rest with air
|
||||||
|
for (int y = newBlock + 1; y <= ((curHeight + 7) >> 3); ++y) {
|
||||||
|
session.setBlock(xr, y, zr, fillerAir);
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
// Set the top block of the column to be the same type
|
||||||
|
// (this could otherwise go wrong with rounding)
|
||||||
|
int setData = newHeight & 7;
|
||||||
|
BaseBlock existing = session.getBlock(xr, curBlock, zr);
|
||||||
|
if (setData != 0) {
|
||||||
|
existing = FaweCache.getBlock(existing.getId(), setData - 1);
|
||||||
|
session.setBlock(xr, newBlock, zr, existing);
|
||||||
|
} else {
|
||||||
|
existing = FaweCache.getBlock(existing.getId(), 7);
|
||||||
|
session.setBlock(xr, newBlock, zr, existing);
|
||||||
|
}
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocksChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int apply(int[] data) throws WorldEditException {
|
||||||
|
checkNotNull(data);
|
||||||
|
|
||||||
|
Vector minY = region.getMinimumPoint();
|
||||||
|
int originX = minY.getBlockX();
|
||||||
|
int originY = minY.getBlockY();
|
||||||
|
int originZ = minY.getBlockZ();
|
||||||
|
|
||||||
|
int maxY = region.getMaximumPoint().getBlockY();
|
||||||
|
BaseBlock fillerAir = EditSession.nullBlock;
|
||||||
|
|
||||||
|
int blocksChanged = 0;
|
||||||
|
|
||||||
|
// Apply heightmap
|
||||||
|
int index = 0;
|
||||||
|
for (int z = 0; z < height; ++z) {
|
||||||
|
int zr = z + originZ;
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
int curHeight = this.data[index];
|
||||||
|
int newHeight = Math.min(maxY, data[index++]);
|
||||||
|
int xr = x + originX;
|
||||||
|
|
||||||
|
// We are keeping the topmost blocks so take that in account for the scale
|
||||||
|
double scale = (double) (curHeight - originY) / (double) (newHeight - originY);
|
||||||
|
|
||||||
|
// Depending on growing or shrinking we need to start at the bottom or top
|
||||||
|
if (newHeight > curHeight) {
|
||||||
|
// Set the top block of the column to be the same type (this might go wrong with rounding)
|
||||||
|
BaseBlock existing = session.getBlock(xr, curHeight, zr);
|
||||||
|
|
||||||
|
// Skip water/lava
|
||||||
|
if (!FaweCache.isLiquidOrGas(existing.getId())) {
|
||||||
|
session.setBlock(xr, newHeight, zr, existing);
|
||||||
|
++blocksChanged;
|
||||||
|
|
||||||
|
// Grow -- start from 1 below top replacing airblocks
|
||||||
|
for (int y = newHeight - 1 - originY; y >= 0; --y) {
|
||||||
|
int copyFrom = (int) (y * scale);
|
||||||
|
session.setBlock(xr, originY + y, zr, session.getBlock(xr, originY + copyFrom, zr));
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (curHeight > newHeight) {
|
||||||
|
// Shrink -- start from bottom
|
||||||
|
for (int y = 0; y < newHeight - originY; ++y) {
|
||||||
|
int copyFrom = (int) (y * scale);
|
||||||
|
session.setBlock(xr, originY + y, zr, session.getBlock(xr, originY + copyFrom, zr));
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the top block of the column to be the same type
|
||||||
|
// (this could otherwise go wrong with rounding)
|
||||||
|
session.setBlock(xr, newHeight, zr, session.getBlock(xr, curHeight, zr));
|
||||||
|
++blocksChanged;
|
||||||
|
|
||||||
|
// Fill rest with air
|
||||||
|
for (int y = newHeight + 1; y <= curHeight; ++y) {
|
||||||
|
session.setBlock(xr, y, zr, fillerAir);
|
||||||
|
++blocksChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocksChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> inject() {
|
||||||
|
return HeightMap.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user