package net.minecraft.client.renderer.special; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Optional; import java.util.function.UnaryOperator; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.model.SkullModelBase; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.SkullBlockRenderer; import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.block.SkullBlock.Type; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class SkullSpecialRenderer implements SpecialModelRenderer { private final Type skullType; private final SkullModelBase model; @Nullable private final ResourceLocation textureOverride; private final float animation; public SkullSpecialRenderer(Type skullType, SkullModelBase model, @Nullable ResourceLocation textureOverride, float animation) { this.skullType = skullType; this.model = model; this.textureOverride = textureOverride; this.animation = animation; } @Nullable public ResolvableProfile extractArgument(ItemStack itemStack) { return itemStack.get(DataComponents.PROFILE); } public void render( @Nullable ResolvableProfile resolvableProfile, ItemDisplayContext itemDisplayContext, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, boolean bl ) { RenderType renderType = SkullBlockRenderer.getRenderType(this.skullType, resolvableProfile, this.textureOverride); SkullBlockRenderer.renderSkull(null, 180.0F, this.animation, poseStack, multiBufferSource, i, this.model, renderType); } @Environment(EnvType.CLIENT) public record Unbaked(Type kind, Optional textureOverride, float animation) implements SpecialModelRenderer.Unbaked { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( Type.CODEC.fieldOf("kind").forGetter(SkullSpecialRenderer.Unbaked::kind), ResourceLocation.CODEC.optionalFieldOf("texture").forGetter(SkullSpecialRenderer.Unbaked::textureOverride), Codec.FLOAT.optionalFieldOf("animation", 0.0F).forGetter(SkullSpecialRenderer.Unbaked::animation) ) .apply(instance, SkullSpecialRenderer.Unbaked::new) ); public Unbaked(Type type) { this(type, Optional.empty(), 0.0F); } @Override public MapCodec type() { return MAP_CODEC; } @Nullable @Override public SpecialModelRenderer bake(EntityModelSet modelSet) { SkullModelBase skullModelBase = SkullBlockRenderer.createModel(modelSet, this.kind); ResourceLocation resourceLocation = (ResourceLocation)this.textureOverride .map(resourceLocationx -> resourceLocationx.withPath((UnaryOperator)(string -> "textures/entity/" + string + ".png"))) .orElse(null); return skullModelBase != null ? new SkullSpecialRenderer(this.kind, skullModelBase, resourceLocation, this.animation) : null; } } }