调整OBB检测

This commit is contained in:
17146 2025-07-14 00:54:09 +08:00 committed by Light_Quanta
parent d42138bb9d
commit 1e56aad393
No known key found for this signature in database
GPG key ID: 11A39A1B8C890959
2 changed files with 71 additions and 199 deletions

View file

@ -36,8 +36,8 @@ import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.event.EventHooks;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.*;
import org.joml.Math;
import org.joml.*;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
@ -52,18 +52,17 @@ public class Type63Entity extends ContainerMobileVehicleEntity implements GeoEnt
public static final EntityDataAccessor<Float> YAW = SynchedEntityData.defineId(Type63Entity.class, EntityDataSerializers.FLOAT);
public static final EntityDataAccessor<List<Integer>> LOADED_AMMO = SynchedEntityData.defineId(Type63Entity.class, ModSerializers.INT_LIST_SERIALIZER.get());
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public OBB[] barrel = new OBB[12];
public OBB pitchController;
public OBB yawController;
public OBB hoe1;
public OBB hoe2;
public OBB wheel1;
public OBB wheel2;
public OBB body1;
public OBB body2;
public double interactionTick;
public Type63Entity(EntityType<Type63Entity> type, Level world) {

View file

@ -6,12 +6,9 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Intersectionf;
import org.joml.Math;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.joml.*;
import java.util.List;
import java.util.Optional;
/**
@ -258,226 +255,102 @@ public record OBB(Vector3f center, Vector3f extents, Quaternionf rotation, Part
projZ <= extents.z;
}
// 获取最近面的全局法向量
public Vector3f getClosestFaceNormal(Vec3 vec3) {
// 转换玩家位置到Vector3f
Vector3f pos = new Vector3f((float) vec3.x, (float) vec3.y, (float) vec3.z);
// 1. 转换到局部坐标系
Vector3f localPos = new Vector3f(pos).sub(center); // 减去中心
Quaternionf invRotation = new Quaternionf(rotation).invert(); // 旋转的逆
invRotation.transform(localPos); // 应用逆旋转
// 2. 计算到六个面的距离
float[] distances = new float[6];
distances[0] = Math.abs(localPos.x - extents.x); // +X
distances[1] = Math.abs(localPos.x + extents.x); // -X
distances[2] = Math.abs(localPos.y - extents.y); // +Y
distances[3] = Math.abs(localPos.y + extents.y); // -Y
distances[4] = Math.abs(localPos.z - extents.z); // +Z
distances[5] = Math.abs(localPos.z + extents.z); // -Z
// 3. 找到最近面的索引
int minIndex = 0;
for (int i = 1; i < distances.length; i++) {
if (distances[i] < distances[minIndex]) {
minIndex = i;
}
}
// 4. 获取局部法向量并转换到全局坐标系
Vector3f localNormal = getLocalNormalByIndex(minIndex);
Vector3f globalNormal = new Vector3f(localNormal);
rotation.transform(globalNormal);
globalNormal.normalize(); // 确保单位长度
return globalNormal;
}
// 根据索引返回局部法向量
private Vector3f getLocalNormalByIndex(int index) {
return switch (index) {
case 0 -> new Vector3f(1, 0, 0); // +X
case 1 -> new Vector3f(-1, 0, 0); // -X
case 2 -> new Vector3f(0, 1, 0); // +Y
case 3 -> new Vector3f(0, -1, 0); // -Y
case 4 -> new Vector3f(0, 0, 1); // +Z
case 5 -> new Vector3f(0, 0, -1); // -Z
default -> throw new IllegalArgumentException("Invalid face index");
};
}
// 计算OBB的包围球中心点相同半径为对角线长度
public float getBoundingSphereRadius() {
return extents.length();
}
// 获取面的信息全局中心点和法向量
public FaceInfo getFaceInfo(int faceIndex) {
// 局部坐标系的面法向量
Vector3f localNormal = getLocalNormalByIndex(faceIndex);
// 局部中心点从OBB中心指向该面中心
Vector3f localCenter = new Vector3f(localNormal).mul(extents);
// 转换到全局坐标系
Vector3f globalCenter = new Vector3f(localCenter);
rotation.transform(globalCenter);
globalCenter.add(center);
Vector3f globalNormal = new Vector3f(localNormal);
rotation.transform(globalNormal);
globalNormal.normalize(); // 确保单位向量
return new FaceInfo(globalCenter, globalNormal);
}
// 计算玩家位置到指定面的距离
public float distanceToFace(int faceIndex, Vector3f playerPos) {
FaceInfo faceInfo = getFaceInfo(faceIndex);
Vector3f diff = new Vector3f(playerPos).sub(faceInfo.center());
return Math.abs(diff.dot(faceInfo.normal()));
}
// 存储面信息
public record FaceInfo(Vector3f center, Vector3f normal) {
}
// 计算点到OBB的距离平方
public float distanceSquaredToPoint(Vector3f point) {
Vector3f localPoint = new Vector3f(point).sub(center);
Quaternionf invRotation = new Quaternionf(rotation).invert();
invRotation.transform(localPoint);
Vector3f closestPoint = new Vector3f();
closestPoint.x = Math.max(-extents.x, Math.min(localPoint.x, extents.x));
closestPoint.y = Math.max(-extents.y, Math.min(localPoint.y, extents.y));
closestPoint.z = Math.max(-extents.z, Math.min(localPoint.z, extents.z));
return localPoint.distanceSquared(closestPoint);
}
/**
* 寻找距离某一个位置最近的OBB
* 获取玩家看向的某个OBB
*/
@Nullable
public static OBB findClosestOBB(List<OBB> obbList, Vec3 vec3) {
if (obbList == null || obbList.isEmpty()) {
public static OBB getLookingObb(Player player, double range) {
Entity lookingEntity = TraceTool.findLookingEntity(player, range);
if (!(lookingEntity instanceof OBBEntity obbEntity)) {
return null;
}
Vector3f pos = vec3.toVector3f();
// 获取玩家视线信息
Vec3 eyePos = player.getEyePosition(1.0f);
Vec3 viewVec = player.getViewVector(1.0f);
Vec3 lookEnd = eyePos.add(viewVec.scale(range));
OBB closestOBB = null;
float minDistanceSq = Float.MAX_VALUE;
double minDistanceSq = Double.MAX_VALUE;
for (OBB obb : obbList) {
float distToCenterSq = pos.distanceSquared(obb.center());
float boundingRadiusSq = obb.getBoundingSphereRadius() * obb.getBoundingSphereRadius();
for (OBB obb : obbEntity.getOBBs()) {
// 使用精确的射线相交检测
Vec3 hitPos = rayIntersect(obb, eyePos, lookEnd);
if (distToCenterSq - boundingRadiusSq > minDistanceSq) {
continue;
}
if (hitPos != null) {
// 计算交点到眼睛的平方距离
double distanceSq = eyePos.distanceToSqr(hitPos);
float distSq = obb.distanceSquaredToPoint(pos);
if (distSq < minDistanceSq) {
minDistanceSq = distSq;
closestOBB = obb;
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
closestOBB = obb;
}
}
}
return closestOBB;
}
// 查找最近的面
@Nullable
public static ClosestFaceResult findClosestFace(List<OBB> obbList, Vec3 playerPos) {
if (obbList == null || obbList.isEmpty()) {
return null;
}
public static Vec3 rayIntersect(OBB obb, Vec3 start, Vec3 end) {
// 获取 OBB 信息
Vec3 center = new Vec3(obb.center());
Vec3 extents = new Vec3(obb.extents());
Quaternionf rotation = obb.rotation();
Vector3f pos = playerPos.toVector3f();
OBB closestOBB = null;
int closestFaceIndex = -1;
float minDistance = Float.MAX_VALUE;
Vector3f closestFaceNormal = null;
Vector3f closestFaceCenter = null;
// 计算逆旋转
Quaternionf inverse = new Quaternionf(rotation).conjugate();
// 第一阶段使用包围球快速筛选候选OBB
for (OBB obb : obbList) {
// 计算玩家到OBB中心的距离
float distToCenter = pos.distance(obb.center());
// 转换起点和终点到局部坐标系
Vector3f localStart = toLocal(obb, start);
Vector3f localEnd = toLocal(obb, end);
// 如果距离大于包围球半径不可能比当前最小值更近
if (distToCenter > obb.getBoundingSphereRadius()) {
continue;
}
// 定义 OBB AABB在局部坐标系中
float minX = (float) -extents.x, minY = (float) -extents.y, minZ = (float) -extents.z;
float maxX = (float) extents.x, maxY = (float) extents.y, maxZ = (float) extents.z;
// 第二阶段检查该OBB的所有面
for (int faceIndex = 0; faceIndex < 6; faceIndex++) {
float dist = obb.distanceToFace(faceIndex, pos);
// 使用 JOML 的相交检测
Vector2f result = new Vector2f();
boolean intersects = Intersectionf.intersectRayAab(
localStart.x, localStart.y, localStart.z,
localEnd.x - localStart.x, localEnd.y - localStart.y, localEnd.z - localStart.z,
minX, minY, minZ,
maxX, maxY, maxZ,
result
);
// 更新最小距离
if (dist < minDistance) {
minDistance = dist;
closestOBB = obb;
closestFaceIndex = faceIndex;
OBB.FaceInfo faceInfo = obb.getFaceInfo(faceIndex);
closestFaceNormal = faceInfo.normal();
closestFaceCenter = faceInfo.center();
}
}
}
if (intersects) {
float t = result.x; // 交点参数
Vector3f localHit = new Vector3f(
localStart.x + t * (localEnd.x - localStart.x),
localStart.y + t * (localEnd.y - localStart.y),
localStart.z + t * (localEnd.z - localStart.z)
);
if (closestOBB == null) {
return null;
}
return new ClosestFaceResult(closestOBB, closestFaceIndex, minDistance,
closestFaceCenter, closestFaceNormal);
}
// 存储最近面结果
public record ClosestFaceResult(OBB obb, int faceIndex, float distance, Vector3f faceCenter, Vector3f faceNormal) {
}
//获取玩家看向的某个OBB
public static OBB getLookingObb(Player player, double range) {
Entity lookingEntity = TraceTool.findLookingEntity(player, range);
if (lookingEntity instanceof OBBEntity obbEntity) {
var obbList = obbEntity.getOBBs();
for (OBB obb : obbList) {
Vec3 hitPos = TraceTool.playerFindLookingPos(player, lookingEntity, range);
if (hitPos != null && obb.contains(hitPos.add(player.getViewVector(1).scale(0.05)))) {
// player.displayClientMessage(Component.literal(String.valueOf(obb)), true);
// 测试粒子
// if (player.level() instanceof ServerLevel serverLevel) {
// sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), hitPos.x, hitPos.y, hitPos.z, 1, 0, 0, 0, 0.01, false);
// }
return obb;
}
}
// 转换回世界坐标系
rotation.transform(localHit);
return new Vec3(localHit.x + center.x, localHit.y + center.y, localHit.z + center.z);
}
return null;
}
public static Optional<OBB> getNearestOBB(Vec3 vec3, List<OBB> obbs) {
if (obbs.isEmpty()) return Optional.empty();
// 将世界坐标点转换到 OBB 局部坐标系
private static Vector3f toLocal(OBB obb, Vec3 worldPoint) {
// 获取 OBB 信息
Vec3 center = new Vec3(obb.center());
Quaternionf rotation = obb.rotation();
Quaternionf inverse = new Quaternionf(rotation).conjugate();
OBB nearest = null;
double minDistanceSq = Double.MAX_VALUE;
// 计算相对于中心的向量
Vector3f relative = new Vector3f(
(float) (worldPoint.x - center.x),
(float) (worldPoint.y - center.y),
(float) (worldPoint.z - center.z)
);
for (OBB obb : obbs) {
double distanceSq = obb.center.distanceSquared((float) vec3.x, (float) vec3.y, (float) vec3.z);
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
nearest = obb;
}
}
return Optional.ofNullable(nearest);
// 应用逆旋转世界坐标 -> 局部坐标
inverse.transform(relative);
return relative;
}
public enum Part {