Reduce bounding box allocation

This commit is contained in:
TheMode 2021-08-13 10:12:28 +02:00
parent 3917d3cb1d
commit c89f049dad
2 changed files with 77 additions and 61 deletions

View File

@ -1,9 +1,14 @@
package net.minestom.server.collision; package net.minestom.server.collision;
import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec; import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity; import net.minestom.server.entity.Entity;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
/** /**
* See https://wiki.vg/Entity_metadata#Mobs_2 * See https://wiki.vg/Entity_metadata#Mobs_2
@ -13,6 +18,14 @@ public class BoundingBox {
private final Entity entity; private final Entity entity;
private final double x, y, z; private final double x, y, z;
private volatile Pos lastPosition;
private List<Vec> bottomFace;
private List<Vec> topFace;
private List<Vec> leftFace;
private List<Vec> rightFace;
private List<Vec> frontFace;
private List<Vec> backFace;
/** /**
* Creates a {@link BoundingBox} linked to an {@link Entity} and with a specific size. * Creates a {@link BoundingBox} linked to an {@link Entity} and with a specific size.
* *
@ -211,14 +224,13 @@ public class BoundingBox {
* *
* @return the points at the bottom of the {@link BoundingBox} * @return the points at the bottom of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getBottomFace() {
public Vec[] getBottomFace() { this.bottomFace = get(bottomFace, () ->
return new Vec[]{ List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()), new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()), new Vec(getMaxX(), getMinY(), getMaxZ()),
new Vec(getMinX(), getMinY(), getMaxZ()), new Vec(getMinX(), getMinY(), getMaxZ())));
}; return bottomFace;
} }
/** /**
@ -226,14 +238,13 @@ public class BoundingBox {
* *
* @return the points at the top of the {@link BoundingBox} * @return the points at the top of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getTopFace() {
public Vec[] getTopFace() { this.topFace = get(topFace, () ->
return new Vec[]{ List.of(new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()), new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()), new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()), new Vec(getMinX(), getMaxY(), getMaxZ())));
}; return topFace;
} }
/** /**
@ -241,14 +252,13 @@ public class BoundingBox {
* *
* @return the points on the left face of the {@link BoundingBox} * @return the points on the left face of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getLeftFace() {
public Vec[] getLeftFace() { this.leftFace = get(leftFace, () ->
return new Vec[]{ List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()), new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()), new Vec(getMinX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMinY(), getMaxZ()), new Vec(getMinX(), getMinY(), getMaxZ())));
}; return leftFace;
} }
/** /**
@ -256,14 +266,13 @@ public class BoundingBox {
* *
* @return the points on the right face of the {@link BoundingBox} * @return the points on the right face of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getRightFace() {
public Vec[] getRightFace() { this.rightFace = get(rightFace, () ->
return new Vec[]{ List.of(new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()), new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()), new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()), new Vec(getMaxX(), getMinY(), getMaxZ())));
}; return rightFace;
} }
/** /**
@ -271,14 +280,13 @@ public class BoundingBox {
* *
* @return the points at the front of the {@link BoundingBox} * @return the points at the front of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getFrontFace() {
public Vec[] getFrontFace() { this.frontFace = get(frontFace, () ->
return new Vec[]{ List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()), new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()), new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()), new Vec(getMinX(), getMaxY(), getMinZ())));
}; return frontFace;
} }
/** /**
@ -286,14 +294,13 @@ public class BoundingBox {
* *
* @return the points at the back of the {@link BoundingBox} * @return the points at the back of the {@link BoundingBox}
*/ */
@NotNull public @NotNull List<Vec> getBackFace() {
public Vec[] getBackFace() { this.backFace = get(backFace, () -> List.of(
return new Vec[]{
new Vec(getMinX(), getMinY(), getMaxZ()), new Vec(getMinX(), getMinY(), getMaxZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()), new Vec(getMaxX(), getMinY(), getMaxZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()), new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()), new Vec(getMinX(), getMaxY(), getMaxZ())));
}; return backFace;
} }
@Override @Override
@ -307,4 +314,14 @@ public class BoundingBox {
result += "[" + getMinZ() + " : " + getMaxZ() + "]"; result += "[" + getMinZ() + " : " + getMaxZ() + "]";
return result; return result;
} }
private @NotNull List<Vec> get(@Nullable List<Vec> face, Supplier<? extends List<Vec>> vecSupplier) {
final var lastPos = this.lastPosition;
final var entityPos = entity.getPosition();
if (face != null && lastPos != null && lastPos.samePoint(entityPos)) {
return face;
}
this.lastPosition = entityPos;
return vecSupplier.get();
}
} }

View File

@ -10,6 +10,9 @@ import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class CollisionUtils { public class CollisionUtils {
private static final Vec Y_AXIS = new Vec(0, 1, 0); private static final Vec Y_AXIS = new Vec(0, 1, 0);
@ -71,30 +74,27 @@ public class CollisionUtils {
* @param corners the corners to check against * @param corners the corners to check against
* @return result of the step * @return result of the step
*/ */
private static StepResult stepAxis(Instance instance, Chunk originChunk, Vec startPosition, Vec axis, double stepAmount, Vec... corners) { private static StepResult stepAxis(Instance instance, Chunk originChunk, Vec startPosition, Vec axis, double stepAmount, List<Vec> corners) {
if (corners.length == 0) final List<Vec> mutableCorners = new ArrayList<>(corners);
return new StepResult(startPosition, false); // avoid degeneracy in following computations
final Vec[] originalCorners = corners.clone();
final double sign = Math.signum(stepAmount); final double sign = Math.signum(stepAmount);
final int blockLength = (int) stepAmount; final int blockLength = (int) stepAmount;
final double remainingLength = stepAmount - blockLength; final double remainingLength = stepAmount - blockLength;
// used to determine if 'remainingLength' should be used // used to determine if 'remainingLength' should be used
boolean collisionFound = false; boolean collisionFound = false;
for (int i = 0; i < Math.abs(blockLength); i++) { for (int i = 0; i < Math.abs(blockLength); i++) {
collisionFound = stepOnce(instance, originChunk, axis, sign, corners); collisionFound = stepOnce(instance, originChunk, axis, sign, mutableCorners);
if (collisionFound) break; if (collisionFound) break;
} }
// add remainingLength // add remainingLength
if (!collisionFound) { if (!collisionFound) {
collisionFound = stepOnce(instance, originChunk, axis, remainingLength, corners); collisionFound = stepOnce(instance, originChunk, axis, remainingLength, mutableCorners);
} }
// find the corner which moved the least // find the corner which moved the least
double smallestDisplacement = Double.POSITIVE_INFINITY; double smallestDisplacement = Double.POSITIVE_INFINITY;
for (int i = 0; i < corners.length; i++) { for (int i = 0; i < corners.size(); i++) {
final double displacement = originalCorners[i].distance(corners[i]); final double displacement = corners.get(i).distance(mutableCorners.get(i));
if (displacement < smallestDisplacement) { if (displacement < smallestDisplacement) {
smallestDisplacement = displacement; smallestDisplacement = displacement;
} }
@ -111,10 +111,10 @@ public class CollisionUtils {
* @param corners the corners of the bounding box to consider * @param corners the corners of the bounding box to consider
* @return true if found collision * @return true if found collision
*/ */
private static boolean stepOnce(Instance instance, Chunk originChunk, Vec axis, double amount, Vec[] corners) { private static boolean stepOnce(Instance instance, Chunk originChunk, Vec axis, double amount, List<Vec> corners) {
final double sign = Math.signum(amount); final double sign = Math.signum(amount);
for (int cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) { for (int cornerIndex = 0; cornerIndex < corners.size(); cornerIndex++) {
final Vec originalCorner = corners[cornerIndex]; final Vec originalCorner = corners.get(cornerIndex);
final Vec newCorner = originalCorner.add(axis.mul(amount)); final Vec newCorner = originalCorner.add(axis.mul(amount));
final Chunk chunk = ChunkUtils.retrieve(instance, originChunk, newCorner); final Chunk chunk = ChunkUtils.retrieve(instance, originChunk, newCorner);
if (!ChunkUtils.isLoaded(chunk)) { if (!ChunkUtils.isLoaded(chunk)) {
@ -122,17 +122,16 @@ public class CollisionUtils {
return true; return true;
} }
final Block block = chunk.getBlock(newCorner); final Block block = chunk.getBlock(newCorner);
// TODO: block collision boxes // TODO: block collision boxes
// TODO: for the moment, always consider a full block // TODO: for the moment, always consider a full block
if (block.isSolid()) { if (block.isSolid()) {
corners[cornerIndex] = new Vec( corners.set(cornerIndex, new Vec(
Math.abs(axis.x()) > 10e-16 ? newCorner.blockX() - axis.x() * sign : originalCorner.x(), Math.abs(axis.x()) > 10e-16 ? newCorner.blockX() - axis.x() * sign : originalCorner.x(),
Math.abs(axis.y()) > 10e-16 ? newCorner.blockY() - axis.y() * sign : originalCorner.y(), Math.abs(axis.y()) > 10e-16 ? newCorner.blockY() - axis.y() * sign : originalCorner.y(),
Math.abs(axis.z()) > 10e-16 ? newCorner.blockZ() - axis.z() * sign : originalCorner.z()); Math.abs(axis.z()) > 10e-16 ? newCorner.blockZ() - axis.z() * sign : originalCorner.z()));
return true; return true;
} }
corners[cornerIndex] = newCorner; corners.set(cornerIndex, newCorner);
} }
return false; return false;
} }