diff --git a/src/main/java/com/atsuishio/superbwarfare/client/renderer/special/OBBRenderer.java b/src/main/java/com/atsuishio/superbwarfare/client/renderer/special/OBBRenderer.java
new file mode 100644
index 000000000..f442949f1
--- /dev/null
+++ b/src/main/java/com/atsuishio/superbwarfare/client/renderer/special/OBBRenderer.java
@@ -0,0 +1,40 @@
+package com.atsuishio.superbwarfare.client.renderer.special;
+
+import com.atsuishio.superbwarfare.tools.OBB;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import net.minecraft.client.renderer.LevelRenderer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.phys.Vec3;
+import org.joml.Quaternionf;
+import org.joml.Vector3f;
+
+/**
+ * Codes based on @AnECanSaiTin's HitboxAPI
+ **/
+public class OBBRenderer {
+
+ public static final OBBRenderer INSTANCE = new OBBRenderer();
+
+ public void render(Entity entity, OBB obb, PoseStack poseStack, VertexConsumer buffer, float red, float green, float blue, float alpha) {
+ Vec3 position = entity.position();
+ Vector3f center = obb.center();
+ Vector3f halfExtents = obb.extents();
+ Quaternionf rotation = obb.rotation();
+ renderOBB(
+ poseStack, buffer,
+ (float) (center.x() - position.x()), (float) (center.y() - position.y()), (float) (center.z() - position.z()),
+ rotation,
+ halfExtents.x(), halfExtents.y(), halfExtents.z(),
+ red, green, blue, alpha
+ );
+ }
+
+ public static void renderOBB(PoseStack poseStack, VertexConsumer buffer, float centerX, float centerY, float centerZ, Quaternionf rotation, float halfX, float halfY, float halfZ, float red, float green, float blue, float alpha) {
+ poseStack.pushPose();
+ poseStack.translate(centerX, centerY, centerZ);
+ poseStack.mulPose(rotation);
+ LevelRenderer.renderLineBox(poseStack, buffer, -halfX, -halfY, -halfZ, halfX, halfY, halfZ, red, green, blue, alpha);
+ poseStack.popPose();
+ }
+}
diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/OBBEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/OBBEntity.java
new file mode 100644
index 000000000..557c7f5dc
--- /dev/null
+++ b/src/main/java/com/atsuishio/superbwarfare/entity/OBBEntity.java
@@ -0,0 +1,10 @@
+package com.atsuishio.superbwarfare.entity;
+
+import com.atsuishio.superbwarfare.tools.OBB;
+
+public interface OBBEntity {
+
+ OBB getOBB();
+
+ void updateOBB();
+}
diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/Yx100Entity.java b/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/Yx100Entity.java
index a73a5ba97..efb588627 100644
--- a/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/Yx100Entity.java
+++ b/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/Yx100Entity.java
@@ -3,6 +3,7 @@ package com.atsuishio.superbwarfare.entity.vehicle;
import com.atsuishio.superbwarfare.Mod;
import com.atsuishio.superbwarfare.config.server.ExplosionConfig;
import com.atsuishio.superbwarfare.config.server.VehicleConfig;
+import com.atsuishio.superbwarfare.entity.OBBEntity;
import com.atsuishio.superbwarfare.entity.projectile.SwarmDroneEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.ContainerMobileVehicleEntity;
import com.atsuishio.superbwarfare.entity.vehicle.base.LandArmorEntity;
@@ -62,8 +63,7 @@ import net.neoforged.neoforge.network.PacketDistributor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Math;
-import org.joml.Matrix4f;
-import org.joml.Vector4f;
+import org.joml.*;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.*;
@@ -77,7 +77,7 @@ import java.util.List;
import static com.atsuishio.superbwarfare.client.RenderHelper.preciseBlit;
import static com.atsuishio.superbwarfare.tools.ParticleTool.sendParticle;
-public class Yx100Entity extends ContainerMobileVehicleEntity implements GeoEntity, LandArmorEntity, WeaponVehicleEntity {
+public class Yx100Entity extends ContainerMobileVehicleEntity implements GeoEntity, LandArmorEntity, WeaponVehicleEntity, OBBEntity {
public static final EntityDataAccessor MG_AMMO = SynchedEntityData.defineId(Yx100Entity.class, EntityDataSerializers.INT);
public static final EntityDataAccessor LOADED_AP = SynchedEntityData.defineId(Yx100Entity.class, EntityDataSerializers.INT);
@@ -88,8 +88,11 @@ public class Yx100Entity extends ContainerMobileVehicleEntity implements GeoEnti
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public int droneReloadCoolDown;
+ public OBB obb;
+
public Yx100Entity(EntityType type, Level world) {
super(type, world);
+ this.obb = new OBB(this.position().toVector3f(), new Vector3f(3.5f, 3.0f, 5.0f), new Quaternionf());
}
@Override
@@ -227,6 +230,8 @@ public class Yx100Entity extends ContainerMobileVehicleEntity implements GeoEnti
public void baseTick() {
super.baseTick();
+ this.updateOBB();
+
if (getLeftTrack() < 0) {
setLeftTrack(80);
}
@@ -1286,4 +1291,17 @@ public class Yx100Entity extends ContainerMobileVehicleEntity implements GeoEnti
public @Nullable ResourceLocation getVehicleItemIcon() {
return Mod.loc("textures/gui/vehicle/type/land.png");
}
+
+ @Override
+ public OBB getOBB() {
+ return this.obb;
+ }
+
+ // TODO 实现正确的旋转设置
+ @Override
+ public void updateOBB() {
+ Quaternionf rotation = eulerToQuaternion(-getRoll(), -getYRot(), getXRot());
+ this.obb.setRotation(rotation);
+ this.obb.center().set(this.position().toVector3f());
+ }
}
diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/base/VehicleEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/base/VehicleEntity.java
index ac990bc5f..69b487fe6 100644
--- a/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/base/VehicleEntity.java
+++ b/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/base/VehicleEntity.java
@@ -816,6 +816,23 @@ public abstract class VehicleEntity extends Entity {
return transform.transform(new Vector4f(x, y, z, 1));
}
+ public static Quaternionf eulerToQuaternion(float yaw, float pitch, float roll) {
+ double cy = Math.cos(yaw * 0.5 * Mth.DEG_TO_RAD);
+ double sy = Math.sin(yaw * 0.5 * Mth.DEG_TO_RAD);
+ double cp = Math.cos(pitch * 0.5 * Mth.DEG_TO_RAD);
+ double sp = Math.sin(pitch * 0.5 * Mth.DEG_TO_RAD);
+ double cr = Math.cos(roll * 0.5 * Mth.DEG_TO_RAD);
+ double sr = Math.sin(roll * 0.5 * Mth.DEG_TO_RAD);
+
+ Quaternionf q = new Quaternionf();
+ q.w = (float) (cy * cp * cr + sy * sp * sr);
+ q.x = (float) (cy * cp * sr - sy * sp * cr);
+ q.y = (float) (sy * cp * sr + cy * sp * cr);
+ q.z = (float) (sy * cp * cr - cy * sp * sr);
+
+ return q;
+ }
+
public void handleClientSync() {
if (isControlledByLocalInstance()) {
interpolationSteps = 0;
diff --git a/src/main/java/com/atsuishio/superbwarfare/mixins/EntityRenderDispatcherMixin.java b/src/main/java/com/atsuishio/superbwarfare/mixins/EntityRenderDispatcherMixin.java
new file mode 100644
index 000000000..416b081c9
--- /dev/null
+++ b/src/main/java/com/atsuishio/superbwarfare/mixins/EntityRenderDispatcherMixin.java
@@ -0,0 +1,24 @@
+package com.atsuishio.superbwarfare.mixins;
+
+import com.atsuishio.superbwarfare.client.renderer.special.OBBRenderer;
+import com.atsuishio.superbwarfare.entity.OBBEntity;
+import com.mojang.blaze3d.vertex.PoseStack;
+import com.mojang.blaze3d.vertex.VertexConsumer;
+import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
+import net.minecraft.world.entity.Entity;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(EntityRenderDispatcher.class)
+public class EntityRenderDispatcherMixin {
+
+ @Inject(method = "renderHitbox",
+ at = @At("RETURN"))
+ private static void renderHitbox(PoseStack poseStack, VertexConsumer buffer, Entity entity, float red, float green, float blue, float alpha, CallbackInfo ci) {
+ if (entity instanceof OBBEntity obbEntity) {
+ OBBRenderer.INSTANCE.render(entity, obbEntity.getOBB(), poseStack, buffer, 1, 1, 1, 1);
+ }
+ }
+}
diff --git a/src/main/java/com/atsuishio/superbwarfare/tools/OBB.java b/src/main/java/com/atsuishio/superbwarfare/tools/OBB.java
new file mode 100644
index 000000000..2cc39cc70
--- /dev/null
+++ b/src/main/java/com/atsuishio/superbwarfare/tools/OBB.java
@@ -0,0 +1,133 @@
+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;
+
+/**
+ * Codes based on @AnECanSaiTin's HitboxAPI
+ *
+ * @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;
+ }
+}
diff --git a/src/main/resources/mixins.superbwarfare.json b/src/main/resources/mixins.superbwarfare.json
index 5ca591322..bf1fc72a8 100644
--- a/src/main/resources/mixins.superbwarfare.json
+++ b/src/main/resources/mixins.superbwarfare.json
@@ -16,6 +16,7 @@
"CameraMixin",
"ClientPacketListenerMixin",
"ClientPlayerEntityMixin",
+ "EntityRenderDispatcherMixin",
"GameRendererMixin",
"ItemInHandLayerMixin",
"KeyboardInputMixin",