diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlobBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlobBrush.java index d9a410ac9..c729f414a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlobBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlobBrush.java @@ -1,17 +1,11 @@ package com.fastasyncworldedit.core.command.tool.brush; -import com.fastasyncworldedit.core.math.MutableVector3; -import com.fastasyncworldedit.core.math.random.SimplexNoise; -import com.fastasyncworldedit.core.util.MathMan; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.math.transform.AffineTransform; - -import java.util.concurrent.ThreadLocalRandom; public class BlobBrush implements Brush { @@ -30,90 +24,7 @@ public class BlobBrush implements Brush { @Override public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { - double seedX = ThreadLocalRandom.current().nextDouble(); - double seedY = ThreadLocalRandom.current().nextDouble(); - double seedZ = ThreadLocalRandom.current().nextDouble(); - - int px = position.getBlockX(); - int py = position.getBlockY(); - int pz = position.getBlockZ(); - - double distort = this.frequency / size; - - double modX = 1d / radius.getX(); - double modY = 1d / radius.getY(); - double modZ = 1d / radius.getZ(); - int radius = (int) size; - int radiusSqr = (int) (size * size); - int sizeInt = (int) size * 2; - - if (sphericity == 1) { - for (int x = -sizeInt; x <= sizeInt; x++) { - double nx = seedX + x * distort; - double d1 = x * x * modX; - for (int y = -sizeInt; y <= sizeInt; y++) { - double d2 = d1 + y * y * modY; - double ny = seedY + y * distort; - for (int z = -sizeInt; z <= sizeInt; z++) { - double nz = seedZ + z * distort; - double distance = d2 + z * z * modZ; - double noise = this.amplitude * SimplexNoise.noise(nx, ny, nz); - if (distance + distance * noise < radiusSqr) { - editSession.setBlock(px + x, py + y, pz + z, pattern); - } - } - } - } - } else { - AffineTransform transform = new AffineTransform() - .rotateX(ThreadLocalRandom.current().nextInt(360)) - .rotateY(ThreadLocalRandom.current().nextInt(360)) - .rotateZ(ThreadLocalRandom.current().nextInt(360)); - - double manScaleX = 1.25 + seedX * 0.5; - double manScaleY = 1.25 + seedY * 0.5; - double manScaleZ = 1.25 + seedZ * 0.5; - - MutableVector3 mutable = new MutableVector3(); - double roughness = 1 - sphericity; - for (int xr = -sizeInt; xr <= sizeInt; xr++) { - for (int yr = -sizeInt; yr <= sizeInt; yr++) { - for (int zr = -sizeInt; zr <= sizeInt; zr++) { - // pt == mutable as it's a MutableVector3 - // so it must be set each time - mutable.mutX(xr); - mutable.mutY(yr); - mutable.mutZ(zr); - Vector3 pt = transform.apply(mutable); - int x = MathMan.roundInt(pt.getX()); - int y = MathMan.roundInt(pt.getY()); - int z = MathMan.roundInt(pt.getZ()); - - double xScaled = Math.abs(x) * modX; - double yScaled = Math.abs(y) * modY; - double zScaled = Math.abs(z) * modZ; - double manDist = xScaled + yScaled + zScaled; - double distSqr = x * x * modX + z * z * modZ + y * y * modY; - - double distance = Math.sqrt(distSqr) * sphericity + MathMan.max( - manDist, - xScaled * manScaleX, - yScaled * manScaleY, - zScaled * manScaleZ - ) * roughness; - - double noise = this.amplitude * SimplexNoise.noise( - seedX + x * distort, - seedZ + z * distort, - seedZ + z * distort - ); - if (distance + distance * noise < radius) { - editSession.setBlock(px + xr, py + yr, pz + zr, pattern); - } - } - } - } - } + editSession.makeBlob(position, pattern, size, frequency, amplitude, radius, sphericity); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 45b088bab..483f70a96 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -48,6 +48,8 @@ import com.fastasyncworldedit.core.history.changeset.BlockBagChangeSet; import com.fastasyncworldedit.core.math.LocalBlockVectorSet; import com.fastasyncworldedit.core.math.MutableBlockVector2; import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.math.MutableVector3; +import com.fastasyncworldedit.core.math.random.SimplexNoise; import com.fastasyncworldedit.core.object.FaweLimit; import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader; import com.fastasyncworldedit.core.regions.RegionWrapper; @@ -157,6 +159,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import static com.google.common.base.Preconditions.checkArgument; @@ -3663,5 +3666,108 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { faweClipboard.setOrigin(region.getMinimumPoint()); return faweClipboard; } + + /** + * Makes a distorted sphere. + * + * @param position Center of blob + * @param pattern pattern to use + * @param size overall size of the blob + * @param frequency distortion amount (0 to 1) + * @param amplitude distortion amplitude (0 to 1) + * @param radius radii to multiply x/y/z by + * @param sphericity how spherical to make the blob. 1 = very spherical, 0 = not + * @return changes + */ + public int makeBlob( + BlockVector3 position, Pattern pattern, double size, double frequency, double amplitude, Vector3 radius, + double sphericity + ) { + double seedX = ThreadLocalRandom.current().nextDouble(); + double seedY = ThreadLocalRandom.current().nextDouble(); + double seedZ = ThreadLocalRandom.current().nextDouble(); + + int px = position.getBlockX(); + int py = position.getBlockY(); + int pz = position.getBlockZ(); + + double distort = frequency / size; + + double modX = 1d / radius.getX(); + double modY = 1d / radius.getY(); + double modZ = 1d / radius.getZ(); + int r = (int) size; + int radiusSqr = (int) (size * size); + int sizeInt = (int) size * 2; + + if (sphericity == 1) { + for (int x = -sizeInt; x <= sizeInt; x++) { + double nx = seedX + x * distort; + double d1 = x * x * modX; + for (int y = -sizeInt; y <= sizeInt; y++) { + double d2 = d1 + y * y * modY; + double ny = seedY + y * distort; + for (int z = -sizeInt; z <= sizeInt; z++) { + double nz = seedZ + z * distort; + double distance = d2 + z * z * modZ; + double noise = amplitude * SimplexNoise.noise(nx, ny, nz); + if (distance + distance * noise < radiusSqr) { + setBlock(px + x, py + y, pz + z, pattern); + } + } + } + } + } else { + AffineTransform transform = new AffineTransform() + .rotateX(ThreadLocalRandom.current().nextInt(360)) + .rotateY(ThreadLocalRandom.current().nextInt(360)) + .rotateZ(ThreadLocalRandom.current().nextInt(360)); + + double manScaleX = 1.25 + seedX * 0.5; + double manScaleY = 1.25 + seedY * 0.5; + double manScaleZ = 1.25 + seedZ * 0.5; + + MutableVector3 mutable = new MutableVector3(); + double roughness = 1 - sphericity; + for (int xr = -sizeInt; xr <= sizeInt; xr++) { + for (int yr = -sizeInt; yr <= sizeInt; yr++) { + for (int zr = -sizeInt; zr <= sizeInt; zr++) { + // pt == mutable as it's a MutableVector3 + // so it must be set each time + mutable.mutX(xr); + mutable.mutY(yr); + mutable.mutZ(zr); + Vector3 pt = transform.apply(mutable); + int x = MathMan.roundInt(pt.getX()); + int y = MathMan.roundInt(pt.getY()); + int z = MathMan.roundInt(pt.getZ()); + + double xScaled = Math.abs(x) * modX; + double yScaled = Math.abs(y) * modY; + double zScaled = Math.abs(z) * modZ; + double manDist = xScaled + yScaled + zScaled; + double distSqr = x * x * modX + z * z * modZ + y * y * modY; + + double distance = Math.sqrt(distSqr) * sphericity + MathMan.max( + manDist, + xScaled * manScaleX, + yScaled * manScaleY, + zScaled * manScaleZ + ) * roughness; + + double noise = amplitude * SimplexNoise.noise( + seedX + x * distort, + seedZ + z * distort, + seedZ + z * distort + ); + if (distance + distance * noise < r) { + setBlock(px + xr, py + yr, pz + zr, pattern); + } + } + } + } + } + return changes; + } //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 9e5699237..c189daaba 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -413,7 +413,7 @@ public class BrushCommands { InjectedValueAccess context, @Arg(desc = "Pattern") Pattern fill, - @Arg(desc = "radius", def = "10") + @Arg(desc = "radii to multiply x,y,z by", def = "10") Vector3 radius, @Arg(name = "roundness", desc = "roundness", def = "100") double sphericity, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 75ef2fb31..bf602be13 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.function.generator.CavesGen; import com.fastasyncworldedit.core.util.MainUtil; +import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TextureUtil; import com.fastasyncworldedit.core.util.image.ImageUtil; import com.sk89q.worldedit.EditSession; @@ -647,6 +648,47 @@ public class GenerationCommands { editSession.addOre(region, mask, material, size, freq, rarity, minY, maxY); actor.print(Caption.of("fawe.worldedit.visitor.visitor.block", editSession.getBlockChangeCount())); } + + @Command( + name = "/blob", + aliases = {"/rock"}, + desc = "Creates a distorted sphere" + ) + @Logging(PLACEMENT) + @CommandPermissions("worldedit.generation.blob") + public int blobBrush( + Actor actor, LocalSession session, EditSession editSession, + @Arg(desc = "Pattern") + Pattern pattern, + @Arg(desc = "size", def = "5") + double size, + @Arg(desc = "radius", def = "5") + Vector3 radius, + @Arg(name = "roundness", desc = "roundness", def = "100") + double sphericity, + @Arg(desc = "double", def = "30") + double frequency, + @Arg(desc = "double", def = "50") + double amplitude + ) throws WorldEditException { + double max = MathMan.max(radius.getX(), radius.getY(), radius.getZ()); + worldEdit.checkMaxRadius(max); + BlockVector3 pos = session.getPlacementPosition(actor); + int affected = editSession.makeBlob( + pos, + pattern, + size, + frequency / 100, + amplitude / 100, + radius.divide(max), + sphericity / 100 + ); + if (actor instanceof Player) { + ((Player) actor).findFreePosition(); + } + actor.print(Caption.of("worldedit.sphere.created", TextComponent.of(affected))); + return affected; + } //FAWE end }