diff options
| author | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-12-22 15:20:04 -0800 |
|---|---|---|
| committer | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-12-22 15:20:04 -0800 |
| commit | ce46c0feb25b6127a1ce4e5624a05acf5ce16a71 (patch) | |
| tree | df65f661c8dc001838d15979a5316fe5db0935eb /src/main | |
| parent | 379e47c13645498bc9fe3c810bef4bba5994504b (diff) | |
| download | Iceberg-ce46c0feb25b6127a1ce4e5624a05acf5ce16a71.tar.gz Iceberg-ce46c0feb25b6127a1ce4e5624a05acf5ce16a71.tar.bz2 Iceberg-ce46c0feb25b6127a1ce4e5624a05acf5ce16a71.zip | |
Added new selectors and item rendering capability.
- Added wildcard item selector.
- Added selector negation.
- Added selector combination.
- Item detail models can now be rendered via the custom item renderer.
- Fixed several tooltip rendering bugs.
- Added Minecraft 1.19.3 support.
Diffstat (limited to 'src/main')
13 files changed, 1205 insertions, 133 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/IcebergClient.java b/src/main/java/com/anthonyhilyard/iceberg/IcebergClient.java index edbfc1b..a5bd0dd 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/IcebergClient.java +++ b/src/main/java/com/anthonyhilyard/iceberg/IcebergClient.java @@ -1,6 +1,14 @@ package com.anthonyhilyard.iceberg; +import com.anthonyhilyard.iceberg.util.Tooltips.TitleBreakComponent; +import com.mojang.datafixers.util.Either; + +import net.minecraft.network.chat.FormattedText; +import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RenderTooltipEvent.GatherComponents; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @@ -14,7 +22,23 @@ public class IcebergClient public void onClientSetup(FMLClientSetupEvent event) { - + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void onGatherComponentsEventEnd(GatherComponents event) + { + if (event.getTooltipElements().size() > 1) + { + // Insert a title break component after the first formattedText component. + for (int i = 0; i < event.getTooltipElements().size(); i++) + { + if (event.getTooltipElements().get(i).left().isPresent()) + { + event.getTooltipElements().add(i + 1, Either.<FormattedText, TooltipComponent>right(new TitleBreakComponent())); + break; + } + } + } } // @SubscribeEvent diff --git a/src/main/java/com/anthonyhilyard/iceberg/Loader.java b/src/main/java/com/anthonyhilyard/iceberg/Loader.java index ae7db63..8d6ceb4 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/Loader.java +++ b/src/main/java/com/anthonyhilyard/iceberg/Loader.java @@ -3,6 +3,8 @@ package com.anthonyhilyard.iceberg; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.anthonyhilyard.iceberg.util.Tooltips.TitleBreakComponent; + import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -23,6 +25,7 @@ public class Loader { if (FMLEnvironment.dist == Dist.CLIENT) { + TitleBreakComponent.registerFactory(); IcebergClient mod = new IcebergClient(); MinecraftForge.EVENT_BUS.register(IcebergClient.class); FMLJavaModLoadingContext.get().getModEventBus().addListener(mod::onClientSetup); diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ScreenMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ScreenMixin.java index 4c7e0c8..68f2e88 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/mixin/ScreenMixin.java +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ScreenMixin.java @@ -3,6 +3,7 @@ package com.anthonyhilyard.iceberg.mixin; import java.util.List; import com.anthonyhilyard.iceberg.events.RenderTooltipExtEvent; +import com.anthonyhilyard.iceberg.util.Tooltips; import com.google.common.collect.Lists; import com.mojang.blaze3d.vertex.PoseStack; @@ -18,9 +19,12 @@ import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.events.AbstractContainerEventHandler; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; +import net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil; +import net.minecraft.network.chat.TextColor; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.event.RenderTooltipEvent; import net.minecraftforge.common.MinecraftForge; @@ -39,17 +43,46 @@ public class ScreenMixin extends AbstractContainerEventHandler @Shadow private final List<GuiEventListener> children = Lists.newArrayList(); + @Inject(method = "renderTooltipInternal", at = @At(value = "HEAD")) + private void icebergRenderTooltipInternalHead(PoseStack poseStack, List<ClientTooltipComponent> components, int x, int y, ClientTooltipPositioner positioner, CallbackInfo info) + { + // If the tooltip stack is empty, try to get the stack from the slot under the mouse. + // This is needed for the creative inventory screen, which doesn't set the tooltip stack. + Screen self = (Screen) (Object) this; + if (tooltipStack.isEmpty() && self instanceof AbstractContainerScreen<?> containerScreen && containerScreen.getSlotUnderMouse() != null) + { + tooltipStack = containerScreen.getSlotUnderMouse().getItem(); + } + } + + @Inject(method = "renderTooltipInternal", at = @At(value = "TAIL")) + private void icebergRenderTooltipInternalTail(PoseStack poseStack, List<ClientTooltipComponent> components, int x, int y, ClientTooltipPositioner positioner, CallbackInfo info) + { + tooltipStack = ItemStack.EMPTY; + } + @Inject(method = "renderTooltipInternal", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/ItemRenderer;blitOffset:F", ordinal = 2, shift = Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void renderTooltipInternal(PoseStack poseStack, List<ClientTooltipComponent> components, int x, int y, CallbackInfo info, RenderTooltipEvent.Pre pre, int tooltipWidth, int tooltipHeight, int postX, int postY) + private void icebergRenderTooltipInternalPost(PoseStack poseStack, List<ClientTooltipComponent> components, int x, int y, ClientTooltipPositioner positioner, CallbackInfo info, RenderTooltipEvent.Pre preEvent, int tooltipWidth, int tooltipHeight, int postX, int postY) { if (!components.isEmpty()) { - MinecraftForge.EVENT_BUS.post(new RenderTooltipExtEvent.Post(tooltipStack, poseStack, postX, postY, ForgeHooksClient.getTooltipFont(tooltipFont, tooltipStack, font), tooltipWidth, tooltipHeight, components, false)); + MinecraftForge.EVENT_BUS.post(new RenderTooltipExtEvent.Post(preEvent.getItemStack(), poseStack, postX, postY, preEvent.getFont(), tooltipWidth, tooltipHeight, components, false)); } } + @Inject(method = "renderTooltipInternal", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil;renderTooltipBackground(Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil$BlitPainter;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/BufferBuilder;IIIII)V", + ordinal = 0, shift = Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private void renderTooltipInternalColor(PoseStack poseStack, List<ClientTooltipComponent> components, int x, int y, ClientTooltipPositioner positioner, CallbackInfo info, RenderTooltipEvent.Pre preEvent) + { + RenderTooltipExtEvent.Color colorEvent = new RenderTooltipExtEvent.Color(preEvent.getItemStack(), poseStack, x, y, preEvent.getFont(), TooltipRenderUtil.BACKGROUND_COLOR, TooltipRenderUtil.BACKGROUND_COLOR, TooltipRenderUtil.BORDER_COLOR_TOP, TooltipRenderUtil.BORDER_COLOR_BOTTOM, components, false, 0); + MinecraftForge.EVENT_BUS.post(colorEvent); + + Tooltips.currentColors = new Tooltips.TooltipColors(TextColor.fromRgb(colorEvent.getBackgroundStart()), TextColor.fromRgb(colorEvent.getBackgroundEnd()), TextColor.fromRgb(colorEvent.getBorderStart()), TextColor.fromRgb(colorEvent.getBorderEnd())); + } + @Override public List<? extends GuiEventListener> children() { diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/TooltipRenderUtilMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/TooltipRenderUtilMixin.java new file mode 100644 index 0000000..791ece3 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/TooltipRenderUtilMixin.java @@ -0,0 +1,120 @@ +package com.anthonyhilyard.iceberg.mixin; + +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.anthonyhilyard.iceberg.util.Tooltips; +import com.mojang.blaze3d.vertex.BufferBuilder; + +import net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil; +import net.minecraft.network.chat.TextColor; + +@Mixin(TooltipRenderUtil.class) +public class TooltipRenderUtilMixin +{ + @Unique + private static TextColor horizontalLineColor; + + @Shadow + @Final + private static int BACKGROUND_COLOR; + + @Shadow + @Final + private static int BORDER_COLOR_TOP; + + @Shadow + @Final + private static int BORDER_COLOR_BOTTOM; + + @Inject(method = "renderTooltipBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil;renderHorizontalLine(Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil$BlitPainter;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/BufferBuilder;IIIII)V", shift = At.Shift.BEFORE, ordinal = 0)) + private static void icebergRenderTooltipBackgroundOne(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int height, int z, CallbackInfo info) + { + horizontalLineColor = Tooltips.currentColors.backgroundColorStart(); + } + + @Inject(method = "renderTooltipBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil;renderHorizontalLine(Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil$BlitPainter;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/BufferBuilder;IIIII)V", shift = At.Shift.BEFORE, ordinal = 1)) + private static void icebergRenderTooltipBackgroundTwo(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int height, int z, CallbackInfo info) + { + horizontalLineColor = Tooltips.currentColors.backgroundColorEnd(); + } + + @Inject(method = "renderFrameGradient", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil;renderHorizontalLine(Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil$BlitPainter;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/BufferBuilder;IIIII)V", shift = At.Shift.BEFORE, ordinal = 0)) + private static void icebergRenderFrameGradientOne(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int height, int z, int color1, int color2, CallbackInfo info) + { + horizontalLineColor = Tooltips.currentColors.borderColorStart(); + } + + @Inject(method = "renderFrameGradient", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil;renderHorizontalLine(Lnet/minecraft/client/gui/screens/inventory/tooltip/TooltipRenderUtil$BlitPainter;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/vertex/BufferBuilder;IIIII)V", shift = At.Shift.BEFORE, ordinal = 1)) + private static void icebergRenderFrameGradientTwo(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int height, int z, int color1, int color2, CallbackInfo info) + { + horizontalLineColor = Tooltips.currentColors.borderColorEnd(); + } + + @Inject(method = "renderHorizontalLine", at = @At(value = "HEAD"), cancellable = true) + private static void icebergRenderHorizontalLine(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int z, int color, CallbackInfo info) + { + if (color != BACKGROUND_COLOR && color != BORDER_COLOR_TOP && color != BORDER_COLOR_BOTTOM) + { + // Do default behavior so other mods that change colors can still work. + } + else + { + // Replace the rendered colors with the ones previously stored. + int renderColor = horizontalLineColor.getValue(); + painter.blit(matrix, bufferbuilder, x, y, x + width, y + 1, z, renderColor, renderColor); + info.cancel(); + } + } + + @Inject(method = "renderRectangle", at = @At(value = "HEAD"), cancellable = true) + private static void icebergRenderRectangle(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int width, int height, int z, int color, CallbackInfo info) + { + if (color != BACKGROUND_COLOR) + { + // Do default behavior so other mods that change colors can still work. + } + else + { + // Replace the rendered colors with the ones previously stored. + painter.blit(matrix, bufferbuilder, x, y, x + width, y + height, z, Tooltips.currentColors.backgroundColorStart().getValue(), Tooltips.currentColors.backgroundColorEnd().getValue()); + info.cancel(); + } + } + + @Inject(method = "renderVerticalLine", at = @At(value = "HEAD"), cancellable = true) + private static void icebergRenderVerticalLine(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int height, int z, int color, CallbackInfo info) + { + if (color != BACKGROUND_COLOR) + { + // Do default behavior so other mods that change colors can still work. + } + else + { + // Replace the rendered colors with the ones previously stored. + painter.blit(matrix, bufferbuilder, x, y, x + 1, y + height, z, Tooltips.currentColors.backgroundColorStart().getValue(), Tooltips.currentColors.backgroundColorEnd().getValue()); + info.cancel(); + } + } + + @Inject(method = "renderVerticalLineGradient", at = @At(value = "HEAD"), cancellable = true) + private static void icebergRenderVerticalLineGradient(TooltipRenderUtil.BlitPainter painter, Matrix4f matrix, BufferBuilder bufferbuilder, int x, int y, int height, int z, int startColor, int endColor, CallbackInfo info) + { + if (startColor != BORDER_COLOR_TOP || endColor != BORDER_COLOR_BOTTOM) + { + // Do default behavior so other mods that change colors can still work. + } + else + { + // Replace the rendered colors with the ones previously stored. + painter.blit(matrix, bufferbuilder, x, y, x + 1, y + height, z, Tooltips.currentColors.borderColorStart().getValue(), Tooltips.currentColors.borderColorEnd().getValue()); + info.cancel(); + } + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java new file mode 100644 index 0000000..5d34821 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java @@ -0,0 +1,68 @@ +package com.anthonyhilyard.iceberg.renderer; + +import com.mojang.blaze3d.vertex.VertexConsumer; + +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; + +public final class CheckedBufferSource implements MultiBufferSource +{ + private boolean hasRendered = false; + private final MultiBufferSource bufferSource; + + public CheckedBufferSource(MultiBufferSource bufferSource) + { + this.bufferSource = bufferSource; + } + + @Override + public VertexConsumer getBuffer(RenderType renderType) + { + final VertexConsumer vertexConsumer = bufferSource.getBuffer(renderType); + VertexConsumer vertexConsumerWrap = new VertexConsumer() + { + @Override + public VertexConsumer vertex(double x, double y, double z) + { + hasRendered = true; + return vertexConsumer.vertex(x, y, z); + } + + @Override + public VertexConsumer color(int r, int g, int b, int a) { return vertexConsumer.color(r, g, b, a); } + + @Override + public VertexConsumer uv(float u, float v) { return vertexConsumer.uv(u, v); } + + @Override + public VertexConsumer overlayCoords(int x, int y) { return vertexConsumer.overlayCoords(x, y); } + + @Override + public VertexConsumer uv2(int u, int v) { return vertexConsumer.uv2(u, v); } + + @Override + public VertexConsumer normal(float x, float y, float z) { return vertexConsumer.normal(x, y, z); } + + @Override + public void endVertex() { vertexConsumer.endVertex(); } + + @Override + public void defaultColor(int r, int g, int b, int a) { vertexConsumer.defaultColor(r, g, b, a); } + + @Override + public void unsetDefaultColor() { vertexConsumer.unsetDefaultColor(); } + }; + + return vertexConsumerWrap; + } + + public boolean hasRendered() + { + return hasRendered; + } + + public void reset() + { + hasRendered = false; + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java b/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java index b77bb09..301b4c4 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java +++ b/src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java @@ -1,50 +1,115 @@ package com.anthonyhilyard.iceberg.renderer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import com.google.common.collect.Maps; + +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import org.lwjgl.opengl.GL11; + import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.datafixers.util.Pair; +import com.mojang.math.MatrixUtil; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.pipeline.MainTarget; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.platform.Lighting; +import com.mojang.blaze3d.platform.GlStateManager.SourceFactor; +import com.mojang.blaze3d.platform.GlStateManager.DestFactor; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.opengl.GL11; - +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.TextureManager; - -import com.mojang.blaze3d.platform.GlStateManager.SourceFactor; -import com.mojang.blaze3d.platform.GlStateManager.DestFactor; -import com.mojang.math.Matrix4f; - import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.gui.GuiComponent; import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer; +import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.MultiBufferSource.BufferSource; import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.horse.Horse; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.entity.vehicle.ChestBoat; import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.BoatItem; +import net.minecraft.world.item.HorseArmorItem; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.MinecartItem; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HalfTransparentBlock; +import net.minecraft.world.level.block.StainedGlassPaneBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; + +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.extensions.common.IClientItemExtensions; /** - * An extended ItemRenderer that allows items to be rendered to RenderTarget before drawing to screen. - * This allows alpha values to be supported properly by all item types, even semi-transparent items. + * An extended ItemRenderer with extended functionality, such as allowing items to be rendered to a RenderTarget + * before drawing to screen for alpha support, and allowing handheld item models to be rendered into the gui. */ public class CustomItemRenderer extends ItemRenderer { - @SuppressWarnings("unused") - private static final Logger LOGGER = LogManager.getLogger(); + /* Cylindrical bounds for a model. */ + private record ModelBounds(Vector3f center, float height, float radius) {} private static RenderTarget iconFrameBuffer = null; + private static ArmorStand armorStand = null; + private static AbstractMinecart minecart = null; + private static Boat boat = null; + private static Horse horse = null; + private static Pair<Item, CompoundTag> cachedArmorStandItem = null; + private static Pair<Item, CompoundTag> cachedHorseArmorItem = null; + private static Map<Item, ModelBounds> modelBoundsCache = Maps.newHashMap(); + + private static final List<Direction> quadDirections; + static { + quadDirections = new ArrayList<>(Arrays.asList(Direction.values())); + quadDirections.add(null); + } + private Minecraft mc; - + private final ModelManager modelManager; + private final BlockEntityWithoutLevelRenderer blockEntityRenderer; + public CustomItemRenderer(TextureManager textureManagerIn, ModelManager modelManagerIn, ItemColors itemColorsIn, BlockEntityWithoutLevelRenderer blockEntityRendererIn, Minecraft mcIn) { super(textureManagerIn, modelManagerIn, itemColorsIn, blockEntityRendererIn); mc = mcIn; + modelManager = modelManagerIn; + blockEntityRenderer = blockEntityRendererIn; // Initialize the icon framebuffer if needed. if (iconFrameBuffer == null) @@ -56,6 +121,509 @@ public class CustomItemRenderer extends ItemRenderer } } + private void renderGuiModel(ItemStack itemStack, int x, int y, Quaternionf rotation, BakedModel bakedModel) + { + mc.getTextureManager().getTexture(InventoryMenu.BLOCK_ATLAS).setFilter(false, false); + RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); + + PoseStack modelViewStack = RenderSystem.getModelViewStack(); + modelViewStack.pushPose(); + modelViewStack.translate(x, y, 100.0f + blitOffset); + + modelViewStack.translate(8.0f, 8.0f, 0.0f); + modelViewStack.scale(1.0f, -1.0f, 1.0f); + modelViewStack.scale(16.0f, 16.0f, 16.0f); + RenderSystem.applyModelViewMatrix(); + + BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); + boolean flatLighting = !bakedModel.usesBlockLight(); + if (flatLighting) { Lighting.setupForFlatItems(); } + + PoseStack poseStack = new PoseStack(); + renderModel(itemStack, ItemTransforms.TransformType.GUI, false, poseStack, rotation, bufferSource, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, bakedModel); + + poseStack.popPose(); + bufferSource.endBatch(); + RenderSystem.enableDepthTest(); + if (flatLighting) { Lighting.setupFor3DItems(); } + + modelViewStack.popPose(); + RenderSystem.applyModelViewMatrix(); + } + + @SuppressWarnings("deprecation") + private void renderEntityModel(Entity entity, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) + { + Minecraft minecraft = Minecraft.getInstance(); + EntityRenderDispatcher entityRenderDispatcher = minecraft.getEntityRenderDispatcher(); + Lighting.setupForEntityInInventory(); + RenderSystem.enableDepthTest(); + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); + entityRenderDispatcher.setRenderShadow(false); + + RenderSystem.runAsFancy(() -> entityRenderDispatcher.render(entity, 0.0, 0.0, 0.0, 0.0f, 1.0f, poseStack, bufferSource, packedLight)); + + if (bufferSource instanceof BufferSource) + { + ((BufferSource)bufferSource).endBatch(); + } + + entityRenderDispatcher.setRenderShadow(true); + + RenderSystem.applyModelViewMatrix(); + Lighting.setupFor3DItems(); + } + + private <T extends MultiBufferSource> void renderModelInternal(ItemStack itemStack, ItemTransforms.TransformType transformType, boolean leftHanded, PoseStack poseStack, + Quaternionf rotation, T bufferSource, int packedLight, int packedOverlay, BakedModel bakedModel, + Predicate<T> bufferSourceReady) + { + Minecraft minecraft = Minecraft.getInstance(); + + if (Player.getEquipmentSlotForItem(itemStack).isArmor()) + { + if (updateArmorStand(itemStack)) + { + renderEntityModel(armorStand, poseStack, bufferSource, packedLight); + } + } + + if (!bakedModel.isCustomRenderer() && !itemStack.is(Items.TRIDENT)) + { + boolean fabulous; + if (transformType != ItemTransforms.TransformType.GUI && !transformType.firstPerson() && itemStack.getItem() instanceof BlockItem) + { + Block block = ((BlockItem)itemStack.getItem()).getBlock(); + fabulous = !(block instanceof HalfTransparentBlock) && !(block instanceof StainedGlassPaneBlock); + } + else + { + fabulous = true; + } + + if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof BlockItem blockItem) + { + BakedModel blockModel = null; + boolean isBlockEntity = false; + + blockModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(blockItem.getBlock().defaultBlockState()); + if (blockModel != modelManager.getMissingModel()) + { + // First try rendering via the BlockEntityWithoutLevelRenderer. + blockEntityRenderer.renderByItem(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay); + } + else + { + blockModel = null; + } + + if (blockItem.getBlock().defaultBlockState().hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) + { + // This is a double block, so we'll need to render both halves. + // First render the bottom half. + BlockState bottomState = blockItem.getBlock().defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER); + BakedModel bottomModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(bottomState); + renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, bottomModel, fabulous); + + // Then render the top half. + poseStack.pushPose(); + poseStack.translate(0.0f, 1.0f, 0.0f); + BlockState topState = blockItem.getBlock().defaultBlockState().setValue(BlockStateProperties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER); + BakedModel topModel = minecraft.getBlockRenderer().getBlockModelShaper().getBlockModel(topState); + renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, topModel, fabulous); + poseStack.popPose(); + } + + if (blockItem.getBlock() instanceof EntityBlock entityBlock) + { + isBlockEntity = true; + try + { + renderBlockEntity(itemStack, poseStack, bufferSource, packedLight, packedOverlay, minecraft, entityBlock, blockItem.getBlock().defaultBlockState()); + } + catch (Exception e) + { + // This can fail for things like beacons that require a level. We'll just ignore it. + } + } + + // If we still haven't rendered anything or this is a block entity, try rendering the block model. + if (blockModel != null && (bufferSourceReady.test(bufferSource) || isBlockEntity)) + { + renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, blockModel, fabulous); + } + } + + // Now try rendering entity models for items that spawn minecarts or boats. + if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof MinecartItem minecartItem) + { + if (updateMinecart(minecartItem)) + { + renderEntityModel(minecart, poseStack, bufferSource, packedLight); + } + } + + if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof BoatItem boatItem) + { + if (updateBoat(boatItem)) + { + renderEntityModel(boat, poseStack, bufferSource, packedLight); + } + } + + // If this is horse armor, render it here. + if (bufferSourceReady.test(bufferSource) && itemStack.getItem() instanceof HorseArmorItem) + { + if (updateHorse(itemStack)) + { + renderEntityModel(horse, poseStack, bufferSource, packedLight); + } + } + + // Finally, fall back to just rendering the item model. + if (bufferSourceReady.test(bufferSource)) + { + renderBakedModel(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay, bakedModel, fabulous); + } + } + else if (bufferSourceReady.test(bufferSource)) + { + IClientItemExtensions.of(itemStack).getCustomRenderer().renderByItem(itemStack, transformType, poseStack, bufferSource, packedLight, packedOverlay); + } + } + + private void renderModel(ItemStack itemStack, ItemTransforms.TransformType transformType, boolean leftHanded, PoseStack poseStack, Quaternionf rotation, MultiBufferSource bufferSource, int packedLight, int packedOverlay, BakedModel bakedModel) + { + if (!itemStack.isEmpty()) + { + boolean isBlockItem = false, spawnsEntity = false, isArmor = false; + if (itemStack.getItem() instanceof BlockItem blockItem) + { + isBlockItem = true; + } + else if (itemStack.getItem() instanceof MinecartItem || itemStack.getItem() instanceof BoatItem) + { + spawnsEntity = true; + } + + if (Player.getEquipmentSlotForItem(itemStack).isArmor()) + { + isArmor = true; + } + + poseStack.pushPose(); + + if (isBlockItem || spawnsEntity) + { + // Apply the standard block rotation so block entities match other blocks. + poseStack.mulPose(new Quaternionf().rotationXYZ((float)Math.toRadians(30.0f), (float)Math.toRadians(225.0f), 0.0f)); + } + else + { + ForgeHooksClient.handleCameraTransforms(poseStack, bakedModel, transformType, leftHanded); + } + + // Get the model bounds. + ModelBounds modelBounds = getModelBounds(itemStack, transformType, leftHanded, poseStack, rotation, bufferSource, packedLight, packedOverlay, bakedModel); + + // Undo the camera transforms now that we have the model bounds. + poseStack.popPose(); + poseStack.pushPose(); + + // Rotate the model. + poseStack.mulPose(rotation); + + // Scale the model to fit. + float scale = 1.0f / Math.max(modelBounds.height, modelBounds.radius * 2.0f); + scale *= isBlockItem ? 0.8f : 1.0f; + + // Adjust the scale based on the armor type. + if (isArmor) + { + switch (Player.getEquipmentSlotForItem(itemStack)) + { + case HEAD: + scale *= 0.75f; + break; + case LEGS: + scale *= 1.1f; + break; + default: + break; + } + } + poseStack.scale(scale, scale, scale); + + // Translate the model to the center of the item. + poseStack.translate(-modelBounds.center.x(), -modelBounds.center.y(), -modelBounds.center.z()); + + // Reapply the camera transforms. + if (isBlockItem || spawnsEntity) + { + // Apply the standard block rotation so block entities match other blocks. + poseStack.mulPose(new Quaternionf().rotationXYZ((float)Math.toRadians(30.0f), (float)Math.toRadians(225.0f), 0.0f)); + } + else + { + bakedModel = ForgeHooksClient.handleCameraTransforms(poseStack, bakedModel, transformType, leftHanded); + } + + CheckedBufferSource checkedBufferSource = new CheckedBufferSource(bufferSource); + renderModelInternal(itemStack, transformType, leftHanded, poseStack, rotation, checkedBufferSource, packedLight, packedOverlay, bakedModel, b -> !b.hasRendered()); + + poseStack.popPose(); + } + } + + private void renderBlockEntity(ItemStack itemStack, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, + int packedOverlay, Minecraft minecraft, EntityBlock entityBlock, BlockState blockState) throws Exception + { + // If we didn't render via the BlockEntityWithoutLevelRenderer, now check if this is a block entity and render that. + BlockEntity blockEntity = entityBlock.newBlockEntity(BlockPos.ZERO, blockState); + if (blockEntity != null) + { + if (itemStack.getTag() != null) + { + blockEntity.load(itemStack.getTag()); + } + + BlockEntityRenderer<BlockEntity> renderer = minecraft.getBlockEntityRenderDispatcher().getRenderer(blockEntity); + if (renderer != null) + { + renderer.render(blockEntity, minecraft.getFrameTime(), poseStack, bufferSource, packedLight, packedOverlay); + } + } + } + + private void renderBakedModel(ItemStack itemStack, ItemTransforms.TransformType transformType, PoseStack poseStack, + MultiBufferSource bufferSource, int packedLight, int packedOverlay, BakedModel bakedModel, boolean fabulous) + { + for (var model : bakedModel.getRenderPasses(itemStack, fabulous)) + { + for (var rendertype : model.getRenderTypes(itemStack, fabulous)) + { + VertexConsumer vertexconsumer; + if (itemStack.is(ItemTags.COMPASSES) && itemStack.hasFoil()) + { + poseStack.pushPose(); + PoseStack.Pose posestack$pose = poseStack.las |
