package net.minecraft.client.renderer.blockentity; import com.google.common.collect.Maps; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import java.util.Map; import java.util.function.Function; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.model.PiglinHeadModel; import net.minecraft.client.model.SkullModel; import net.minecraft.client.model.SkullModelBase; import net.minecraft.client.model.dragon.DragonHeadModel; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.block.AbstractSkullBlock; import net.minecraft.world.level.block.SkullBlock; import net.minecraft.world.level.block.WallSkullBlock; import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.RotationSegment; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class SkullBlockRenderer implements BlockEntityRenderer { private final Function modelByType; private static final Map SKIN_BY_TYPE = Util.make(Maps.newHashMap(), hashMap -> { hashMap.put(SkullBlock.Types.SKELETON, ResourceLocation.withDefaultNamespace("textures/entity/skeleton/skeleton.png")); hashMap.put(SkullBlock.Types.WITHER_SKELETON, ResourceLocation.withDefaultNamespace("textures/entity/skeleton/wither_skeleton.png")); hashMap.put(SkullBlock.Types.ZOMBIE, ResourceLocation.withDefaultNamespace("textures/entity/zombie/zombie.png")); hashMap.put(SkullBlock.Types.CREEPER, ResourceLocation.withDefaultNamespace("textures/entity/creeper/creeper.png")); hashMap.put(SkullBlock.Types.DRAGON, ResourceLocation.withDefaultNamespace("textures/entity/enderdragon/dragon.png")); hashMap.put(SkullBlock.Types.PIGLIN, ResourceLocation.withDefaultNamespace("textures/entity/piglin/piglin.png")); hashMap.put(SkullBlock.Types.PLAYER, DefaultPlayerSkin.getDefaultTexture()); }); @Nullable public static SkullModelBase createModel(EntityModelSet modelSet, SkullBlock.Type type) { if (type instanceof SkullBlock.Types types) { return (SkullModelBase)(switch (types) { case SKELETON -> new SkullModel(modelSet.bakeLayer(ModelLayers.SKELETON_SKULL)); case WITHER_SKELETON -> new SkullModel(modelSet.bakeLayer(ModelLayers.WITHER_SKELETON_SKULL)); case PLAYER -> new SkullModel(modelSet.bakeLayer(ModelLayers.PLAYER_HEAD)); case ZOMBIE -> new SkullModel(modelSet.bakeLayer(ModelLayers.ZOMBIE_HEAD)); case CREEPER -> new SkullModel(modelSet.bakeLayer(ModelLayers.CREEPER_HEAD)); case DRAGON -> new DragonHeadModel(modelSet.bakeLayer(ModelLayers.DRAGON_SKULL)); case PIGLIN -> new PiglinHeadModel(modelSet.bakeLayer(ModelLayers.PIGLIN_HEAD)); }); } else { return null; } } public SkullBlockRenderer(Context context) { EntityModelSet entityModelSet = context.getModelSet(); this.modelByType = Util.memoize((Function)(type -> createModel(entityModelSet, type))); } public void render(SkullBlockEntity skullBlockEntity, float f, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, Vec3 vec3) { float g = skullBlockEntity.getAnimation(f); BlockState blockState = skullBlockEntity.getBlockState(); boolean bl = blockState.getBlock() instanceof WallSkullBlock; Direction direction = bl ? blockState.getValue(WallSkullBlock.FACING) : null; int k = bl ? RotationSegment.convertToSegment(direction.getOpposite()) : (Integer)blockState.getValue(SkullBlock.ROTATION); float h = RotationSegment.convertToDegrees(k); SkullBlock.Type type = ((AbstractSkullBlock)blockState.getBlock()).getType(); SkullModelBase skullModelBase = (SkullModelBase)this.modelByType.apply(type); RenderType renderType = getRenderType(type, skullBlockEntity.getOwnerProfile()); renderSkull(direction, h, g, poseStack, multiBufferSource, i, skullModelBase, renderType); } public static void renderSkull( @Nullable Direction direction, float yRot, float mouthAnimation, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, SkullModelBase model, RenderType renderType ) { poseStack.pushPose(); if (direction == null) { poseStack.translate(0.5F, 0.0F, 0.5F); } else { float f = 0.25F; poseStack.translate(0.5F - direction.getStepX() * 0.25F, 0.25F, 0.5F - direction.getStepZ() * 0.25F); } poseStack.scale(-1.0F, -1.0F, 1.0F); VertexConsumer vertexConsumer = bufferSource.getBuffer(renderType); model.setupAnim(mouthAnimation, yRot, 0.0F); model.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY); poseStack.popPose(); } public static RenderType getRenderType(SkullBlock.Type type, @Nullable ResolvableProfile profile) { return getRenderType(type, profile, null); } public static RenderType getRenderType(SkullBlock.Type type, @Nullable ResolvableProfile profile, @Nullable ResourceLocation textureOverride) { return type == SkullBlock.Types.PLAYER && profile != null ? RenderType.entityTranslucent( textureOverride != null ? textureOverride : Minecraft.getInstance().getSkinManager().getInsecureSkin(profile.gameProfile()).texture() ) : RenderType.entityCutoutNoCullZOffset(textureOverride != null ? textureOverride : (ResourceLocation)SKIN_BY_TYPE.get(type)); } }