Fixes #490 (shatter brush)
This commit is contained in:
parent
bc2879df99
commit
b7e3eff59b
@ -88,8 +88,11 @@ public class ScatterBrush implements Brush {
|
|||||||
apply(editSession, placed, pos, pattern, size);
|
apply(editSession, placed, pos, pattern, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finish(editSession, placed, position, pattern, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void finish(EditSession editSession, LocalBlockVectorSet placed, Vector pos, Pattern pattern, double size) {}
|
||||||
|
|
||||||
public boolean canApply(EditSession editSession, Vector pos) {
|
public boolean canApply(EditSession editSession, Vector pos) {
|
||||||
return mask.test(pos);
|
return mask.test(pos);
|
||||||
}
|
}
|
||||||
|
@ -2,73 +2,39 @@ package com.boydti.fawe.object.brush;
|
|||||||
|
|
||||||
import com.boydti.fawe.object.PseudoRandom;
|
import com.boydti.fawe.object.PseudoRandom;
|
||||||
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
import com.boydti.fawe.object.collection.LocalBlockVectorSet;
|
||||||
import com.sk89q.worldedit.BlockVector;
|
import com.boydti.fawe.object.mask.SurfaceMask;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.MutableBlockVector;
|
import com.sk89q.worldedit.MutableBlockVector;
|
||||||
import com.sk89q.worldedit.Vector;
|
import com.sk89q.worldedit.Vector;
|
||||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.function.mask.Masks;
|
import com.sk89q.worldedit.function.mask.Masks;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
|
||||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
|
||||||
|
|
||||||
public class ShatterBrush implements Brush {
|
public class ShatterBrush extends ScatterBrush {
|
||||||
private final int count;
|
private final MutableBlockVector mutable = new MutableBlockVector();
|
||||||
|
|
||||||
public ShatterBrush(int count) {
|
public ShatterBrush(int count) {
|
||||||
this.count = count;
|
super(count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, final Vector position, Pattern pattern, double size) throws MaxChangedBlocksException {
|
public void apply(final EditSession editSession, final LocalBlockVectorSet placed, final Vector position, Pattern p, double size) throws MaxChangedBlocksException {
|
||||||
// We'll want this to be somewhat circular, so the cuboid needs to fit.
|
|
||||||
int r2Radius = (int) Math.ceil(size * Math.sqrt(2));
|
|
||||||
int radius2 = (int) (Math.ceil(r2Radius * r2Radius));
|
|
||||||
Vector bot = new MutableBlockVector(position.subtract(size, size, size));
|
|
||||||
Vector top = new MutableBlockVector(position.add(size, size, size));
|
|
||||||
CuboidRegion region = new CuboidRegion(bot, top);
|
|
||||||
// We'll want to use a fast random
|
|
||||||
PseudoRandom random = new PseudoRandom();
|
|
||||||
// We don't need double precision, so use a BlockVector
|
|
||||||
BlockVector min = region.getMinimumPoint().toBlockVector();
|
|
||||||
BlockVector max = region.getMaximumPoint().toBlockVector();
|
|
||||||
// Let's keep it inside the brush radius
|
|
||||||
int dx = max.getBlockX() - min.getBlockX() + 1;
|
|
||||||
int dy = max.getBlockY() - min.getBlockY() + 1;
|
|
||||||
int dz = max.getBlockZ() - min.getBlockZ() + 1;
|
|
||||||
// We'll store the points in a set
|
|
||||||
LocalBlockVectorSet queue = new LocalBlockVectorSet();
|
|
||||||
// User could select a single block and try to create 10 points = infinite loop
|
|
||||||
// To avoid being stuck in an infinite loop, let's stop after 5 collisions
|
|
||||||
int maxFails = 5;
|
|
||||||
for (int added = 0; added < count;) {
|
|
||||||
int x = (int) (random.nextDouble() * dx) + min.getBlockX();
|
|
||||||
int z = (int) (random.nextDouble() * dz) + min.getBlockZ();
|
|
||||||
int y = editSession.getHighestTerrainBlock(x, z, 0, 255);
|
|
||||||
// Check the adjacent blocks efficiently (loops over the adjacent blocks, or the set; whichever is faster)
|
|
||||||
if (!queue.containsRadius(x, y, z, 1)) {
|
|
||||||
added++;
|
|
||||||
queue.add(x, y, z);
|
|
||||||
} else if (maxFails-- <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Ideally we'd calculate all the bisecting planes, but that's complex to program
|
@Override
|
||||||
// With this algorithm compute time depends on the number of blocks rather than the number of points
|
public void finish(EditSession editSession, LocalBlockVectorSet placed, final Vector position, Pattern pattern, double size) {
|
||||||
// - Expand from each point (block by block) until there is a collision
|
int radius2 = (int) (size * size);
|
||||||
{
|
|
||||||
// Keep track of where we've visited
|
// Keep track of where we've visited
|
||||||
LocalBlockVectorSet visited = queue;
|
|
||||||
LocalBlockVectorSet tmp = new LocalBlockVectorSet();
|
LocalBlockVectorSet tmp = new LocalBlockVectorSet();
|
||||||
// Individual frontier for each point
|
// Individual frontier for each point
|
||||||
LocalBlockVectorSet[] frontiers = new LocalBlockVectorSet[queue.size()];
|
LocalBlockVectorSet[] frontiers = new LocalBlockVectorSet[placed.size()];
|
||||||
// Keep track of where each frontier has visited
|
// Keep track of where each frontier has visited
|
||||||
LocalBlockVectorSet[] frontiersVisited = new LocalBlockVectorSet[queue.size()];
|
LocalBlockVectorSet[] frontiersVisited = new LocalBlockVectorSet[placed.size()];
|
||||||
// Initiate the frontier with the starting points
|
// Initiate the frontier with the starting points
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Vector pos : queue) {
|
for (Vector pos : placed) {
|
||||||
LocalBlockVectorSet set = new LocalBlockVectorSet();
|
LocalBlockVectorSet set = new LocalBlockVectorSet();
|
||||||
set.add(pos);
|
set.add(pos);
|
||||||
frontiers[i] = set;
|
frontiers[i] = set;
|
||||||
@ -81,6 +47,7 @@ public class ShatterBrush implements Brush {
|
|||||||
mask = Masks.alwaysTrue();
|
mask = Masks.alwaysTrue();
|
||||||
}
|
}
|
||||||
final Mask finalMask = mask;
|
final Mask finalMask = mask;
|
||||||
|
final SurfaceMask surfaceTest = new SurfaceMask(editSession);
|
||||||
// Expand
|
// Expand
|
||||||
boolean notEmpty = true;
|
boolean notEmpty = true;
|
||||||
while (notEmpty) {
|
while (notEmpty) {
|
||||||
@ -98,7 +65,8 @@ public class ShatterBrush implements Brush {
|
|||||||
finalTmp.add(x, y, z);
|
finalTmp.add(x, y, z);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Vector direction : BreadthFirstSearch.DEFAULT_DIRECTIONS) {
|
for (int i = 0; i < BreadthFirstSearch.DIAGONAL_DIRECTIONS.length; i++) {
|
||||||
|
Vector direction = BreadthFirstSearch.DIAGONAL_DIRECTIONS[i];
|
||||||
int x2 = x + direction.getBlockX();
|
int x2 = x + direction.getBlockX();
|
||||||
int y2 = y + direction.getBlockY();
|
int y2 = y + direction.getBlockY();
|
||||||
int z2 = z + direction.getBlockZ();
|
int z2 = z + direction.getBlockZ();
|
||||||
@ -108,9 +76,10 @@ public class ShatterBrush implements Brush {
|
|||||||
int dz = position.getBlockZ() - z2;
|
int dz = position.getBlockZ() - z2;
|
||||||
int dSqr = (dx * dx) + (dy * dy) + (dz * dz);
|
int dSqr = (dx * dx) + (dy * dy) + (dz * dz);
|
||||||
if (dSqr <= radius2) {
|
if (dSqr <= radius2) {
|
||||||
if (finalMask.test(MutableBlockVector.get(x2, y2, z2))) {
|
MutableBlockVector v = mutable.setComponents(x2, y2, z2);
|
||||||
|
if (surfaceTest.test(v) && finalMask.test(v)) {
|
||||||
// (collision) If it's visited and part of another frontier, set the block
|
// (collision) If it's visited and part of another frontier, set the block
|
||||||
if (!visited.add(x2, y2, z2)) {
|
if (!placed.add(x2, y2, z2)) {
|
||||||
if (!frontierVisited.contains(x2, y2, z2)) {
|
if (!frontierVisited.contains(x2, y2, z2)) {
|
||||||
editSession.setBlock(x2, y2, z2, pattern);
|
editSession.setBlock(x2, y2, z2, pattern);
|
||||||
}
|
}
|
||||||
@ -131,5 +100,4 @@ public class ShatterBrush implements Brush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.boydti.fawe.object.mask;
|
||||||
|
|
||||||
|
import com.boydti.fawe.FaweCache;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
|
||||||
|
public class SurfaceMask extends AdjacentAnyMask {
|
||||||
|
public SurfaceMask(Extent extent) {
|
||||||
|
super(extent);
|
||||||
|
for (int id = 0; id < 256; id++) {
|
||||||
|
if (FaweCache.canPassThrough(id, 0)) {
|
||||||
|
add(new BaseBlock(id, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Vector v) {
|
||||||
|
BaseBlock block = getExtent().getBlock(v);
|
||||||
|
return !test(block.getId()) && super.test(v);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user