diff --git a/src/main/java/com/atsuishio/superbwarfare/client/ModRenderTypes.java b/src/main/java/com/atsuishio/superbwarfare/client/ModRenderTypes.java new file mode 100644 index 000000000..aea0260c5 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/ModRenderTypes.java @@ -0,0 +1,25 @@ +package com.atsuishio.superbwarfare.client; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.Util; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; + +import java.util.function.Function; + +public class ModRenderTypes extends RenderType { + + public ModRenderTypes(String pName, VertexFormat pFormat, VertexFormat.Mode pMode, int pBufferSize, boolean pAffectsCrumbling, boolean pSortOnUpload, Runnable pSetupState, Runnable pClearState) { + super(pName, pFormat, pMode, pBufferSize, pAffectsCrumbling, pSortOnUpload, pSetupState, pClearState); + } + + public static final Function LASER = Util.memoize((location) -> { + TextureStateShard shard = new RenderStateShard.TextureStateShard(location, false, false); + RenderType.CompositeState state = RenderType.CompositeState.builder().setTextureState(shard) + .setShaderState(RENDERTYPE_ENTITY_TRANSLUCENT_EMISSIVE_SHADER).setTransparencyState(ADDITIVE_TRANSPARENCY) + .setCullState(NO_CULL).setOverlayState(OVERLAY).setWriteMaskState(COLOR_WRITE).createCompositeState(false); + return RenderType.create("laser", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, false, state); + }); +} diff --git a/src/main/java/com/atsuishio/superbwarfare/client/RenderHelper.java b/src/main/java/com/atsuishio/superbwarfare/client/RenderHelper.java index 5aa47148f..08eb4d12b 100644 --- a/src/main/java/com/atsuishio/superbwarfare/client/RenderHelper.java +++ b/src/main/java/com/atsuishio/superbwarfare/client/RenderHelper.java @@ -8,6 +8,7 @@ import net.minecraft.resources.ResourceLocation; import org.joml.Matrix4f; public class RenderHelper { + public static void preciseBlit(GuiGraphics gui, ResourceLocation pAtlasLocation, float pX, float pY, float pUOffset, float pVOffset, float pWidth, float pHeight, float pTextureWidth, float pTextureHeight) { float pX2 = pX + pWidth; float pY2 = pY + pHeight; diff --git a/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/AbstractLaserEntityRenderer.java b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/AbstractLaserEntityRenderer.java new file mode 100644 index 000000000..01522a342 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/AbstractLaserEntityRenderer.java @@ -0,0 +1,162 @@ +package com.atsuishio.superbwarfare.client.renderer.entity; + +import com.atsuishio.superbwarfare.client.ModRenderTypes; +import com.atsuishio.superbwarfare.entity.projectile.AbstractLaserEntity; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Axis; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Quaternionf; + +/** + * Code based on @BobMowzie's MowziesMobs and @EEEAB's EEEABsMobs + */ +@OnlyIn(Dist.CLIENT) +public abstract class AbstractLaserEntityRenderer extends EntityRenderer { + + private static final float TEXTURE_WIDTH = 256; + private static final float TEXTURE_HEIGHT = 32; + private final float quadRadius; + private final float beamRadius; + + public AbstractLaserEntityRenderer(EntityRendererProvider.Context context, float quadRadius, float beamRadius) { + super(context); + this.quadRadius = quadRadius; + this.beamRadius = beamRadius; + } + + @Override + public abstract ResourceLocation getTextureLocation(T entity); + + @Override + public void render(T beam, float entityYaw, float delta, PoseStack matrixStackIn, MultiBufferSource bufferIn, int packedLightIn) { + double collidePosX = beam.prevCollidePosX + (beam.collidePosX - beam.prevCollidePosX) * delta; + double collidePosY = beam.prevCollidePosY + (beam.collidePosY - beam.prevCollidePosY) * delta; + double collidePosZ = beam.prevCollidePosZ + (beam.collidePosZ - beam.prevCollidePosZ) * delta; + double posX = beam.xo + (beam.getX() - beam.xo) * delta; + double posY = beam.yo + (beam.getY() - beam.yo) * delta; + double posZ = beam.zo + (beam.getZ() - beam.zo) * delta; + float yaw = beam.preYaw + (beam.yaw - beam.preYaw) * delta; + float pitch = beam.prePitch + (beam.pitch - beam.prePitch) * delta; + + float length = (float) Math.sqrt(Math.pow(collidePosX - posX, 2) + Math.pow(collidePosY - posY, 2) + Math.pow(collidePosZ - posZ, 2)); + int frame = Mth.floor((beam.ticker.getTick() - 1 + delta) * 2); + if (frame < 0) { + frame = 6; + } + + if (!beam.isAccumulating()) return; + VertexConsumer vertex$builder = bufferIn.getBuffer(ModRenderTypes.LASER.apply(getTextureLocation(beam))); + renderStart(beam, frame, matrixStackIn, vertex$builder, delta, packedLightIn); + renderBeam(length, 180f / (float) Math.PI * yaw, 180f / (float) Math.PI * pitch, frame, matrixStackIn, vertex$builder, packedLightIn); + matrixStackIn.pushPose(); + matrixStackIn.translate(collidePosX - posX, collidePosY - posY, collidePosZ - posZ); + renderEnd(beam, frame, beam.blockSide, matrixStackIn, vertex$builder, delta, packedLightIn); + matrixStackIn.popPose(); + } + + protected void renderFlatQuad(int frame, PoseStack matrixStackIn, VertexConsumer builder, int packedLightIn, boolean inGround) { + float minU = 0 + 16F / TEXTURE_WIDTH * frame; + float minV = 0; + float maxU = minU + 16F / TEXTURE_WIDTH; + float maxV = minV + 16F / TEXTURE_HEIGHT; + float SIZE = this.quadRadius + (inGround ? 0.2F : 0); + PoseStack.Pose matrix$stack$entry = matrixStackIn.last(); + Matrix4f matrix4f = matrix$stack$entry.pose(); + Matrix3f matrix3f = matrix$stack$entry.normal(); + drawVertex(matrix4f, matrix3f, builder, -SIZE, -SIZE, 0, minU, minV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, -SIZE, SIZE, 0, minU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, SIZE, SIZE, 0, maxU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, SIZE, -SIZE, 0, maxU, minV, packedLightIn); + } + + protected void renderStart(T entity, int frame, PoseStack matrixStackIn, VertexConsumer builder, float delta, int packedLightIn) { + matrixStackIn.pushPose(); + Quaternionf quaternionf = this.entityRenderDispatcher.cameraOrientation(); + matrixStackIn.mulPose(quaternionf); + renderFlatQuad(frame, matrixStackIn, builder, packedLightIn, false); + matrixStackIn.popPose(); + } + + protected void renderEnd(T entity, int frame, Direction side, PoseStack matrixStackIn, VertexConsumer builder, float delta, int packedLightIn) { + matrixStackIn.pushPose(); + Quaternionf quaternionf = this.entityRenderDispatcher.cameraOrientation(); + matrixStackIn.mulPose(quaternionf); + renderFlatQuad(frame, matrixStackIn, builder, packedLightIn, false); + matrixStackIn.popPose(); + + if (side == null) { + return; + } + + matrixStackIn.pushPose(); + Quaternionf rotation = side.getRotation(); + rotation.mul(Axis.XP.rotationDegrees(90F)); + matrixStackIn.mulPose(rotation); + matrixStackIn.translate(0, 0, -0.01f); + renderFlatQuad(frame, matrixStackIn, builder, packedLightIn, true); + matrixStackIn.popPose(); + } + + protected void renderBeam(float length, float yaw, float pitch, int frame, PoseStack matrixStackIn, VertexConsumer builder, int packedLightIn) { + matrixStackIn.pushPose(); + + matrixStackIn.mulPose(Axis.XP.rotationDegrees(90F)); + matrixStackIn.mulPose(Axis.ZP.rotationDegrees(yaw - 90F)); + matrixStackIn.mulPose(Axis.XP.rotationDegrees(-pitch)); + + matrixStackIn.pushPose(); + matrixStackIn.mulPose(Axis.YP.rotationDegrees(Minecraft.getInstance().gameRenderer.getMainCamera().getXRot() + 90F)); + drawBeam(length, frame, matrixStackIn, builder, packedLightIn); + matrixStackIn.popPose(); + + matrixStackIn.pushPose(); + matrixStackIn.mulPose(Axis.YP.rotationDegrees(-Minecraft.getInstance().gameRenderer.getMainCamera().getXRot() - 90F)); + drawBeam(length, frame, matrixStackIn, builder, packedLightIn); + matrixStackIn.popPose(); + + matrixStackIn.pushPose(); + matrixStackIn.mulPose(Axis.YP.rotationDegrees(-Minecraft.getInstance().gameRenderer.getMainCamera().getXRot() + 180F)); + drawBeam(length, frame, matrixStackIn, builder, packedLightIn); + matrixStackIn.popPose(); + + matrixStackIn.pushPose(); + matrixStackIn.mulPose(Axis.YP.rotationDegrees(-Minecraft.getInstance().gameRenderer.getMainCamera().getXRot() - 180F)); + drawBeam(length, frame, matrixStackIn, builder, packedLightIn); + matrixStackIn.popPose(); + + matrixStackIn.popPose(); + } + + protected void drawBeam(float length, int frame, PoseStack matrixStackIn, VertexConsumer builder, int packedLightIn) { + float minU = 0; + float minV = 16 / TEXTURE_HEIGHT + 1 / TEXTURE_HEIGHT * frame; + float maxU = minU + 20 / TEXTURE_WIDTH; + float maxV = minV + 1 / TEXTURE_HEIGHT; + PoseStack.Pose matrix$stack$entry = matrixStackIn.last(); + Matrix4f matrix4f = matrix$stack$entry.pose(); + Matrix3f matrix3f = matrix$stack$entry.normal(); + float offset = 0; + float size = this.beamRadius; + drawVertex(matrix4f, matrix3f, builder, -size, offset, 0, minU, minV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, -size, length, 0, minU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, length, 0, maxU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, offset, 0, maxU, minV, packedLightIn); + } + + protected void drawVertex(Matrix4f matrix, Matrix3f normals, VertexConsumer vertexBuilder, float offsetX, float offsetY, float offsetZ, float textureX, float textureY, int packedLightIn) { + vertexBuilder.vertex(matrix, offsetX, offsetY, offsetZ).color(1F, 1F, 1F, 1F).uv(textureX, textureY).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(packedLightIn).normal(normals, 0.0F, 1.0F, 0.0F).endVertex(); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/LaserEntityRenderer.java b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/LaserEntityRenderer.java new file mode 100644 index 000000000..3e56e4131 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/client/renderer/entity/LaserEntityRenderer.java @@ -0,0 +1,88 @@ +package com.atsuishio.superbwarfare.client.renderer.entity; + +import com.atsuishio.superbwarfare.ModUtils; +import com.atsuishio.superbwarfare.entity.projectile.LaserEntity; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.CameraType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +/** + * Code based on @BobMowzie's MowziesMobs and @EEEAB's EEEABsMobs + */ +@OnlyIn(Dist.CLIENT) +public class LaserEntityRenderer extends AbstractLaserEntityRenderer { + + private boolean playerView; + + private static final float TEXTURE_WIDTH = 256; + private static final float TEXTURE_HEIGHT = 32; + + public static final ResourceLocation TEXTURE = ModUtils.loc("textures/entity/laser.png"); + + public LaserEntityRenderer(EntityRendererProvider.Context context) { + super(context, 0.8f, 0.6f); + } + + @Override + public ResourceLocation getTextureLocation(LaserEntity entity) { + return TEXTURE; + } + + @Override + public void render(LaserEntity beam, float entityYaw, float delta, PoseStack matrixStackIn, MultiBufferSource bufferIn, int packedLightIn) { + this.playerView = beam.caster instanceof Player && Minecraft.getInstance().player == beam.caster + && Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON; + if (this.playerView) { + return; + } + super.render(beam, entityYaw, delta, matrixStackIn, bufferIn, packedLightIn); + } + + @Override + protected void renderFlatQuad(int frame, PoseStack matrixStackIn, VertexConsumer builder, int packedLightIn, boolean inGround) { + float minU = 0 + 16F / TEXTURE_WIDTH * frame; + float minV = 0; + float maxU = minU + 16F / TEXTURE_WIDTH; + float maxV = minV + 16F / TEXTURE_HEIGHT; + float size = 0.8f; + PoseStack.Pose matrix$stack$entry = matrixStackIn.last(); + Matrix4f matrix4f = matrix$stack$entry.pose(); + Matrix3f matrix3f = matrix$stack$entry.normal(); + drawVertex(matrix4f, matrix3f, builder, -size, -size, 0, minU, minV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, -size, size, 0, minU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, size, 0, maxU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, -size, 0, maxU, minV, packedLightIn); + } + + @Override + protected void renderStart(LaserEntity laser, int frame, PoseStack matrixStackIn, VertexConsumer builder, float delta, int packedLightIn) { + if (this.playerView) return; + super.renderStart(laser, frame, matrixStackIn, builder, delta, packedLightIn); + } + + @Override + protected void drawBeam(float length, int frame, PoseStack matrixStackIn, VertexConsumer builder, int packedLightIn) { + float minU = 0; + float minV = 16 / TEXTURE_HEIGHT + 1 / TEXTURE_HEIGHT * frame; + float maxU = minU + 20 / TEXTURE_WIDTH; + float maxV = minV + 1 / TEXTURE_HEIGHT; + PoseStack.Pose matrix$stack$entry = matrixStackIn.last(); + Matrix4f matrix4f = matrix$stack$entry.pose(); + Matrix3f matrix3f = matrix$stack$entry.normal(); + float offset = playerView ? -1 : 0; + float size = 0.6f; + drawVertex(matrix4f, matrix3f, builder, -size, offset, 0, minU, minV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, -size, length, 0, minU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, length, 0, maxU, maxV, packedLightIn); + drawVertex(matrix4f, matrix3f, builder, size, offset, 0, maxU, minV, packedLightIn); + } +} diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java index 053e2ae9f..fdb3c7fdd 100644 --- a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/AbstractLaserEntity.java @@ -49,7 +49,7 @@ public abstract class AbstractLaserEntity extends Entity implements TraceableEnt public AbstractLaserEntity(EntityType type, Level level, int countDown) { super(type, level); this.setCountDown(countDown); - noCulling = true; + this.noCulling = true; } @Override diff --git a/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java new file mode 100644 index 000000000..7f5edf445 --- /dev/null +++ b/src/main/java/com/atsuishio/superbwarfare/entity/projectile/LaserEntity.java @@ -0,0 +1,95 @@ +package com.atsuishio.superbwarfare.entity.projectile; + +import com.atsuishio.superbwarfare.init.ModEntities; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; + +/** + * Code based on @BobMowzie's MowziesMobs, @EEEAB's EEEABsMobs and @Mercurows's DreamaticVoyage + */ +public class LaserEntity extends AbstractLaserEntity { + + public static final double RADIUS = 512D; + + public LaserEntity(EntityType type, Level level) { + super(type, level, 0); + } + + public LaserEntity(Level level, LivingEntity caster, double x, double y, double z, float yaw, float pitch, int duration) { + super(ModEntities.LASER.get(), level, 0); + this.caster = caster; + this.setYaw(yaw); + this.setPitch(pitch); + this.setDuration(duration); + this.setPos(x, y, z); + this.calculateEndPos(RADIUS); + if (!level().isClientSide) { + setCasterId(caster.getId()); + } + } + + @Override + public void beamTick() { + if (!this.level().isClientSide) { + if (this.caster instanceof Player) { + this.updateWithPlayer(); + } else if (this.caster != null) { + this.updateWithEntity(0F, 0.75F); + } + } + + if (caster != null) { + this.yaw = (float) Math.toRadians(caster.yHeadRot + 90); + this.pitch = (float) -Math.toRadians(caster.getXRot()); + } + + if (this.tickCount >= this.getCountDown()) { + this.calculateEndPos(RADIUS); + if (this.blockSide != null) { + this.spawnExplosionParticles(); + } + } + } + + public void spawnExplosionParticles() { + } + + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + } + + @Override + protected void readAdditionalSaveData(CompoundTag compoundTag) { + if (this.caster == null) { + discard(); + } + } + + @Override + public boolean shouldRenderAtSqrDistance(double distance) { + double radius = RADIUS; + return distance < (radius * radius) * 2; + } + + private void updateWithPlayer() { + this.setYaw((float) Math.toRadians(caster.yHeadRot + 90)); + this.setPitch((float) Math.toRadians(-caster.getXRot())); + Vec3 vecOffset = caster.getLookAngle().normalize().scale(1.25); + this.setPos(caster.getX() + vecOffset.x(), caster.getY() + caster.getBbHeight() * 0.5F + vecOffset.y(), caster.getZ() + vecOffset.z()); + } + + private void updateWithEntity(float offset, float yOffset) { + double radians = Math.toRadians(this.caster.yHeadRot + 90); + this.setYaw((float) radians); + this.setPitch((float) ((double) (-this.caster.getXRot()) * Math.PI / 180.0)); + double offsetX = Math.cos(radians) * offset; + double offsetZ = Math.sin(radians) * offset; + this.setPos(this.caster.getX() + offsetX, this.caster.getY(yOffset), this.caster.getZ() + offsetZ); + } + +} diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java index d9bf20a5a..544266331 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModEntities.java @@ -57,7 +57,8 @@ public class ModEntities { EntityType.Builder.of(RgoGrenadeEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(512).setUpdateInterval(1).setCustomClientFactory(RgoGrenadeEntity::new).sized(0.3f, 0.3f)); public static final RegistryObject> JAVELIN_MISSILE = register("projectile_javelin_missile", EntityType.Builder.of(JavelinMissileEntity::new, MobCategory.MISC).setShouldReceiveVelocityUpdates(true).setTrackingRange(512).setUpdateInterval(1).setCustomClientFactory(JavelinMissileEntity::new).sized(0.5f, 0.5f)); - + public static final RegistryObject> LASER = register("laser", + EntityType.Builder.of(LaserEntity::new, MobCategory.MISC).sized(0.1f, 0.1f).fireImmune().setUpdateInterval(1)); private static RegistryObject> register(String name, EntityType.Builder entityTypeBuilder) { return REGISTRY.register(name, () -> entityTypeBuilder.build(name)); diff --git a/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java b/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java index 45fb82e23..ce7991df4 100644 --- a/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java +++ b/src/main/java/com/atsuishio/superbwarfare/init/ModEntityRenderers.java @@ -27,5 +27,6 @@ public class ModEntityRenderers { event.registerEntityRenderer(ModEntities.RGO_GRENADE.get(), RgoGrenadeRenderer::new); event.registerEntityRenderer(ModEntities.MLE_1934.get(), Mle1934Renderer::new); event.registerEntityRenderer(ModEntities.JAVELIN_MISSILE.get(), JavelinMissileRenderer::new); + event.registerEntityRenderer(ModEntities.LASER.get(), LaserEntityRenderer::new); } }