package net.minestom.server.collision; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** * See https://wiki.vg/Entity_metadata#Mobs_2 */ public final class BoundingBox implements Shape { private final double width, height, depth; private final Point offset; BoundingBox(double width, double height, double depth, Point offset) { this.width = width; this.height = height; this.depth = depth; this.offset = offset; } public BoundingBox(double width, double height, double depth) { this(width, height, depth, new Vec(-width / 2, 0, -depth / 2)); } @Override @ApiStatus.Experimental public boolean intersectBox(@NotNull Point positionRelative, @NotNull BoundingBox boundingBox) { return (minX() + positionRelative.x() <= boundingBox.maxX() && maxX() + positionRelative.x() >= boundingBox.minX()) && (minY() + positionRelative.y() <= boundingBox.maxY() && maxY() + positionRelative.y() >= boundingBox.minY()) && (minZ() + positionRelative.z() <= boundingBox.maxZ() && maxZ() + positionRelative.z() >= boundingBox.minZ()); } @Override @ApiStatus.Experimental public boolean intersectBoxSwept(@NotNull Point rayStart, @NotNull Point rayDirection, @NotNull Point shapePos, @NotNull BoundingBox moving, @NotNull SweepResult finalResult) { final boolean isHit = RayUtils.BoundingBoxIntersectionCheck( moving, rayStart, rayDirection, this, shapePos ); if (!isHit) return false; SweepResult tempResult = new SweepResult(1, 0, 0, 0, null); // Longer check to get result of collision RayUtils.SweptAABB(moving, rayStart, rayDirection, this, shapePos, tempResult); // Update final result if the temp result collision is sooner than the current final result if (tempResult.res < finalResult.res) { finalResult.res = tempResult.res; finalResult.normalX = tempResult.normalX; finalResult.normalY = tempResult.normalY; finalResult.normalZ = tempResult.normalZ; finalResult.collidedShapePosition = shapePos; finalResult.collidedShape = this; finalResult.blockType = null; } return true; } /** * Used to know if this {@link BoundingBox} intersects with the bounding box of an entity. * * @param entity the entity to check the bounding box * @return true if this bounding box intersects with the entity, false otherwise */ @ApiStatus.Experimental public boolean intersectEntity(@NotNull Point src, @NotNull Entity entity) { return intersectBox(src.sub(entity.getPosition()), entity.getBoundingBox()); } @ApiStatus.Experimental public boolean boundingBoxRayIntersectionCheck(Vec start, Vec direction, Pos position) { return RayUtils.BoundingBoxRayIntersectionCheck(start, direction, this, position); } @Override public @NotNull Point relativeStart() { return offset; } @Override public @NotNull Point relativeEnd() { return offset.add(width, height, depth); } @Override public String toString() { String result = "BoundingBox"; result += "\n"; result += "[" + minX() + " : " + maxX() + "]"; result += "\n"; result += "[" + minY() + " : " + maxY() + "]"; result += "\n"; result += "[" + minZ() + " : " + maxZ() + "]"; return result; } /** * Creates a new {@link BoundingBox} linked to the same {@link Entity} with expanded size. * * @param x the X offset * @param y the Y offset * @param z the Z offset * @return a new {@link BoundingBox} expanded */ public @NotNull BoundingBox expand(double x, double y, double z) { return new BoundingBox(this.width + x, this.height + y, this.depth + z); } /** * Creates a new {@link BoundingBox} linked to the same {@link Entity} with contracted size. * * @param x the X offset * @param y the Y offset * @param z the Z offset * @return a new bounding box contracted */ public @NotNull BoundingBox contract(double x, double y, double z) { return new BoundingBox(this.width - x, this.height - y, this.depth - z); } public double width() { return width; } public double height() { return height; } public double depth() { return depth; } public double minX() { return relativeStart().x(); } public double maxX() { return relativeEnd().x(); } public double minY() { return relativeStart().y(); } public double maxY() { return relativeEnd().y(); } public double minZ() { return relativeStart().z(); } public double maxZ() { return relativeEnd().z(); } }