aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorAnthony Hilyard <anthony.hilyard@gmail.com>2022-12-22 15:20:04 -0800
committerAnthony Hilyard <anthony.hilyard@gmail.com>2022-12-22 15:20:04 -0800
commitce46c0feb25b6127a1ce4e5624a05acf5ce16a71 (patch)
treedf65f661c8dc001838d15979a5316fe5db0935eb /src/main
parent379e47c13645498bc9fe3c810bef4bba5994504b (diff)
downloadIceberg-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')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/IcebergClient.java26
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/Loader.java3
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/mixin/ScreenMixin.java39
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/mixin/TooltipRenderUtilMixin.java120
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/CheckedBufferSource.java68
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/CustomItemRenderer.java617
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/renderer/VertexCollector.java79
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/DynamicResourcePack.java59
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/GuiHelper.java2
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java85
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/Tooltips.java229
-rw-r--r--src/main/resources/META-INF/accesstransformer.cfg8
-rw-r--r--src/main/resources/iceberg.mixins.json3
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