Various minor
Stencil/Color brush can now be used on any surface, not just the top Optimize adjacency and radius mask
This commit is contained in:
parent
0434755333
commit
5bbc381dd0
@ -119,7 +119,6 @@ public class FawePrimitiveBinding extends BindingHelper {
|
||||
try {
|
||||
return Double.parseDouble(input);
|
||||
} catch (NumberFormatException e1) {
|
||||
System.out.println("NUMBER FORMAT EXCEPTION " + e1);
|
||||
try {
|
||||
Expression expression = Expression.compile(input);
|
||||
return expression.evaluate();
|
||||
|
@ -1,56 +1,117 @@
|
||||
package com.boydti.fawe.object.brush;
|
||||
|
||||
import com.boydti.fawe.object.PseudoRandom;
|
||||
import com.boydti.fawe.object.mask.AdjacentAnyMask;
|
||||
import com.boydti.fawe.object.mask.RadiusMask;
|
||||
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.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||
import com.sk89q.worldedit.function.RegionFunction;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
||||
import com.sk89q.worldedit.function.operation.Operations;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StencilBrush extends HeightBrush {
|
||||
private final boolean onlyWhite;
|
||||
private final int depth;
|
||||
|
||||
public StencilBrush(InputStream stream, int depth, 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);
|
||||
this.onlyWhite = onlyWhite;
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(EditSession editSession, Vector position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException {
|
||||
final int cx = position.getBlockX();
|
||||
final int cy = position.getBlockY();
|
||||
final int cz = position.getBlockZ();
|
||||
int size = (int) sizeDouble;
|
||||
Mask mask = new ExistingBlockMask(editSession);
|
||||
|
||||
int maxY = editSession.getMaxY();
|
||||
double scale = (yscale / sizeDouble) * (maxY + 1);
|
||||
heightMap.setSize(size);
|
||||
int cutoff = onlyWhite ? maxY : 0;
|
||||
|
||||
for (int x = -size; x <= size; x++) {
|
||||
int xx = position.getBlockX() + x;
|
||||
for (int z = -size; z <= size; z++) {
|
||||
double raise;
|
||||
switch (rotation) {
|
||||
default:raise = heightMap.getHeight(x, z); break;
|
||||
case 1: raise = heightMap.getHeight(z, x); break;
|
||||
case 2: raise = heightMap.getHeight(-x, -z); break;
|
||||
case 3: raise = heightMap.getHeight(-z, -x);break;
|
||||
}
|
||||
int val = (int) Math.ceil(raise * scale);
|
||||
if (val <= cutoff) {
|
||||
continue;
|
||||
}
|
||||
if (val >= 255 || PseudoRandom.random.random(maxY) < val) {
|
||||
int zz = position.getBlockZ() + z;
|
||||
int y = editSession.getNearestSurfaceTerrainBlock(xx, zz, position.getBlockY(), 0, maxY);
|
||||
for (int i = 0; i < depth; i++) {
|
||||
editSession.setBlock(xx, y - i, zz, pattern);
|
||||
final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0)));
|
||||
final SolidBlockMask solid = new SolidBlockMask(editSession);
|
||||
final RadiusMask radius = new RadiusMask(0, size);
|
||||
RecursiveVisitor visitor = new RecursiveVisitor(new Mask() {
|
||||
@Override
|
||||
public boolean test(Vector vector) {
|
||||
if (solid.test(vector) && radius.test(vector)) {
|
||||
Vector dir = adjacent.direction(vector);
|
||||
if (dir != null) {
|
||||
int dx = vector.getBlockX() - cx;
|
||||
int dy = vector.getBlockY() - cy;
|
||||
int dz = vector.getBlockZ() - cz;
|
||||
if (dy != 0) {
|
||||
if (dir.getBlockX() != 0) {
|
||||
dx += dir.getBlockX() * dy;
|
||||
} else if (dir.getBlockZ() != 0) {
|
||||
dz += dir.getBlockZ() * dy;
|
||||
}
|
||||
}
|
||||
double raise = heightMap.getHeight(dx, dz);
|
||||
int val = (int) Math.ceil(raise * scale);
|
||||
if (val <= cutoff) {
|
||||
return true;
|
||||
}
|
||||
if (val >= 255 || PseudoRandom.random.random(maxY) < val) {
|
||||
editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}, new RegionFunction() {
|
||||
@Override
|
||||
public boolean apply(Vector vector) throws WorldEditException {
|
||||
return true;
|
||||
}
|
||||
}, Integer.MAX_VALUE, editSession);
|
||||
visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS));
|
||||
visitor.visit(position);
|
||||
Operations.completeBlindly(visitor);
|
||||
|
||||
// Mask mask = new ExistingBlockMask(editSession);
|
||||
// int maxY = editSession.getMaxY();
|
||||
// double scale = (yscale / sizeDouble) * (maxY + 1);
|
||||
// heightMap.setSize(size);
|
||||
// int cutoff = onlyWhite ? maxY : 0;
|
||||
//
|
||||
// for (int x = -size; x <= size; x++) {
|
||||
// int xx = position.getBlockX() + x;
|
||||
// for (int z = -size; z <= size; z++) {
|
||||
// double raise;
|
||||
// switch (rotation) {
|
||||
// default:raise = heightMap.getHeight(x, z); break;
|
||||
// case 1: raise = heightMap.getHeight(z, x); break;
|
||||
// case 2: raise = heightMap.getHeight(-x, -z); break;
|
||||
// case 3: raise = heightMap.getHeight(-z, -x);break;
|
||||
// }
|
||||
// int val = (int) Math.ceil(raise * scale);
|
||||
// if (val <= cutoff) {
|
||||
// continue;
|
||||
// }
|
||||
// if (val >= 255 || PseudoRandom.random.random(maxY) < val) {
|
||||
// int zz = position.getBlockZ() + z;
|
||||
// int y = editSession.getNearestSurfaceTerrainBlock(xx, zz, position.getBlockY(), 0, maxY);
|
||||
// for (int i = 0; i < depth; i++) {
|
||||
// editSession.setBlock(xx, y - i, zz, pattern);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private void apply(double val) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.boydti.fawe.object.mask;
|
||||
|
||||
import com.sk89q.worldedit.MutableBlockVector;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Just an optimized version of the Adjacent Mask for single adjacency
|
||||
*/
|
||||
public class AdjacentAnyMask extends BlockMask {
|
||||
public AdjacentAnyMask(Extent extent, Collection<BaseBlock> blocks) {
|
||||
super(extent, blocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Vector v) {
|
||||
int x = v.getBlockX();
|
||||
int y = v.getBlockY();
|
||||
int z = v.getBlockZ();
|
||||
v.mutY(y + 1);
|
||||
if (super.test(v)) { v.mutY(y); return true; }
|
||||
v.mutY(y - 1);
|
||||
if (super.test(v)) { v.mutY(y); return true; }
|
||||
v.mutY(y);
|
||||
v.mutX(x + 1);
|
||||
if (super.test(v)) { v.mutX(x); return true; }
|
||||
v.mutX(x - 1);
|
||||
if (super.test(v)) { v.mutX(x); return true; }
|
||||
v.mutX(x);
|
||||
v.mutZ(z + 1);
|
||||
if (super.test(v)) { v.mutZ(z); return true; }
|
||||
v.mutZ(z - 1);
|
||||
if (super.test(v)) { v.mutZ(z); return true; }
|
||||
v.mutZ(z);
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector direction(Vector v) {
|
||||
int x = v.getBlockX();
|
||||
int y = v.getBlockY();
|
||||
int z = v.getBlockZ();
|
||||
v.mutY(y + 1);
|
||||
if (super.test(v)) { v.mutY(y); return MutableBlockVector.get(0, 1, 0); }
|
||||
v.mutY(y - 1);
|
||||
if (super.test(v)) { v.mutY(y); return MutableBlockVector.get(0, -1, 0); }
|
||||
v.mutY(y);
|
||||
v.mutX(x + 1);
|
||||
if (super.test(v)) { v.mutX(x); return MutableBlockVector.get(1, 0, 0); }
|
||||
v.mutX(x - 1);
|
||||
if (super.test(v)) { v.mutX(x); return MutableBlockVector.get(-1, 0, 0); }
|
||||
v.mutX(x);
|
||||
v.mutZ(z + 1);
|
||||
if (super.test(v)) { v.mutZ(z); return MutableBlockVector.get(0, 0, 1); }
|
||||
v.mutZ(z - 1);
|
||||
if (super.test(v)) { v.mutZ(z); return MutableBlockVector.get(0, 0, - 1); }
|
||||
v.mutZ(z);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.boydti.fawe.object.mask;
|
||||
|
||||
import com.sk89q.worldedit.MutableBlockVector;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Mask2D;
|
||||
@ -24,19 +25,19 @@ public class RadiusMask implements Mask, ResettableMask{
|
||||
@Override
|
||||
public boolean test(Vector to) {
|
||||
if (pos == null) {
|
||||
pos = new Vector(to);
|
||||
pos = new MutableBlockVector(to);
|
||||
}
|
||||
int dx = Math.abs((int) (pos.getX() - to.getX()));
|
||||
int dy = Math.abs((int) (pos.getY() - to.getY()));
|
||||
int dz = Math.abs((int) (pos.getZ() - to.getZ()));
|
||||
int dx = pos.getBlockX() - to.getBlockX();
|
||||
int d = dx * dx;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
if (d > maxSqr) {
|
||||
return false;
|
||||
}
|
||||
int dz = pos.getBlockZ() - to.getBlockZ();
|
||||
d += dz * dz;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
if (d > maxSqr) {
|
||||
return false;
|
||||
}
|
||||
int dy = pos.getBlockY() - to.getBlockY();
|
||||
d += dy * dy;
|
||||
if (d < minSqr || d > maxSqr) {
|
||||
return false;
|
||||
|
@ -542,16 +542,16 @@ public class BrushCommands {
|
||||
max = -1
|
||||
)
|
||||
@CommandPermissions("worldedit.brush.stencil")
|
||||
public void stencilBrush(Player player, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("1") double depth, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite) throws WorldEditException {
|
||||
public void stencilBrush(Player player, EditSession editSession, LocalSession session, Pattern fill, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale, @Switch('w') boolean onlyWhite) throws WorldEditException {
|
||||
worldEdit.checkMaxBrushRadius(radius);
|
||||
BrushTool tool = session.getBrushTool(player);
|
||||
InputStream stream = getHeightmapStream(filename);
|
||||
tool.setFill(fill);
|
||||
tool.setSize(radius);
|
||||
try {
|
||||
tool.setBrush(new StencilBrush(stream, (int) depth, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player);
|
||||
tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, filename.equalsIgnoreCase("#clipboard") ? session.getClipboard().getClipboard() : null), "worldedit.brush.height", player);
|
||||
} catch (EmptyClipboardException ignore) {
|
||||
tool.setBrush(new StencilBrush(stream, (int) depth, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player);
|
||||
tool.setBrush(new StencilBrush(stream, rotation, yscale, onlyWhite, null), "worldedit.brush.height", player);
|
||||
}
|
||||
|
||||
player.print(BBC.getPrefix() + BBC.BRUSH_STENCIL.f(radius));
|
||||
|
@ -241,7 +241,7 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
||||
return new WallMask(extent, Arrays.asList(new BaseBlock(0)), 1, 8);
|
||||
case "#surface":
|
||||
masks.add(new ExistingBlockMask(extent));
|
||||
return new AdjacentMask(extent, Arrays.asList(new BaseBlock(0)), 1, 8);
|
||||
return new AdjacentAnyMask(extent, Arrays.asList(new BaseBlock(0)));
|
||||
default:
|
||||
throw new SuggestInputParseException(input, HASHTAG_MASKS);
|
||||
}
|
||||
@ -297,6 +297,9 @@ public class DefaultMaskParser extends FaweParser<Mask> {
|
||||
}
|
||||
}
|
||||
if (firstChar == '~') {
|
||||
if (requiredMax >= 8 && requiredMin == 1) {
|
||||
return new AdjacentAnyMask(extent, worldEdit.getBlockFactory().parseFromListInput(split[0], tempContext));
|
||||
}
|
||||
return new AdjacentMask(extent, worldEdit.getBlockFactory().parseFromListInput(split[0], tempContext), requiredMin, requiredMax);
|
||||
} else {
|
||||
return new WallMask(extent, worldEdit.getBlockFactory().parseFromListInput(input.substring(1), tempContext), requiredMin, requiredMax);
|
||||
|
@ -45,7 +45,9 @@ public interface Mask extends com.sk89q.worldedit.masks.Mask {
|
||||
* @return a 2D mask version or {@code null} if this mask can't be 2D
|
||||
*/
|
||||
@Nullable
|
||||
Mask2D toMask2D();
|
||||
default Mask2D toMask2D() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default void prepare(LocalSession session, LocalPlayer player, Vector target) {}
|
||||
|
||||
|
@ -16,11 +16,14 @@ import com.sk89q.worldedit.function.operation.RunContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BreadthFirstSearch implements Operation {
|
||||
|
||||
public static Vector[] DEFAULT_DIRECTIONS = new Vector[6];
|
||||
public static final Vector[] DEFAULT_DIRECTIONS = new Vector[6];
|
||||
public static final Vector[] DIAGONAL_DIRECTIONS;
|
||||
static {
|
||||
DEFAULT_DIRECTIONS[0] = (new MutableBlockVector(0, -1, 0));
|
||||
DEFAULT_DIRECTIONS[1] = (new MutableBlockVector(0, 1, 0));
|
||||
@ -28,10 +31,30 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
DEFAULT_DIRECTIONS[3] = (new MutableBlockVector(1, 0, 0));
|
||||
DEFAULT_DIRECTIONS[4] = (new MutableBlockVector(0, 0, -1));
|
||||
DEFAULT_DIRECTIONS[5] = (new MutableBlockVector(0, 0, 1));
|
||||
List<MutableBlockVector> list = new ArrayList<>();
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
if (x != 0 || y != 0 || z != 0) {
|
||||
MutableBlockVector pos = new MutableBlockVector(x, y, z);
|
||||
if (!list.contains(pos)) {
|
||||
list.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(list, new Comparator<Vector>() {
|
||||
@Override
|
||||
public int compare(Vector o1, Vector o2) {
|
||||
return (int) Math.signum(o1.lengthSq() - o2.lengthSq());
|
||||
}
|
||||
});
|
||||
DIAGONAL_DIRECTIONS = list.toArray(new Vector[list.size()]);
|
||||
}
|
||||
|
||||
private final RegionFunction function;
|
||||
private final List<Vector> directions = new ArrayList<>();
|
||||
private List<Vector> directions = new ArrayList<>();
|
||||
private BlockVectorSet visited;
|
||||
private final MappedFaweQueue mFaweQueue;
|
||||
private BlockVectorSet queue;
|
||||
@ -62,6 +85,10 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
return this.directions;
|
||||
}
|
||||
|
||||
public void setDirections(List<Vector> directions) {
|
||||
this.directions = directions;
|
||||
}
|
||||
|
||||
private IntegerTrio[] getIntDirections() {
|
||||
IntegerTrio[] array = new IntegerTrio[directions.size()];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
@ -91,8 +118,6 @@ public abstract class BreadthFirstSearch implements Operation {
|
||||
return visited.contains(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Operation resume(RunContext run) throws WorldEditException {
|
||||
MutableBlockVector mutable = new MutableBlockVector();
|
||||
|
Loading…
Reference in New Issue
Block a user