minecraft-src/net/minecraft/client/renderer/entity/ItemRenderer.java
2025-07-04 02:49:36 +03:00

294 lines
12 KiB
Java

package net.minecraft.client.renderer.entity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexMultiConsumer;
import com.mojang.math.MatrixUtil;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.ItemModelShaper;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.ARGB;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.BundleItem;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class ItemRenderer implements ResourceManagerReloadListener {
public static final ResourceLocation ENCHANTED_GLINT_ENTITY = ResourceLocation.withDefaultNamespace("textures/misc/enchanted_glint_entity.png");
public static final ResourceLocation ENCHANTED_GLINT_ITEM = ResourceLocation.withDefaultNamespace("textures/misc/enchanted_glint_item.png");
public static final int GUI_SLOT_CENTER_X = 8;
public static final int GUI_SLOT_CENTER_Y = 8;
public static final int ITEM_DECORATION_BLIT_OFFSET = 200;
public static final float COMPASS_FOIL_UI_SCALE = 0.5F;
public static final float COMPASS_FOIL_FIRST_PERSON_SCALE = 0.75F;
public static final float COMPASS_FOIL_TEXTURE_SCALE = 0.0078125F;
public static final ModelResourceLocation TRIDENT_MODEL = ModelResourceLocation.inventory(ResourceLocation.withDefaultNamespace("trident"));
public static final ModelResourceLocation SPYGLASS_MODEL = ModelResourceLocation.inventory(ResourceLocation.withDefaultNamespace("spyglass"));
private final ModelManager modelManager;
private final ItemModelShaper itemModelShaper;
private final ItemColors itemColors;
private final BlockEntityWithoutLevelRenderer blockEntityRenderer;
public ItemRenderer(ModelManager modelManager, ItemColors itemColors, BlockEntityWithoutLevelRenderer blockEntityRenderer) {
this.modelManager = modelManager;
this.itemModelShaper = new ItemModelShaper(modelManager);
this.blockEntityRenderer = blockEntityRenderer;
this.itemColors = itemColors;
}
private void renderModelLists(BakedModel model, ItemStack stack, int packedLight, int packedOverlay, PoseStack poseStack, VertexConsumer buffer) {
RandomSource randomSource = RandomSource.create();
long l = 42L;
for (Direction direction : Direction.values()) {
randomSource.setSeed(42L);
this.renderQuadList(poseStack, buffer, model.getQuads(null, direction, randomSource), stack, packedLight, packedOverlay);
}
randomSource.setSeed(42L);
this.renderQuadList(poseStack, buffer, model.getQuads(null, null, randomSource), stack, packedLight, packedOverlay);
}
public void render(
ItemStack itemStack,
ItemDisplayContext displayContext,
boolean leftHand,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLIght,
int packedOverlay,
BakedModel model
) {
if (!itemStack.isEmpty()) {
this.renderSimpleItemModel(
itemStack, displayContext, leftHand, poseStack, bufferSource, packedLIght, packedOverlay, model, shouldRenderItemFlat(displayContext)
);
}
}
public void renderBundleItem(
ItemStack itemStack,
ItemDisplayContext displayContext,
boolean leftHand,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
int packedOverlay,
BakedModel model,
@Nullable Level level,
@Nullable LivingEntity entity,
int seed
) {
if (itemStack.getItem() instanceof BundleItem bundleItem) {
if (BundleItem.hasSelectedItem(itemStack)) {
boolean bl = shouldRenderItemFlat(displayContext);
BakedModel bakedModel = this.resolveModelOverride(this.itemModelShaper.getItemModel(bundleItem.openBackModel()), itemStack, level, entity, seed);
this.renderItemModelRaw(itemStack, displayContext, leftHand, poseStack, bufferSource, packedLight, packedOverlay, bakedModel, bl, -1.5F);
ItemStack itemStack2 = BundleItem.getSelectedItemStack(itemStack);
BakedModel bakedModel2 = this.getModel(itemStack2, level, entity, seed);
this.renderSimpleItemModel(itemStack2, displayContext, leftHand, poseStack, bufferSource, packedLight, packedOverlay, bakedModel2, bl);
BakedModel bakedModel3 = this.resolveModelOverride(this.itemModelShaper.getItemModel(bundleItem.openFrontModel()), itemStack, level, entity, seed);
this.renderItemModelRaw(itemStack, displayContext, leftHand, poseStack, bufferSource, packedLight, packedOverlay, bakedModel3, bl, 0.5F);
} else {
this.render(itemStack, displayContext, leftHand, poseStack, bufferSource, packedLight, packedOverlay, model);
}
}
}
private void renderSimpleItemModel(
ItemStack itemStack,
ItemDisplayContext displayContext,
boolean leftHand,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
int packedOverlay,
BakedModel model,
boolean renderOpenBundle
) {
if (renderOpenBundle) {
if (itemStack.is(Items.TRIDENT)) {
model = this.modelManager.getModel(TRIDENT_MODEL);
} else if (itemStack.is(Items.SPYGLASS)) {
model = this.modelManager.getModel(SPYGLASS_MODEL);
}
}
this.renderItemModelRaw(itemStack, displayContext, leftHand, poseStack, bufferSource, packedLight, packedOverlay, model, renderOpenBundle, -0.5F);
}
private void renderItemModelRaw(
ItemStack itemStack,
ItemDisplayContext displayContext,
boolean leftHand,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
int packedOverlay,
BakedModel model,
boolean renderOpenBundle,
float z
) {
poseStack.pushPose();
model.getTransforms().getTransform(displayContext).apply(leftHand, poseStack);
poseStack.translate(-0.5F, -0.5F, z);
this.renderItem(itemStack, displayContext, poseStack, bufferSource, packedLight, packedOverlay, model, renderOpenBundle);
poseStack.popPose();
}
private void renderItem(
ItemStack itemStack,
ItemDisplayContext displayContext,
PoseStack poseStack,
MultiBufferSource bufferSource,
int packedLight,
int packedOverlay,
BakedModel model,
boolean renderOpenBundle
) {
if (!model.isCustomRenderer() && (!itemStack.is(Items.TRIDENT) || renderOpenBundle)) {
RenderType renderType = ItemBlockRenderTypes.getRenderType(itemStack);
VertexConsumer vertexConsumer;
if (hasAnimatedTexture(itemStack) && itemStack.hasFoil()) {
PoseStack.Pose pose = poseStack.last().copy();
if (displayContext == ItemDisplayContext.GUI) {
MatrixUtil.mulComponentWise(pose.pose(), 0.5F);
} else if (displayContext.firstPerson()) {
MatrixUtil.mulComponentWise(pose.pose(), 0.75F);
}
vertexConsumer = getCompassFoilBuffer(bufferSource, renderType, pose);
} else {
vertexConsumer = getFoilBuffer(bufferSource, renderType, true, itemStack.hasFoil());
}
this.renderModelLists(model, itemStack, packedLight, packedOverlay, poseStack, vertexConsumer);
} else {
this.blockEntityRenderer.renderByItem(itemStack, displayContext, poseStack, bufferSource, packedLight, packedOverlay);
}
}
private static boolean shouldRenderItemFlat(ItemDisplayContext displayContent) {
return displayContent == ItemDisplayContext.GUI || displayContent == ItemDisplayContext.GROUND || displayContent == ItemDisplayContext.FIXED;
}
private static boolean hasAnimatedTexture(ItemStack stack) {
return stack.is(ItemTags.COMPASSES) || stack.is(Items.CLOCK);
}
public static VertexConsumer getArmorFoilBuffer(MultiBufferSource bufferSource, RenderType renderType, boolean hasFoil) {
return hasFoil
? VertexMultiConsumer.create(bufferSource.getBuffer(RenderType.armorEntityGlint()), bufferSource.getBuffer(renderType))
: bufferSource.getBuffer(renderType);
}
public static VertexConsumer getCompassFoilBuffer(MultiBufferSource bufferSource, RenderType renderType, PoseStack.Pose pose) {
return VertexMultiConsumer.create(
new SheetedDecalTextureGenerator(bufferSource.getBuffer(RenderType.glint()), pose, 0.0078125F), bufferSource.getBuffer(renderType)
);
}
public static VertexConsumer getFoilBuffer(MultiBufferSource bufferSource, RenderType renderType, boolean isItem, boolean glint) {
if (glint) {
return Minecraft.useShaderTransparency() && renderType == Sheets.translucentItemSheet()
? VertexMultiConsumer.create(bufferSource.getBuffer(RenderType.glintTranslucent()), bufferSource.getBuffer(renderType))
: VertexMultiConsumer.create(bufferSource.getBuffer(isItem ? RenderType.glint() : RenderType.entityGlint()), bufferSource.getBuffer(renderType));
} else {
return bufferSource.getBuffer(renderType);
}
}
private void renderQuadList(PoseStack poseStack, VertexConsumer buffer, List<BakedQuad> quads, ItemStack itemStack, int combinedLight, int combinedOverlay) {
boolean bl = !itemStack.isEmpty();
PoseStack.Pose pose = poseStack.last();
for (BakedQuad bakedQuad : quads) {
int i = -1;
if (bl && bakedQuad.isTinted()) {
i = this.itemColors.getColor(itemStack, bakedQuad.getTintIndex());
}
float f = ARGB.alpha(i) / 255.0F;
float g = ARGB.red(i) / 255.0F;
float h = ARGB.green(i) / 255.0F;
float j = ARGB.blue(i) / 255.0F;
buffer.putBulkData(pose, bakedQuad, g, h, j, f, combinedLight, combinedOverlay);
}
}
public BakedModel getModel(ItemStack stack, @Nullable Level level, @Nullable LivingEntity entity, int seed) {
BakedModel bakedModel = this.itemModelShaper.getItemModel(stack);
return this.resolveModelOverride(bakedModel, stack, level, entity, seed);
}
public void renderStatic(
ItemStack stack,
ItemDisplayContext displayContext,
int combinedLight,
int combinedOverlay,
PoseStack poseStack,
MultiBufferSource bufferSource,
@Nullable Level level,
int seed
) {
this.renderStatic(null, stack, displayContext, false, poseStack, bufferSource, level, combinedLight, combinedOverlay, seed);
}
public void renderStatic(
@Nullable LivingEntity entity,
ItemStack itemStack,
ItemDisplayContext diplayContext,
boolean leftHand,
PoseStack poseStack,
MultiBufferSource bufferSource,
@Nullable Level level,
int combinedLight,
int combinedOverlay,
int seed
) {
if (!itemStack.isEmpty()) {
BakedModel bakedModel = this.getModel(itemStack, level, entity, seed);
this.render(itemStack, diplayContext, leftHand, poseStack, bufferSource, combinedLight, combinedOverlay, bakedModel);
}
}
@Override
public void onResourceManagerReload(ResourceManager resourceManager) {
this.itemModelShaper.invalidateCache();
}
@Nullable
public BakedModel resolveItemModel(ItemStack stack, LivingEntity entity, ItemDisplayContext displayContext) {
return stack.isEmpty() ? null : this.getModel(stack, entity.level(), entity, entity.getId() + displayContext.ordinal());
}
private BakedModel resolveModelOverride(BakedModel model, ItemStack stack, @Nullable Level level, @Nullable LivingEntity entity, int seed) {
ClientLevel clientLevel = level instanceof ClientLevel ? (ClientLevel)level : null;
BakedModel bakedModel = model.overrides().findOverride(stack, clientLevel, entity, seed);
return bakedModel == null ? model : bakedModel;
}
}