216 lines
7.2 KiB
Java
216 lines
7.2 KiB
Java
package com.atsuishio.superbwarfare.tools;
|
||
|
||
import net.minecraft.world.phys.AABB;
|
||
import org.joml.Intersectionf;
|
||
import org.joml.Math;
|
||
import org.joml.Quaternionf;
|
||
import org.joml.Vector3f;
|
||
|
||
import java.util.Optional;
|
||
|
||
/**
|
||
* Codes based on @AnECanSaiTin's <a href="https://github.com/AnECanSaiTin/HitboxAPI">HitboxAPI</a>
|
||
*
|
||
* @param center 旋转中心
|
||
* @param extents 三个轴向上的半长
|
||
* @param rotation 旋转
|
||
*/
|
||
public record OBB(Vector3f center, Vector3f extents, Quaternionf rotation) {
|
||
|
||
public void setCenter(Vector3f center) {
|
||
this.center.set(center);
|
||
}
|
||
|
||
public void setExtents(Vector3f extents) {
|
||
this.extents.set(extents);
|
||
}
|
||
|
||
public void setRotation(Quaternionf rotation) {
|
||
this.rotation.set(rotation);
|
||
}
|
||
|
||
/**
|
||
* 获取OBB的8个顶点坐标
|
||
*
|
||
* @return 顶点坐标
|
||
*/
|
||
public Vector3f[] getVertices() {
|
||
Vector3f[] vertices = new Vector3f[8];
|
||
|
||
Vector3f[] localVertices = new Vector3f[]{
|
||
new Vector3f(-extents.x, -extents.y, -extents.z),
|
||
new Vector3f(extents.x, -extents.y, -extents.z),
|
||
new Vector3f(extents.x, extents.y, -extents.z),
|
||
new Vector3f(-extents.x, extents.y, -extents.z),
|
||
new Vector3f(-extents.x, -extents.y, extents.z),
|
||
new Vector3f(extents.x, -extents.y, extents.z),
|
||
new Vector3f(extents.x, extents.y, extents.z),
|
||
new Vector3f(-extents.x, extents.y, extents.z)
|
||
};
|
||
|
||
for (int i = 0; i < 8; i++) {
|
||
Vector3f vertex = localVertices[i];
|
||
vertex.rotate(rotation);
|
||
vertex.add(center);
|
||
vertices[i] = vertex;
|
||
}
|
||
|
||
return vertices;
|
||
}
|
||
|
||
/**
|
||
* 获取OBB的三个正交轴
|
||
*
|
||
* @return 正交轴
|
||
*/
|
||
public Vector3f[] getAxes() {
|
||
Vector3f[] axes = new Vector3f[]{
|
||
new Vector3f(1, 0, 0),
|
||
new Vector3f(0, 1, 0),
|
||
new Vector3f(0, 0, 1)};
|
||
rotation.transform(axes[0]);
|
||
rotation.transform(axes[1]);
|
||
rotation.transform(axes[2]);
|
||
return axes;
|
||
}
|
||
|
||
/**
|
||
* 判断两个OBB是否相撞
|
||
*/
|
||
public static boolean isColliding(OBB obb, OBB other) {
|
||
Vector3f[] axes1 = obb.getAxes();
|
||
Vector3f[] axes2 = other.getAxes();
|
||
return Intersectionf.testObOb(obb.center(), axes1[0], axes1[1], axes1[2], obb.extents(),
|
||
other.center(), axes2[0], axes2[1], axes2[2], other.extents());
|
||
}
|
||
|
||
/**
|
||
* 判断OBB和AABB是否相撞
|
||
*/
|
||
public static boolean isColliding(OBB obb, AABB aabb) {
|
||
Vector3f obbCenter = obb.center();
|
||
Vector3f[] obbAxes = obb.getAxes();
|
||
Vector3f obbHalfExtents = obb.extents();
|
||
Vector3f aabbCenter = aabb.getCenter().toVector3f();
|
||
Vector3f aabbHalfExtents = new Vector3f((float) (aabb.getXsize() / 2f), (float) (aabb.getYsize() / 2f), (float) (aabb.getZsize() / 2f));
|
||
return Intersectionf.testObOb(
|
||
obbCenter.x, obbCenter.y, obbCenter.z,
|
||
obbAxes[0].x, obbAxes[0].y, obbAxes[0].z,
|
||
obbAxes[1].x, obbAxes[1].y, obbAxes[1].z,
|
||
obbAxes[2].x, obbAxes[2].y, obbAxes[2].z,
|
||
obbHalfExtents.x, obbHalfExtents.y, obbHalfExtents.z,
|
||
aabbCenter.x, aabbCenter.y, aabbCenter.z,
|
||
1, 0, 0,
|
||
0, 1, 0,
|
||
0, 0, 1,
|
||
aabbHalfExtents.x, aabbHalfExtents.y, aabbHalfExtents.z
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 计算OBB上离待判定点最近的点
|
||
*
|
||
* @param point 待判定点
|
||
* @param obb OBB盒
|
||
* @return 在OBB上离待判定点最近的点
|
||
*/
|
||
public static Vector3f getClosestPointOBB(Vector3f point, OBB obb) {
|
||
Vector3f nearP = new Vector3f(obb.center());
|
||
Vector3f dist = point.sub(nearP, new Vector3f());
|
||
|
||
float[] extents = new float[]{obb.extents().x, obb.extents().y, obb.extents().z};
|
||
Vector3f[] axes = obb.getAxes();
|
||
|
||
for (int i = 0; i < 3; i++) {
|
||
float distance = dist.dot(axes[i]);
|
||
distance = Math.clamp(distance, -extents[i], extents[i]);
|
||
|
||
nearP.x += distance * axes[i].x;
|
||
nearP.y += distance * axes[i].y;
|
||
nearP.z += distance * axes[i].z;
|
||
}
|
||
|
||
return nearP;
|
||
}
|
||
|
||
public Optional<Vector3f> clip(Vector3f pFrom, Vector3f pTo) {
|
||
// 计算OBB的局部坐标系基向量(世界坐标系中的方向)
|
||
Vector3f[] axes = new Vector3f[3];
|
||
axes[0] = rotation.transform(new Vector3f(1, 0, 0));
|
||
axes[1] = rotation.transform(new Vector3f(0, 1, 0));
|
||
axes[2] = rotation.transform(new Vector3f(0, 0, 1));
|
||
|
||
// 将点转换到OBB局部坐标系
|
||
Vector3f localFrom = worldToLocal(pFrom, axes);
|
||
Vector3f localTo = worldToLocal(pTo, axes);
|
||
|
||
// 射线方向(局部坐标系)
|
||
Vector3f dir = new Vector3f(localTo).sub(localFrom);
|
||
|
||
// Slab算法参数
|
||
double tEnter = 0.0; // 进入时间
|
||
double tExit = 1.0; // 离开时间
|
||
|
||
// 在三个轴上执行Slab算法
|
||
for (int i = 0; i < 3; i++) {
|
||
double min = -extents.get(i);
|
||
double max = extents.get(i);
|
||
double origin = localFrom.get(i);
|
||
double direction = dir.get(i);
|
||
|
||
// 处理射线平行于轴的情况
|
||
if (Math.abs(direction) < 1e-7f) {
|
||
if (origin < min || origin > max) {
|
||
return Optional.empty();
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// 计算与两个平面的交点参数
|
||
double t1 = (min - origin) / direction;
|
||
double t2 = (max - origin) / direction;
|
||
|
||
// 确保tNear是近平面,tFar是远平面
|
||
double tNear = Math.min(t1, t2);
|
||
double tFar = Math.max(t1, t2);
|
||
|
||
// 更新进入/离开时间
|
||
if (tNear > tEnter) tEnter = tNear;
|
||
if (tFar < tExit) tExit = tFar;
|
||
|
||
// 检查是否提前退出(无交点)
|
||
if (tEnter > tExit) {
|
||
return Optional.empty();
|
||
}
|
||
}
|
||
|
||
// 检查是否有有效交点
|
||
if (tEnter >= 0 && tEnter <= 1) {
|
||
// 计算局部坐标系中的交点
|
||
Vector3f localHit = new Vector3f(dir).mul((float) tEnter).add(localFrom);
|
||
// 转换回世界坐标系
|
||
return Optional.of(localToWorld(localHit, axes));
|
||
}
|
||
|
||
return Optional.empty();
|
||
}
|
||
|
||
// 世界坐标转局部坐标
|
||
private Vector3f worldToLocal(Vector3f worldPoint, Vector3f[] axes) {
|
||
Vector3f rel = new Vector3f(worldPoint).sub(center);
|
||
return new Vector3f(
|
||
rel.dot(axes[0]),
|
||
rel.dot(axes[1]),
|
||
rel.dot(axes[2])
|
||
);
|
||
}
|
||
|
||
// 局部坐标转世界坐标
|
||
private Vector3f localToWorld(Vector3f localPoint, Vector3f[] axes) {
|
||
Vector3f result = new Vector3f(center);
|
||
result.add(axes[0].mul(localPoint.x, new Vector3f()));
|
||
result.add(axes[1].mul(localPoint.y, new Vector3f()));
|
||
result.add(axes[2].mul(localPoint.z, new Vector3f()));
|
||
return result;
|
||
}
|
||
}
|