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",