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;
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.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
/**
* See https://wiki.vg/Entity_metadata#Mobs_2
@ -13,6 +18,14 @@ public class BoundingBox {
private final Entity entity;
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.
*
@ -211,14 +224,13 @@ public class BoundingBox {
*
* @return the points at the bottom of the {@link BoundingBox}
*/
@NotNull
public Vec[] getBottomFace() {
return new Vec[]{
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()),
new Vec(getMinX(), getMinY(), getMaxZ()),
};
public @NotNull List<Vec> getBottomFace() {
this.bottomFace = get(bottomFace, () ->
List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), 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}
*/
@NotNull
public Vec[] getTopFace() {
return new Vec[]{
new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()),
};
public @NotNull List<Vec> getTopFace() {
this.topFace = get(topFace, () ->
List.of(new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), 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}
*/
@NotNull
public Vec[] getLeftFace() {
return new Vec[]{
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMinY(), getMaxZ()),
};
public @NotNull List<Vec> getLeftFace() {
this.leftFace = get(leftFace, () ->
List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), 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}
*/
@NotNull
public Vec[] getRightFace() {
return new Vec[]{
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()),
};
public @NotNull List<Vec> getRightFace() {
this.rightFace = get(rightFace, () ->
List.of(new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), 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}
*/
@NotNull
public Vec[] getFrontFace() {
return new Vec[]{
new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMaxY(), getMinZ()),
new Vec(getMinX(), getMaxY(), getMinZ()),
};
public @NotNull List<Vec> getFrontFace() {
this.frontFace = get(frontFace, () ->
List.of(new Vec(getMinX(), getMinY(), getMinZ()),
new Vec(getMaxX(), getMinY(), getMinZ()),
new Vec(getMaxX(), 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}
*/
@NotNull
public Vec[] getBackFace() {
return new Vec[]{
public @NotNull List<Vec> getBackFace() {
this.backFace = get(backFace, () -> List.of(
new Vec(getMinX(), getMinY(), getMaxZ()),
new Vec(getMaxX(), getMinY(), getMaxZ()),
new Vec(getMaxX(), getMaxY(), getMaxZ()),
new Vec(getMinX(), getMaxY(), getMaxZ()),
};
new Vec(getMinX(), getMaxY(), getMaxZ())));
return backFace;
}
@Override
@ -307,4 +314,14 @@ public class BoundingBox {
result += "[" + getMinZ() + " : " + getMaxZ() + "]";
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 org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class CollisionUtils {
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
* @return result of the step
*/
private static StepResult stepAxis(Instance instance, Chunk originChunk, Vec startPosition, Vec axis, double stepAmount, Vec... corners) {
if (corners.length == 0)
return new StepResult(startPosition, false); // avoid degeneracy in following computations
final Vec[] originalCorners = corners.clone();
private static StepResult stepAxis(Instance instance, Chunk originChunk, Vec startPosition, Vec axis, double stepAmount, List<Vec> corners) {
final List<Vec> mutableCorners = new ArrayList<>(corners);
final double sign = Math.signum(stepAmount);
final int blockLength = (int) stepAmount;
final double remainingLength = stepAmount - blockLength;
// used to determine if 'remainingLength' should be used
boolean collisionFound = false;
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;
}
// add remainingLength
if (!collisionFound) {
collisionFound = stepOnce(instance, originChunk, axis, remainingLength, corners);
collisionFound = stepOnce(instance, originChunk, axis, remainingLength, mutableCorners);
}
// find the corner which moved the least
double smallestDisplacement = Double.POSITIVE_INFINITY;
for (int i = 0; i < corners.length; i++) {
final double displacement = originalCorners[i].distance(corners[i]);
for (int i = 0; i < corners.size(); i++) {
final double displacement = corners.get(i).distance(mutableCorners.get(i));
if (displacement < smallestDisplacement) {
smallestDisplacement = displacement;
}
@ -111,10 +111,10 @@ public class CollisionUtils {
* @param corners the corners of the bounding box to consider
* @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);
for (int cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
final Vec originalCorner = corners[cornerIndex];
for (int cornerIndex = 0; cornerIndex < corners.size(); cornerIndex++) {
final Vec originalCorner = corners.get(cornerIndex);
final Vec newCorner = originalCorner.add(axis.mul(amount));
final Chunk chunk = ChunkUtils.retrieve(instance, originChunk, newCorner);
if (!ChunkUtils.isLoaded(chunk)) {
@ -122,17 +122,16 @@ public class CollisionUtils {
return true;
}
final Block block = chunk.getBlock(newCorner);
// TODO: block collision boxes
// TODO: for the moment, always consider a full block
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.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;
}
corners[cornerIndex] = newCorner;
corners.set(cornerIndex, newCorner);
}
return false;
}