Frustum filtering tracker

This commit is contained in:
2026-07-16 23:09:01 +08:00
parent dcbf2e541c
commit 1c5998411d
3 changed files with 359 additions and 0 deletions
@@ -0,0 +1,286 @@
package io.nanachiyo0721.shiroha.utils;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NonNull;
public class Culling {
private static final float VERTICAL_FOV_RAD = (float) Math.toRadians(120);
private static final float ASPECT = 1.0f;
private static final float NEAR = 0.1f;
private static final float FAR = 32.0f;
private float fov;
private float aspect;
private float near;
private float far;
private final Player player;
private float[] planes;
@Contract(value = "_ -> new", pure = true)
public static @NonNull Culling newDefault(Player player) {
return fromBuilder(new Builder(), player);
}
@Contract(value = "_, _ -> new", pure = true)
public static @NonNull Culling fromBuilder(@NonNull Builder builder, Player player) {
return new Culling(builder.fov, builder.aspect, builder.near, builder.far, player);
}
public static class Builder {
private float fov;
private float aspect;
private float near;
private float far;
public Builder() {
this.fov = VERTICAL_FOV_RAD;
this.aspect = ASPECT;
this.near = NEAR;
this.far = FAR;
}
public Builder fov(float fov) {
this.fov = fov;
return this;
}
public Builder aspect(float aspect) {
this.aspect = aspect;
return this;
}
public Builder near(float near) {
this.near = near;
return this;
}
public Builder far(float far) {
this.far = far;
return this;
}
public Culling build(Player player) {
return fromBuilder(this, player);
}
}
public Culling(float fov, float aspect, float near, float far, Player player) {
this.fov = fov;
this.aspect = aspect;
this.near = near;
this.far = far;
this.player = player;
}
public static float @NonNull [] buildFrustumPlanes(
float eyeX, float eyeY, float eyeZ,
float forwardX, float forwardY, float forwardZ,
float upX, float upY, float upZ,
float verticalFovRad, float aspect, float nearDist, float farDist) {
float lenF = (float) Math.sqrt(forwardX * forwardX + forwardY * forwardY + forwardZ * forwardZ);
float fx = forwardX / lenF;
float fy = forwardY / lenF;
float fz = forwardZ / lenF;
float rx = fy * upZ - fz * upY;
float ry = fz * upX - fx * upZ;
float rz = fx * upY - fy * upX;
float lenR = (float) Math.sqrt(rx * rx + ry * ry + rz * rz);
if (lenR < 1.0e-4f) {
float fallbackUpX = 0.0f, fallbackUpY = 0.0f, fallbackUpZ = 1.0f;
if (Math.abs(fz) > 0.999f) {
fallbackUpX = 1.0f;
fallbackUpY = 0.0f;
fallbackUpZ = 0.0f;
}
rx = fy * fallbackUpZ - fz * fallbackUpY;
ry = fz * fallbackUpX - fx * fallbackUpZ;
rz = fx * fallbackUpY - fy * fallbackUpX;
lenR = (float) Math.sqrt(rx * rx + ry * ry + rz * rz);
}
rx /= lenR;
ry /= lenR;
rz /= lenR;
float ux = ry * fz - rz * fy;
float uy = rz * fx - rx * fz;
float uz = rx * fy - ry * fx;
float lenU = (float) Math.sqrt(ux * ux + uy * uy + uz * uz);
ux /= lenU;
uy /= lenU;
uz /= lenU;
float tanHalf = (float) Math.tan(verticalFovRad * 0.5);
float halfVNear = tanHalf * nearDist;
float halfHNear = halfVNear * aspect;
float halfVFar = tanHalf * farDist;
float halfHFar = halfVFar * aspect;
float nearCX = eyeX + fx * nearDist;
float nearCY = eyeY + fy * nearDist;
float nearCZ = eyeZ + fz * nearDist;
float farCX = eyeX + fx * farDist;
float farCY = eyeY + fy * farDist;
float farCZ = eyeZ + fz * farDist;
float ntlX = nearCX + ux * halfVNear - rx * halfHNear;
float ntlY = nearCY + uy * halfVNear - ry * halfHNear;
float ntlZ = nearCZ + uz * halfVNear - rz * halfHNear;
float ntrX = nearCX + ux * halfVNear + rx * halfHNear;
float ntrY = nearCY + uy * halfVNear + ry * halfHNear;
float ntrZ = nearCZ + uz * halfVNear + rz * halfHNear;
float nblX = nearCX - ux * halfVNear - rx * halfHNear;
float nblY = nearCY - uy * halfVNear - ry * halfHNear;
float nblZ = nearCZ - uz * halfVNear - rz * halfHNear;
float nbrX = nearCX - ux * halfVNear + rx * halfHNear;
float nbrY = nearCY - uy * halfVNear + ry * halfHNear;
float nbrZ = nearCZ - uz * halfVNear + rz * halfHNear;
float ftlX = farCX + ux * halfVFar - rx * halfHFar;
float ftlY = farCY + uy * halfVFar - ry * halfHFar;
float ftlZ = farCZ + uz * halfVFar - rz * halfHFar;
float ftrX = farCX + ux * halfVFar + rx * halfHFar;
float ftrY = farCY + uy * halfVFar + ry * halfHFar;
float ftrZ = farCZ + uz * halfVFar + rz * halfHFar;
float fbrX = farCX - ux * halfVFar + rx * halfHFar;
float fbrY = farCY - uy * halfVFar + ry * halfHFar;
float fbrZ = farCZ - uz * halfVFar + rz * halfHFar;
float midDist = (nearDist + farDist) * 0.5f;
float refX = eyeX + fx * midDist;
float refY = eyeY + fy * midDist;
float refZ = eyeZ + fz * midDist;
float[] planes = new float[24];
int idx = 0;
idx = computePlane(planes, idx,
eyeX, eyeY, eyeZ,
ntlX, ntlY, ntlZ,
nblX, nblY, nblZ,
refX, refY, refZ);
idx = computePlane(planes, idx,
eyeX, eyeY, eyeZ,
nbrX, nbrY, nbrZ,
ntrX, ntrY, ntrZ,
refX, refY, refZ);
idx = computePlane(planes, idx,
eyeX, eyeY, eyeZ,
nblX, nblY, nblZ,
nbrX, nbrY, nbrZ,
refX, refY, refZ);
idx = computePlane(planes, idx,
eyeX, eyeY, eyeZ,
ntrX, ntrY, ntrZ,
ntlX, ntlY, ntlZ,
refX, refY, refZ);
idx = computePlane(planes, idx,
ntlX, ntlY, ntlZ,
ntrX, ntrY, ntrZ,
nblX, nblY, nblZ,
refX, refY, refZ);
idx = computePlane(planes, idx,
ftrX, ftrY, ftrZ,
ftlX, ftlY, ftlZ,
fbrX, fbrY, fbrZ,
refX, refY, refZ);
return planes;
}
private static int computePlane(float[] out, int start,
float p0x, float p0y, float p0z,
float p1x, float p1y, float p1z,
float p2x, float p2y, float p2z,
float refX, float refY, float refZ) {
float e1x = p1x - p0x, e1y = p1y - p0y, e1z = p1z - p0z;
float e2x = p2x - p0x, e2y = p2y - p0y, e2z = p2z - p0z;
float nx = e1y * e2z - e1z * e2y;
float ny = e1z * e2x - e1x * e2z;
float nz = e1x * e2y - e1y * e2x;
float d = -(nx * p0x + ny * p0y + nz * p0z);
float val = nx * refX + ny * refY + nz * refZ + d;
if (val < 0.0f) {
nx = -nx;
ny = -ny;
nz = -nz;
d = -d;
}
float len = (float) Math.sqrt(nx * nx + ny * ny + nz * nz);
nx /= len;
ny /= len;
nz /= len;
d = -(nx * p0x + ny * p0y + nz * p0z);
out[start] = nx;
out[start + 1] = ny;
out[start + 2] = nz;
out[start + 3] = d;
return start + 4;
}
public void apply(@NonNull Builder builder) {
this.fov = builder.fov;
this.aspect = builder.aspect;
this.near = builder.near;
this.far = builder.far;
}
public void updateFrustum() {
Vec3 eye = this.player.getEyePosition();
Vec3 look = this.player.getLookAngle();
this.planes = buildFrustumPlanes(
(float) eye.x, (float) eye.y, (float) eye.z,
(float) look.x, (float) look.y, (float) look.z,
0.0f, 1.0f, 0.0f,
this.fov, this.aspect, this.near, this.far
);
}
public void far(float far) {
this.far = far;
}
public boolean isVisibleFor(@NonNull AABB box) {
final float[] planes = this.planes;
for (int i = 0; i < 6; i++) {
int base = i << 2;
float nx = planes[base], ny = planes[base+1], nz = planes[base+2], d = planes[base+3];
float px = (nx >= 0) ? (float) box.maxX : (float) box.minX;
float py = (ny >= 0) ? (float) box.maxY : (float) box.minY;
float pz = (nz >= 0) ? (float) box.maxZ : (float) box.minZ;
if (nx * px + ny * py + nz * pz + d < 0) {
return false;
}
}
return true;
}
}