package net.minecraft.client.renderer.entity; import com.google.common.collect.ImmutableList.Builder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.model.dragon.EnderDragonModel; import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.entity.EntityRendererProvider.Context; import net.minecraft.client.renderer.entity.state.EnderDragonRenderState; import net.minecraft.client.renderer.entity.state.HitboxRenderState; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.boss.enderdragon.EndCrystal; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.boss.enderdragon.phases.DragonPhaseInstance; import net.minecraft.world.entity.boss.enderdragon.phases.EnderDragonPhase; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.feature.EndPodiumFeature; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Quaternionf; import org.joml.Vector3f; @Environment(EnvType.CLIENT) public class EnderDragonRenderer extends EntityRenderer { public static final ResourceLocation CRYSTAL_BEAM_LOCATION = ResourceLocation.withDefaultNamespace("textures/entity/end_crystal/end_crystal_beam.png"); private static final ResourceLocation DRAGON_EXPLODING_LOCATION = ResourceLocation.withDefaultNamespace("textures/entity/enderdragon/dragon_exploding.png"); private static final ResourceLocation DRAGON_LOCATION = ResourceLocation.withDefaultNamespace("textures/entity/enderdragon/dragon.png"); private static final ResourceLocation DRAGON_EYES_LOCATION = ResourceLocation.withDefaultNamespace("textures/entity/enderdragon/dragon_eyes.png"); private static final RenderType RENDER_TYPE = RenderType.entityCutoutNoCull(DRAGON_LOCATION); private static final RenderType DECAL = RenderType.entityDecal(DRAGON_LOCATION); private static final RenderType EYES = RenderType.eyes(DRAGON_EYES_LOCATION); private static final RenderType BEAM = RenderType.entitySmoothCutout(CRYSTAL_BEAM_LOCATION); private static final float HALF_SQRT_3 = (float)(Math.sqrt(3.0) / 2.0); private final EnderDragonModel model; public EnderDragonRenderer(Context context) { super(context); this.shadowRadius = 0.5F; this.model = new EnderDragonModel(context.bakeLayer(ModelLayers.ENDER_DRAGON)); } public void render(EnderDragonRenderState enderDragonRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { poseStack.pushPose(); float f = enderDragonRenderState.getHistoricalPos(7).yRot(); float g = (float)(enderDragonRenderState.getHistoricalPos(5).y() - enderDragonRenderState.getHistoricalPos(10).y()); poseStack.mulPose(Axis.YP.rotationDegrees(-f)); poseStack.mulPose(Axis.XP.rotationDegrees(g * 10.0F)); poseStack.translate(0.0F, 0.0F, 1.0F); poseStack.scale(-1.0F, -1.0F, 1.0F); poseStack.translate(0.0F, -1.501F, 0.0F); this.model.setupAnim(enderDragonRenderState); if (enderDragonRenderState.deathTime > 0.0F) { float h = enderDragonRenderState.deathTime / 200.0F; int j = ARGB.color(Mth.floor(h * 255.0F), -1); VertexConsumer vertexConsumer = multiBufferSource.getBuffer(RenderType.dragonExplosionAlpha(DRAGON_EXPLODING_LOCATION)); this.model.renderToBuffer(poseStack, vertexConsumer, i, OverlayTexture.NO_OVERLAY, j); VertexConsumer vertexConsumer2 = multiBufferSource.getBuffer(DECAL); this.model.renderToBuffer(poseStack, vertexConsumer2, i, OverlayTexture.pack(0.0F, enderDragonRenderState.hasRedOverlay)); } else { VertexConsumer vertexConsumer3 = multiBufferSource.getBuffer(RENDER_TYPE); this.model.renderToBuffer(poseStack, vertexConsumer3, i, OverlayTexture.pack(0.0F, enderDragonRenderState.hasRedOverlay)); } VertexConsumer vertexConsumer3 = multiBufferSource.getBuffer(EYES); this.model.renderToBuffer(poseStack, vertexConsumer3, i, OverlayTexture.NO_OVERLAY); if (enderDragonRenderState.deathTime > 0.0F) { float k = enderDragonRenderState.deathTime / 200.0F; poseStack.pushPose(); poseStack.translate(0.0F, -1.0F, -2.0F); renderRays(poseStack, k, multiBufferSource.getBuffer(RenderType.dragonRays())); renderRays(poseStack, k, multiBufferSource.getBuffer(RenderType.dragonRaysDepth())); poseStack.popPose(); } poseStack.popPose(); if (enderDragonRenderState.beamOffset != null) { renderCrystalBeams( (float)enderDragonRenderState.beamOffset.x, (float)enderDragonRenderState.beamOffset.y, (float)enderDragonRenderState.beamOffset.z, enderDragonRenderState.ageInTicks, poseStack, multiBufferSource, i ); } super.render(enderDragonRenderState, poseStack, multiBufferSource, i); } private static void renderRays(PoseStack poseStack, float dragonDeathCompletion, VertexConsumer buffer) { poseStack.pushPose(); float f = Math.min(dragonDeathCompletion > 0.8F ? (dragonDeathCompletion - 0.8F) / 0.2F : 0.0F, 1.0F); int i = ARGB.colorFromFloat(1.0F - f, 1.0F, 1.0F, 1.0F); int j = 16711935; RandomSource randomSource = RandomSource.create(432L); Vector3f vector3f = new Vector3f(); Vector3f vector3f2 = new Vector3f(); Vector3f vector3f3 = new Vector3f(); Vector3f vector3f4 = new Vector3f(); Quaternionf quaternionf = new Quaternionf(); int k = Mth.floor((dragonDeathCompletion + dragonDeathCompletion * dragonDeathCompletion) / 2.0F * 60.0F); for (int l = 0; l < k; l++) { quaternionf.rotationXYZ( randomSource.nextFloat() * (float) (Math.PI * 2), randomSource.nextFloat() * (float) (Math.PI * 2), randomSource.nextFloat() * (float) (Math.PI * 2) ) .rotateXYZ( randomSource.nextFloat() * (float) (Math.PI * 2), randomSource.nextFloat() * (float) (Math.PI * 2), randomSource.nextFloat() * (float) (Math.PI * 2) + dragonDeathCompletion * (float) (Math.PI / 2) ); poseStack.mulPose(quaternionf); float g = randomSource.nextFloat() * 20.0F + 5.0F + f * 10.0F; float h = randomSource.nextFloat() * 2.0F + 1.0F + f * 2.0F; vector3f2.set(-HALF_SQRT_3 * h, g, -0.5F * h); vector3f3.set(HALF_SQRT_3 * h, g, -0.5F * h); vector3f4.set(0.0F, g, h); PoseStack.Pose pose = poseStack.last(); buffer.addVertex(pose, vector3f).setColor(i); buffer.addVertex(pose, vector3f2).setColor(16711935); buffer.addVertex(pose, vector3f3).setColor(16711935); buffer.addVertex(pose, vector3f).setColor(i); buffer.addVertex(pose, vector3f3).setColor(16711935); buffer.addVertex(pose, vector3f4).setColor(16711935); buffer.addVertex(pose, vector3f).setColor(i); buffer.addVertex(pose, vector3f4).setColor(16711935); buffer.addVertex(pose, vector3f2).setColor(16711935); } poseStack.popPose(); } public static void renderCrystalBeams( float offsetX, float offsetY, float offsetZ, float ageInTicks, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight ) { float f = Mth.sqrt(offsetX * offsetX + offsetZ * offsetZ); float g = Mth.sqrt(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ); poseStack.pushPose(); poseStack.translate(0.0F, 2.0F, 0.0F); poseStack.mulPose(Axis.YP.rotation((float)(-Math.atan2(offsetZ, offsetX)) - (float) (Math.PI / 2))); poseStack.mulPose(Axis.XP.rotation((float)(-Math.atan2(f, offsetY)) - (float) (Math.PI / 2))); VertexConsumer vertexConsumer = bufferSource.getBuffer(BEAM); float h = 0.0F - ageInTicks * 0.01F; float i = g / 32.0F - ageInTicks * 0.01F; int j = 8; float k = 0.0F; float l = 0.75F; float m = 0.0F; PoseStack.Pose pose = poseStack.last(); for (int n = 1; n <= 8; n++) { float o = Mth.sin(n * (float) (Math.PI * 2) / 8.0F) * 0.75F; float p = Mth.cos(n * (float) (Math.PI * 2) / 8.0F) * 0.75F; float q = n / 8.0F; vertexConsumer.addVertex(pose, k * 0.2F, l * 0.2F, 0.0F) .setColor(-16777216) .setUv(m, h) .setOverlay(OverlayTexture.NO_OVERLAY) .setLight(packedLight) .setNormal(pose, 0.0F, -1.0F, 0.0F); vertexConsumer.addVertex(pose, k, l, g) .setColor(-1) .setUv(m, i) .setOverlay(OverlayTexture.NO_OVERLAY) .setLight(packedLight) .setNormal(pose, 0.0F, -1.0F, 0.0F); vertexConsumer.addVertex(pose, o, p, g) .setColor(-1) .setUv(q, i) .setOverlay(OverlayTexture.NO_OVERLAY) .setLight(packedLight) .setNormal(pose, 0.0F, -1.0F, 0.0F); vertexConsumer.addVertex(pose, o * 0.2F, p * 0.2F, 0.0F) .setColor(-16777216) .setUv(q, h) .setOverlay(OverlayTexture.NO_OVERLAY) .setLight(packedLight) .setNormal(pose, 0.0F, -1.0F, 0.0F); k = o; l = p; m = q; } poseStack.popPose(); } public EnderDragonRenderState createRenderState() { return new EnderDragonRenderState(); } public void extractRenderState(EnderDragon enderDragon, EnderDragonRenderState enderDragonRenderState, float f) { super.extractRenderState(enderDragon, enderDragonRenderState, f); enderDragonRenderState.flapTime = Mth.lerp(f, enderDragon.oFlapTime, enderDragon.flapTime); enderDragonRenderState.deathTime = enderDragon.dragonDeathTime > 0 ? enderDragon.dragonDeathTime + f : 0.0F; enderDragonRenderState.hasRedOverlay = enderDragon.hurtTime > 0; EndCrystal endCrystal = enderDragon.nearestCrystal; if (endCrystal != null) { Vec3 vec3 = endCrystal.getPosition(f).add(0.0, EndCrystalRenderer.getY(endCrystal.time + f), 0.0); enderDragonRenderState.beamOffset = vec3.subtract(enderDragon.getPosition(f)); } else { enderDragonRenderState.beamOffset = null; } DragonPhaseInstance dragonPhaseInstance = enderDragon.getPhaseManager().getCurrentPhase(); enderDragonRenderState.isLandingOrTakingOff = dragonPhaseInstance == EnderDragonPhase.LANDING || dragonPhaseInstance == EnderDragonPhase.TAKEOFF; enderDragonRenderState.isSitting = dragonPhaseInstance.isSitting(); BlockPos blockPos = enderDragon.level() .getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(enderDragon.getFightOrigin())); enderDragonRenderState.distanceToEgg = blockPos.distToCenterSqr(enderDragon.position()); enderDragonRenderState.partialTicks = enderDragon.isDeadOrDying() ? 0.0F : f; enderDragonRenderState.flightHistory.copyFrom(enderDragon.flightHistory); } protected void extractAdditionalHitboxes(EnderDragon enderDragon, Builder builder, float f) { super.extractAdditionalHitboxes(enderDragon, builder, f); double d = -Mth.lerp((double)f, enderDragon.xOld, enderDragon.getX()); double e = -Mth.lerp((double)f, enderDragon.yOld, enderDragon.getY()); double g = -Mth.lerp((double)f, enderDragon.zOld, enderDragon.getZ()); for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { AABB aABB = enderDragonPart.getBoundingBox(); HitboxRenderState hitboxRenderState = new HitboxRenderState( aABB.minX - enderDragonPart.getX(), aABB.minY - enderDragonPart.getY(), aABB.minZ - enderDragonPart.getZ(), aABB.maxX - enderDragonPart.getX(), aABB.maxY - enderDragonPart.getY(), aABB.maxZ - enderDragonPart.getZ(), (float)(d + Mth.lerp((double)f, enderDragonPart.xOld, enderDragonPart.getX())), (float)(e + Mth.lerp((double)f, enderDragonPart.yOld, enderDragonPart.getY())), (float)(g + Mth.lerp((double)f, enderDragonPart.zOld, enderDragonPart.getZ())), 0.25F, 1.0F, 0.0F ); builder.add(hitboxRenderState); } } protected boolean affectedByCulling(EnderDragon enderDragon) { return false; } }