registerRenderer(BoundingBoxBiomeBorder.class, new BiomeBorderRenderer());
registerRenderer(BoundingBoxConduit.class, new ConduitRenderer());
registerRenderer(BoundingBoxSpawnableBlocks.class, new SpawnableBlocksRenderer());
+ registerRenderer(BoundingBoxLine.class, new LineRenderer());
registerProvider(new SlimeChunkProvider());
registerProvider(new WorldSpawnProvider());
registerProvider(new MobSpawnerProvider());
registerProvider(new ConduitProvider());
registerProvider(new SpawnableBlocksProvider());
+ registerProvider(new CustomLineProvider());
}
public static <T extends AbstractBoundingBox> void registerProvider(IBoundingBoxProvider<T> provider) {
import com.irtimaled.bbor.client.providers.CustomBeaconProvider;
import com.irtimaled.bbor.client.providers.CustomBoxProvider;
+import com.irtimaled.bbor.client.providers.CustomLineProvider;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.command.Commands;
private static final String COMMAND = "bbor:custom";
private static final String BOX = "box";
private static final String BEACON = "beacon";
+ private static final String LINE = "line";
public static void register(CommandDispatcher<ISuggestionProvider> commandDispatcher) {
LiteralArgumentBuilder command = Commands.literal(COMMAND)
.then(BoxCommandBuilder.build(BOX))
.then(BeaconCommandBuilder.build(BEACON))
+ .then(LineCommandBuilder.build(LINE))
.then(Commands.literal(ArgumentNames.CLEAR)
.executes(context -> {
CustomBoxProvider.clear();
CustomBeaconProvider.clear();
+ CustomLineProvider.clear();
CommandHelper.feedback(context, "bbor.commands.custom.cleared.all");
return 0;
--- /dev/null
+package com.irtimaled.bbor.client.commands;
+
+import com.irtimaled.bbor.client.providers.CustomLineProvider;
+import com.irtimaled.bbor.common.models.Point;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.minecraft.command.CommandSource;
+import net.minecraft.command.Commands;
+
+class LineCommandBuilder {
+ private static final String WIDTH = "width";
+
+ static LiteralArgumentBuilder<CommandSource> build(String command) {
+ return Commands.literal(command)
+ .then(Commands.literal(ArgumentNames.ADD)
+ .then(Commands.argument(ArgumentNames.FROM, Arguments.point())
+ .then(Commands.argument(ArgumentNames.TO, Arguments.point())
+ .executes(LineCommandBuilder::addLine)
+ .then(Commands.argument(WIDTH, Arguments.doubleArg())
+ .executes(LineCommandBuilder::addLine)))))
+ .then(Commands.literal(ArgumentNames.CLEAR)
+ .executes(context -> {
+ CustomLineProvider.clear();
+
+ CommandHelper.feedback(context, "bbor.commands.line.cleared.all");
+ return 0;
+ })
+ .then(Commands.argument(ArgumentNames.FROM, Arguments.coords())
+ .then(Commands.argument(ArgumentNames.TO, Arguments.coords())
+ .executes(context -> {
+ Point from = Arguments.getPoint(context, ArgumentNames.FROM).snapXZ(0.5d);
+ Point to = Arguments.getPoint(context, ArgumentNames.TO).snapXZ(0.5d);
+ boolean removed = CustomLineProvider.remove(from, to);
+
+ String format = removed ? "bbor.commands.line.cleared" : "bbor.commands.line.notFound";
+ CommandHelper.feedback(context, format,
+ from.getX(), from.getY(), from.getZ(),
+ to.getX(), to.getY(), to.getZ());
+ return 0;
+ }))));
+ }
+
+ private static int addLine(CommandContext<CommandSource> context) throws CommandSyntaxException {
+ Point from = Arguments.getPoint(context, ArgumentNames.FROM).snapXZ(0.5d);
+ Point to = Arguments.getPoint(context, ArgumentNames.TO).snapXZ(0.5d);
+ Double width = Arguments.getDouble(context, WIDTH);
+ CustomLineProvider.add(from, to, width);
+
+ CommandHelper.feedback(context, "bbor.commands.line.added",
+ from.getX(), from.getY(), from.getZ(),
+ to.getX(), to.getY(), to.getZ());
+ return 0;
+ }
+}
--- /dev/null
+package com.irtimaled.bbor.client.models;
+
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.TypeHelper;
+import com.irtimaled.bbor.common.models.AbstractBoundingBox;
+import com.irtimaled.bbor.common.models.Point;
+
+public class BoundingBoxLine extends AbstractBoundingBox {
+ private final Point minPoint;
+ private final Point maxPoint;
+ private final Double width;
+ private final Point[] corners;
+
+ protected BoundingBoxLine(Point minPoint, Point maxPoint, double width, BoundingBoxType type, Point... corners) {
+ super(type);
+ this.minPoint = minPoint;
+ this.maxPoint = maxPoint;
+ this.width = width;
+ this.corners = corners;
+ }
+
+ public static BoundingBoxLine from(Point minPoint, Point maxPoint, Double width, BoundingBoxType type) {
+ if (width == 0) return new BoundingBoxLine(minPoint, maxPoint, width, type);
+
+ double halfWidth = width / 2.0d;
+
+ double dx = maxPoint.getX() - minPoint.getX();
+ double dz = maxPoint.getZ() - minPoint.getZ();
+
+ double dxm = dx == 0 ? 0 : dx / Math.abs(dx);
+ double dzm = dz == 0 ? 0 : dz / Math.abs(dz);
+
+ double xc, zc;
+ if (dxm == 0 || dzm == 0) {
+ xc = Math.abs(dzm) * halfWidth;
+ zc = Math.abs(dxm) * halfWidth;
+ } else {
+ double h = Math.sqrt(dx * dx + dz * dz);
+ double theta = Math.acos((dz * dz + h * h - dx * dx) / (2 * dz * h));
+ zc = halfWidth * Math.sin(theta);
+ xc = Math.sqrt(halfWidth * halfWidth - zc * zc) * dxm * dzm;
+ }
+
+ return new BoundingBoxLine(minPoint, maxPoint, width, type,
+ new Point(minPoint.getX() + xc, minPoint.getY(), minPoint.getZ() - zc),
+ new Point(minPoint.getX() - xc, minPoint.getY(), minPoint.getZ() + zc),
+ new Point(maxPoint.getX() - xc, maxPoint.getY(), maxPoint.getZ() + zc),
+ new Point(maxPoint.getX() + xc, maxPoint.getY(), maxPoint.getZ() - zc));
+ }
+
+ @Override
+ public int hashCode() {
+ return TypeHelper.combineHashCodes(minPoint.hashCode(), maxPoint.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ BoundingBoxLine other = (BoundingBoxLine) obj;
+ return minPoint.equals(other.minPoint) && maxPoint.equals(other.maxPoint);
+ }
+
+ public Point getMinPoint() {
+ return minPoint;
+ }
+
+ public Point getMaxPoint() {
+ return maxPoint;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public Point[] getCorners() {
+ return corners;
+ }
+
+ @Override
+ public Boolean intersectsBounds(int minX, int minZ, int maxX, int maxZ) {
+ boolean minXWithinBounds = isBetween(minPoint.getX(), minX, maxX);
+ boolean maxXWithinBounds = isBetween(maxPoint.getX(), minX, maxX);
+ boolean minZWithinBounds = isBetween(minPoint.getZ(), minZ, maxZ);
+ boolean maxZWithinBounds = isBetween(maxPoint.getZ(), minZ, maxZ);
+
+ return (minXWithinBounds && minZWithinBounds) ||
+ (maxXWithinBounds && maxZWithinBounds) ||
+ (minXWithinBounds && maxZWithinBounds) ||
+ (maxXWithinBounds && minZWithinBounds);
+ }
+
+ private boolean isBetween(double val, int min, int max) {
+ return val >= min && val <= max;
+ }
+}
--- /dev/null
+package com.irtimaled.bbor.client.providers;
+
+import com.irtimaled.bbor.client.Player;
+import com.irtimaled.bbor.client.models.BoundingBoxLine;
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.models.Point;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class CustomLineProvider implements IBoundingBoxProvider<BoundingBoxLine> {
+ private static final Map<Integer, Map<Integer, BoundingBoxLine>> dimensionCache = new HashMap<>();
+
+ private static int getHashKey(Point minPoint, Point maxPoint) {
+ return (31 + minPoint.hashCode()) * 31 + maxPoint.hashCode();
+ }
+
+ private static Map<Integer, BoundingBoxLine> getCache(int dimensionId) {
+ return dimensionCache.computeIfAbsent(dimensionId, i -> new ConcurrentHashMap<>());
+ }
+
+ public static void add(Point minPoint, Point maxPoint, Double width) {
+ int dimensionId = Player.getDimensionId();
+ int cacheKey = getHashKey(minPoint, maxPoint);
+ BoundingBoxLine line = BoundingBoxLine.from(minPoint, maxPoint, width, BoundingBoxType.Custom);
+ getCache(dimensionId).put(cacheKey, line);
+ }
+
+ public static boolean remove(Point min, Point max) {
+ int dimensionId = Player.getDimensionId();
+ int cacheKey = getHashKey(min, max);
+ return getCache(dimensionId).remove(cacheKey) != null;
+ }
+
+ public static void clear() {
+ dimensionCache.values().forEach(Map::clear);
+ }
+
+ public Iterable<BoundingBoxLine> get(int dimensionId) {
+ return getCache(dimensionId).values();
+ }
+}
--- /dev/null
+package com.irtimaled.bbor.client.renderers;
+
+import com.irtimaled.bbor.client.config.ConfigManager;
+import com.irtimaled.bbor.client.models.BoundingBoxLine;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.Arrays;
+
+public class LineRenderer extends AbstractRenderer<BoundingBoxLine> {
+ @Override
+ public void render(BoundingBoxLine boundingBox) {
+ Color color = boundingBox.getColor();
+
+ if (boundingBox.getWidth() == 0) {
+ OffsetPoint startPoint = new OffsetPoint(boundingBox.getMinPoint()).offset(0, 0.001f, 0);
+ OffsetPoint endPoint = new OffsetPoint(boundingBox.getMaxPoint()).offset(0, 0.001f, 0);
+ renderLine(startPoint, endPoint, color);
+ return;
+ }
+
+ OffsetPoint[] cornerPoints = Arrays.stream(boundingBox.getCorners()).
+ map(point -> new OffsetPoint(point).offset(0,0.001f, 0)).
+ toArray(OffsetPoint[]::new);
+
+ GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
+ Renderer.startQuads()
+ .setColor(color)
+ .addPoints(cornerPoints)
+ .render();
+
+ if(!ConfigManager.fill.get()) return;
+
+ GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
+ GL11.glEnable(GL11.GL_BLEND);
+ Renderer.startQuads()
+ .setColor(color)
+ .setAlpha(30)
+ .addPoints(cornerPoints)
+ .render();
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_POLYGON_OFFSET_LINE);
+ GL11.glPolygonOffset(-1.f, -1.f);
+ }
+}
return addPoint(point.getX(), point.getY(), point.getZ());
}
+ public Renderer addPoints(OffsetPoint[] points) {
+ Renderer renderer = this;
+ for (OffsetPoint point : points) {
+ renderer = renderer.addPoint(point);
+ }
+ return renderer;
+ }
+
Renderer addPoint(double x, double y, double z) {
pos(x, y, z);
color();
"bbor.commands.box.cleared": "Box cleared with start [x=%d, y=%d, z=%d] and end [x=%d, y=%d, z=%d]",
"bbor.commands.box.notFound": "No box found with start [x=%d, y=%d, z=%d] and end [x=%d, y=%d, z=%d]",
+ "bbor.commands.line.added": "Line added with start [x=%d, y=%d, z=%d] and end [x=%d, y=%d, z=%d]",
+ "bbor.commands.line.cleared": "All lines cleared",
+ "bbor.commands.line.cleared.all": "Line cleared with start [x=%d, y=%d, z=%d] and end [x=%d, y=%d, z=%d]",
+ "bbor.commands.line.notFound": "No line found with start [x=%d, y=%d, z=%d] and end [x=%d, y=%d, z=%d]",
+
"bbor.commands.spawningSphere.set": "Spawning sphere set",
"bbor.commands.spawningSphere.notSet": "No spawning sphere set",
"bbor.commands.spawningSphere.cleared": "Spawning sphere cleared",