package net.minecraft.client.renderer.entity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import java.util.Objects; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.model.MinecartModel; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.entity.EntityRendererProvider.Context; import net.minecraft.client.renderer.entity.state.MinecartRenderState; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.vehicle.AbstractMinecart; import net.minecraft.world.entity.vehicle.NewMinecartBehavior; import net.minecraft.world.entity.vehicle.OldMinecartBehavior; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @Environment(EnvType.CLIENT) public abstract class AbstractMinecartRenderer extends EntityRenderer { private static final ResourceLocation MINECART_LOCATION = ResourceLocation.withDefaultNamespace("textures/entity/minecart.png"); private static final float DISPLAY_BLOCK_SCALE = 0.75F; protected final MinecartModel model; private final BlockRenderDispatcher blockRenderer; public AbstractMinecartRenderer(Context context, ModelLayerLocation modelLayer) { super(context); this.shadowRadius = 0.7F; this.model = new MinecartModel(context.bakeLayer(modelLayer)); this.blockRenderer = context.getBlockRenderDispatcher(); } public void render(S minecartRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { super.render(minecartRenderState, poseStack, multiBufferSource, i); poseStack.pushPose(); long l = minecartRenderState.offsetSeed; float f = (((float)(l >> 16 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F; float g = (((float)(l >> 20 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F; float h = (((float)(l >> 24 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F; poseStack.translate(f, g, h); if (minecartRenderState.isNewRender) { newRender(minecartRenderState, poseStack); } else { oldRender(minecartRenderState, poseStack); } float j = minecartRenderState.hurtTime; if (j > 0.0F) { poseStack.mulPose(Axis.XP.rotationDegrees(Mth.sin(j) * j * minecartRenderState.damageTime / 10.0F * minecartRenderState.hurtDir)); } BlockState blockState = minecartRenderState.displayBlockState; if (blockState.getRenderShape() != RenderShape.INVISIBLE) { poseStack.pushPose(); poseStack.scale(0.75F, 0.75F, 0.75F); poseStack.translate(-0.5F, (minecartRenderState.displayOffset - 8) / 16.0F, 0.5F); poseStack.mulPose(Axis.YP.rotationDegrees(90.0F)); this.renderMinecartContents(minecartRenderState, blockState, poseStack, multiBufferSource, i); poseStack.popPose(); } poseStack.scale(-1.0F, -1.0F, 1.0F); this.model.setupAnim(minecartRenderState); VertexConsumer vertexConsumer = multiBufferSource.getBuffer(this.model.renderType(MINECART_LOCATION)); this.model.renderToBuffer(poseStack, vertexConsumer, i, OverlayTexture.NO_OVERLAY); poseStack.popPose(); } private static void newRender(S renderState, PoseStack poseStack) { poseStack.mulPose(Axis.YP.rotationDegrees(renderState.yRot)); poseStack.mulPose(Axis.ZP.rotationDegrees(-renderState.xRot)); poseStack.translate(0.0F, 0.375F, 0.0F); } private static void oldRender(S renderState, PoseStack poseStack) { double d = renderState.x; double e = renderState.y; double f = renderState.z; float g = renderState.xRot; float h = renderState.yRot; if (renderState.posOnRail != null && renderState.frontPos != null && renderState.backPos != null) { Vec3 vec3 = renderState.frontPos; Vec3 vec32 = renderState.backPos; poseStack.translate(renderState.posOnRail.x - d, (vec3.y + vec32.y) / 2.0 - e, renderState.posOnRail.z - f); Vec3 vec33 = vec32.add(-vec3.x, -vec3.y, -vec3.z); if (vec33.length() != 0.0) { vec33 = vec33.normalize(); h = (float)(Math.atan2(vec33.z, vec33.x) * 180.0 / Math.PI); g = (float)(Math.atan(vec33.y) * 73.0); } } poseStack.translate(0.0F, 0.375F, 0.0F); poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - h)); poseStack.mulPose(Axis.ZP.rotationDegrees(-g)); } public void extractRenderState(T abstractMinecart, S minecartRenderState, float f) { super.extractRenderState(abstractMinecart, minecartRenderState, f); if (abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior) { newExtractState(abstractMinecart, newMinecartBehavior, minecartRenderState, f); minecartRenderState.isNewRender = true; } else if (abstractMinecart.getBehavior() instanceof OldMinecartBehavior oldMinecartBehavior) { oldExtractState(abstractMinecart, oldMinecartBehavior, minecartRenderState, f); minecartRenderState.isNewRender = false; } long l = abstractMinecart.getId() * 493286711L; minecartRenderState.offsetSeed = l * l * 4392167121L + l * 98761L; minecartRenderState.hurtTime = abstractMinecart.getHurtTime() - f; minecartRenderState.hurtDir = abstractMinecart.getHurtDir(); minecartRenderState.damageTime = Math.max(abstractMinecart.getDamage() - f, 0.0F); minecartRenderState.displayOffset = abstractMinecart.getDisplayOffset(); minecartRenderState.displayBlockState = abstractMinecart.getDisplayBlockState(); } private static void newExtractState( T minecart, NewMinecartBehavior behavior, S renderState, float partialTick ) { if (behavior.cartHasPosRotLerp()) { renderState.renderPos = behavior.getCartLerpPosition(partialTick); renderState.xRot = behavior.getCartLerpXRot(partialTick); renderState.yRot = behavior.getCartLerpYRot(partialTick); } else { renderState.renderPos = null; renderState.xRot = minecart.getXRot(); renderState.yRot = minecart.getYRot(); } } private static void oldExtractState( T minecart, OldMinecartBehavior behavior, S renderState, float partialTick ) { float f = 0.3F; renderState.xRot = minecart.getXRot(partialTick); renderState.yRot = minecart.getYRot(partialTick); double d = renderState.x; double e = renderState.y; double g = renderState.z; Vec3 vec3 = behavior.getPos(d, e, g); if (vec3 != null) { renderState.posOnRail = vec3; Vec3 vec32 = behavior.getPosOffs(d, e, g, 0.3F); Vec3 vec33 = behavior.getPosOffs(d, e, g, -0.3F); renderState.frontPos = (Vec3)Objects.requireNonNullElse(vec32, vec3); renderState.backPos = (Vec3)Objects.requireNonNullElse(vec33, vec3); } else { renderState.posOnRail = null; renderState.frontPos = null; renderState.backPos = null; } } protected void renderMinecartContents(S renderState, BlockState state, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { this.blockRenderer.renderSingleBlock(state, poseStack, bufferSource, packedLight, OverlayTexture.NO_OVERLAY); } protected AABB getBoundingBoxForCulling(T abstractMinecart) { AABB aABB = super.getBoundingBoxForCulling(abstractMinecart); return !abstractMinecart.getDisplayBlockState().isAir() ? aABB.expandTowards(0.0, abstractMinecart.getDisplayOffset() * 0.75F / 16.0F, 0.0) : aABB; } public Vec3 getRenderOffset(S minecartRenderState) { Vec3 vec3 = super.getRenderOffset(minecartRenderState); return minecartRenderState.isNewRender && minecartRenderState.renderPos != null ? vec3.add( minecartRenderState.renderPos.x - minecartRenderState.x, minecartRenderState.renderPos.y - minecartRenderState.y, minecartRenderState.renderPos.z - minecartRenderState.z ) : vec3; } }