From b7e3eff59b560778ec102ebcab6e90e44a0547a3 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Thu, 20 Apr 2017 21:02:28 +1000 Subject: [PATCH] Fixes #490 (shatter brush) --- .../fawe/object/brush/ScatterBrush.java | 3 + .../fawe/object/brush/ShatterBrush.java | 184 ++++++++---------- .../boydti/fawe/object/mask/SurfaceMask.java | 23 +++ 3 files changed, 102 insertions(+), 108 deletions(-) create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java diff --git a/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java index 5891ce8b..3b8650bf 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/ScatterBrush.java @@ -88,8 +88,11 @@ public class ScatterBrush implements Brush { 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) { return mask.test(pos); } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java index d5718bec..5617826b 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/ShatterBrush.java @@ -2,134 +2,102 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.PseudoRandom; 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.MaxChangedBlocksException; import com.sk89q.worldedit.MutableBlockVector; 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.Masks; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; -import com.sk89q.worldedit.regions.CuboidRegion; -public class ShatterBrush implements Brush { - private final int count; +public class ShatterBrush extends ScatterBrush { + private final MutableBlockVector mutable = new MutableBlockVector(); public ShatterBrush(int count) { - this.count = count; + super(count, 1); } @Override - public void build(EditSession editSession, final Vector position, Pattern pattern, 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; - } + public void apply(final EditSession editSession, final LocalBlockVectorSet placed, final Vector position, Pattern p, double size) throws MaxChangedBlocksException { + } + + @Override + public void finish(EditSession editSession, LocalBlockVectorSet placed, final Vector position, Pattern pattern, double size) { + int radius2 = (int) (size * size); + // Keep track of where we've visited + LocalBlockVectorSet tmp = new LocalBlockVectorSet(); + // Individual frontier for each point + LocalBlockVectorSet[] frontiers = new LocalBlockVectorSet[placed.size()]; + // Keep track of where each frontier has visited + LocalBlockVectorSet[] frontiersVisited = new LocalBlockVectorSet[placed.size()]; + // Initiate the frontier with the starting points + int i = 0; + for (Vector pos : placed) { + LocalBlockVectorSet set = new LocalBlockVectorSet(); + set.add(pos); + frontiers[i] = set; + frontiersVisited[i] = set.clone(); + i++; } - // Ideally we'd calculate all the bisecting planes, but that's complex to program - // With this algorithm compute time depends on the number of blocks rather than the number of points - // - Expand from each point (block by block) until there is a collision - { - // Keep track of where we've visited - LocalBlockVectorSet visited = queue; - LocalBlockVectorSet tmp = new LocalBlockVectorSet(); - // Individual frontier for each point - LocalBlockVectorSet[] frontiers = new LocalBlockVectorSet[queue.size()]; - // Keep track of where each frontier has visited - LocalBlockVectorSet[] frontiersVisited = new LocalBlockVectorSet[queue.size()]; - // Initiate the frontier with the starting points - int i = 0; - for (Vector pos : queue) { - LocalBlockVectorSet set = new LocalBlockVectorSet(); - set.add(pos); - frontiers[i] = set; - frontiersVisited[i] = set.clone(); - i++; - } - // Mask - Mask mask = editSession.getMask(); - if (mask == null) { - mask = Masks.alwaysTrue(); - } - final Mask finalMask = mask; - // Expand - boolean notEmpty = true; - while (notEmpty) { - notEmpty = false; - for (i = 0; i < frontiers.length; i++) { - LocalBlockVectorSet frontier = frontiers[i]; - notEmpty |= !frontier.isEmpty(); - final LocalBlockVectorSet frontierVisited = frontiersVisited[i]; - // This is a temporary set with the next blocks the frontier will visit - final LocalBlockVectorSet finalTmp = tmp; - frontier.forEach(new LocalBlockVectorSet.BlockVectorSetVisitor() { - @Override - public void run(int x, int y, int z, int index) { - if (PseudoRandom.random.random(2) == 0) { - finalTmp.add(x, y, z); - return; - } - for (Vector direction : BreadthFirstSearch.DEFAULT_DIRECTIONS) { - int x2 = x + direction.getBlockX(); - int y2 = y + direction.getBlockY(); - int z2 = z + direction.getBlockZ(); - // Check boundary - int dx = position.getBlockX() - x2; - int dy = position.getBlockY() - y2; - int dz = position.getBlockZ() - z2; - int dSqr = (dx * dx) + (dy * dy) + (dz * dz); - if (dSqr <= radius2) { - if (finalMask.test(MutableBlockVector.get(x2, y2, z2))) { - // (collision) If it's visited and part of another frontier, set the block - if (!visited.add(x2, y2, z2)) { - if (!frontierVisited.contains(x2, y2, z2)) { - editSession.setBlock(x2, y2, z2, pattern); - } - } else { - // Hasn't visited and not a collision = add it - finalTmp.add(x2, y2, z2); - frontierVisited.add(x2, y2, z2); + // Mask + Mask mask = editSession.getMask(); + if (mask == null) { + mask = Masks.alwaysTrue(); + } + final Mask finalMask = mask; + final SurfaceMask surfaceTest = new SurfaceMask(editSession); + // Expand + boolean notEmpty = true; + while (notEmpty) { + notEmpty = false; + for (i = 0; i < frontiers.length; i++) { + LocalBlockVectorSet frontier = frontiers[i]; + notEmpty |= !frontier.isEmpty(); + final LocalBlockVectorSet frontierVisited = frontiersVisited[i]; + // This is a temporary set with the next blocks the frontier will visit + final LocalBlockVectorSet finalTmp = tmp; + frontier.forEach(new LocalBlockVectorSet.BlockVectorSetVisitor() { + @Override + public void run(int x, int y, int z, int index) { + if (PseudoRandom.random.random(2) == 0) { + finalTmp.add(x, y, z); + return; + } + for (int i = 0; i < BreadthFirstSearch.DIAGONAL_DIRECTIONS.length; i++) { + Vector direction = BreadthFirstSearch.DIAGONAL_DIRECTIONS[i]; + int x2 = x + direction.getBlockX(); + int y2 = y + direction.getBlockY(); + int z2 = z + direction.getBlockZ(); + // Check boundary + int dx = position.getBlockX() - x2; + int dy = position.getBlockY() - y2; + int dz = position.getBlockZ() - z2; + int dSqr = (dx * dx) + (dy * dy) + (dz * dz); + if (dSqr <= radius2) { + 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 + if (!placed.add(x2, y2, z2)) { + if (!frontierVisited.contains(x2, y2, z2)) { + editSession.setBlock(x2, y2, z2, pattern); } + } else { + // Hasn't visited and not a collision = add it + finalTmp.add(x2, y2, z2); + frontierVisited.add(x2, y2, z2); } } } } - }); - // Swap the frontier with the temporary set - frontiers[i] = tmp; - tmp = frontier; - tmp.clear(); - } + } + }); + // Swap the frontier with the temporary set + frontiers[i] = tmp; + tmp = frontier; + tmp.clear(); } } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java b/core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java new file mode 100644 index 00000000..2b6434f0 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java @@ -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); + } +}