From 70e1e2cec6f80a4c82a70b57db04b33f031424de Mon Sep 17 00:00:00 2001 From: mezz Date: Tue, 27 Aug 2024 14:07:36 +0900 Subject: [PATCH] Batch-render itemstacks --- .../jei/library/render/ItemStackRenderer.java | 9 ++ .../render/batch/ElementWithModel.java | 7 + .../render/batch/ItemStackBatchRenderer.java | 126 ++++++++++++++++++ .../batch/ItemStackBatchRendererCache.java | 35 +++++ .../render/batch/LimitedQuadItemModel.java | 71 ++++++++++ .../library/render/batch/package-info.java | 7 + 6 files changed, 255 insertions(+) create mode 100644 Library/src/main/java/mezz/jei/library/render/batch/ElementWithModel.java create mode 100644 Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRenderer.java create mode 100644 Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRendererCache.java create mode 100644 Library/src/main/java/mezz/jei/library/render/batch/LimitedQuadItemModel.java create mode 100644 Library/src/main/java/mezz/jei/library/render/batch/package-info.java diff --git a/Library/src/main/java/mezz/jei/library/render/ItemStackRenderer.java b/Library/src/main/java/mezz/jei/library/render/ItemStackRenderer.java index 97f59f636..1f05c1a8f 100644 --- a/Library/src/main/java/mezz/jei/library/render/ItemStackRenderer.java +++ b/Library/src/main/java/mezz/jei/library/render/ItemStackRenderer.java @@ -3,8 +3,10 @@ import com.mojang.blaze3d.systems.RenderSystem; import mezz.jei.api.gui.builder.ITooltipBuilder; import mezz.jei.api.ingredients.IIngredientRenderer; +import mezz.jei.api.ingredients.rendering.BatchRenderElement; import mezz.jei.common.platform.IPlatformRenderHelper; import mezz.jei.common.platform.Services; +import mezz.jei.library.render.batch.ItemStackBatchRendererCache; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -18,6 +20,8 @@ import java.util.List; public class ItemStackRenderer implements IIngredientRenderer { + private final ItemStackBatchRendererCache batchRenderer = new ItemStackBatchRendererCache(); + @Override public void render(GuiGraphics guiGraphics, @Nullable ItemStack ingredient) { render(guiGraphics, ingredient, 0, 0); @@ -36,6 +40,11 @@ public void render(GuiGraphics guiGraphics, @Nullable ItemStack ingredient, int } } + @Override + public void renderBatch(GuiGraphics guiGraphics, List> batchRenderElements) { + batchRenderer.renderBatch(guiGraphics, batchRenderElements); + } + @SuppressWarnings("removal") @Override public List getTooltip(ItemStack ingredient, TooltipFlag tooltipFlag) { diff --git a/Library/src/main/java/mezz/jei/library/render/batch/ElementWithModel.java b/Library/src/main/java/mezz/jei/library/render/batch/ElementWithModel.java new file mode 100644 index 000000000..4693e8e5b --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/render/batch/ElementWithModel.java @@ -0,0 +1,7 @@ +package mezz.jei.library.render.batch; + +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.world.item.ItemStack; + +public record ElementWithModel(BakedModel model, ItemStack stack, int x, int y) { +} diff --git a/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRenderer.java b/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRenderer.java new file mode 100644 index 000000000..9c0a4cf0b --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRenderer.java @@ -0,0 +1,126 @@ +package mezz.jei.library.render.batch; + +import com.mojang.blaze3d.platform.Lighting; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mezz.jei.api.ingredients.rendering.BatchRenderElement; +import mezz.jei.common.platform.IPlatformRenderHelper; +import mezz.jei.common.platform.Services; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public final class ItemStackBatchRenderer { + private final List> elements; + private final List useBlockLight; + private final List noBlockLight; + private final List customRender; + + public ItemStackBatchRenderer(Minecraft minecraft, List> elements) { + this.elements = elements; + this.useBlockLight = new ArrayList<>(); + this.noBlockLight = new ArrayList<>(); + this.customRender = new ArrayList<>(); + + ClientLevel level = minecraft.level; + ItemRenderer itemRenderer = minecraft.getItemRenderer(); + + for (BatchRenderElement element : elements) { + ItemStack itemStack = element.ingredient(); + if (!itemStack.isEmpty()) { + BakedModel bakedmodel = itemRenderer.getModel(itemStack, level, null, 0); + if (bakedmodel.isCustomRenderer()) { + ElementWithModel elementWithModel = new ElementWithModel(bakedmodel, itemStack, element.x(), element.y()); + customRender.add(elementWithModel); + } else if (bakedmodel.usesBlockLight()) { + ElementWithModel elementWithModel = new ElementWithModel(bakedmodel, itemStack, element.x(), element.y()); + useBlockLight.add(elementWithModel); + } else { + bakedmodel = new LimitedQuadItemModel(bakedmodel); + ElementWithModel elementWithModel = new ElementWithModel(bakedmodel, itemStack, element.x(), element.y()); + noBlockLight.add(elementWithModel); + } + } + } + } + + public void render(GuiGraphics guiGraphics, Minecraft minecraft, ItemRenderer itemRenderer) { + if (!noBlockLight.isEmpty()) { + Lighting.setupForFlatItems(); + for (ElementWithModel element : noBlockLight) { + renderItem(guiGraphics, itemRenderer, element.model(), element.stack(), element.x(), element.y()); + } + guiGraphics.flush(); + Lighting.setupFor3DItems(); + } + + if (!useBlockLight.isEmpty()) { + Lighting.setupFor3DItems(); + for (ElementWithModel element : useBlockLight) { + renderItem(guiGraphics, itemRenderer, element.model(), element.stack(), element.x(), element.y()); + } + guiGraphics.flush(); + } + + for (ElementWithModel element : customRender) { + renderItem(guiGraphics, itemRenderer, element.model(), element.stack(), element.x(), element.y()); + guiGraphics.flush(); + } + + IPlatformRenderHelper renderHelper = Services.PLATFORM.getRenderHelper(); + for (BatchRenderElement element : elements) { + ItemStack ingredient = element.ingredient(); + Font font = renderHelper.getFontRenderer(minecraft, ingredient); + guiGraphics.renderItemDecorations(font, ingredient, element.x(), element.y()); + } + RenderSystem.disableBlend(); + } + + private void renderItem( + GuiGraphics guiGraphics, + ItemRenderer itemRenderer, + BakedModel bakedmodel, + ItemStack itemStack, + int x, + int y + ) { + PoseStack poseStack = guiGraphics.pose(); + poseStack.pushPose(); + poseStack.translate((float) (x + 8), (float) (y + 8), 150f); + poseStack.scale(16.0F, -16.0F, 16.0F); + + try { + itemRenderer.render( + itemStack, + ItemDisplayContext.GUI, + false, + poseStack, + guiGraphics.bufferSource(), + 0xf000f0, + OverlayTexture.NO_OVERLAY, + bakedmodel + ); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item"); + CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered"); + crashreportcategory.setDetail("Item Type", () -> String.valueOf(itemStack.getItem())); + crashreportcategory.setDetail("Item Components", () -> String.valueOf(itemStack.getComponents())); + crashreportcategory.setDetail("Item Foil", () -> String.valueOf(itemStack.hasFoil())); + throw new ReportedException(crashreport); + } + + poseStack.popPose(); + } +} diff --git a/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRendererCache.java b/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRendererCache.java new file mode 100644 index 000000000..394ba9c86 --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/render/batch/ItemStackBatchRendererCache.java @@ -0,0 +1,35 @@ +package mezz.jei.library.render.batch; + + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import mezz.jei.api.ingredients.rendering.BatchRenderElement; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.world.item.ItemStack; + +import java.util.List; + +public class ItemStackBatchRendererCache { + private final LoadingCache>, ItemStackBatchRenderer> cache = + CacheBuilder.newBuilder() + .maximumSize(6) + .build(new CacheLoader<>() { + @Override + public ItemStackBatchRenderer load(List> elements) { + Minecraft minecraft = Minecraft.getInstance(); + return new ItemStackBatchRenderer(minecraft, elements); + } + }); + + public void renderBatch(GuiGraphics guiGraphics, List> elements) { + ItemStackBatchRenderer batchData = cache.getUnchecked(elements); + + Minecraft minecraft = Minecraft.getInstance(); + ItemRenderer itemRenderer = minecraft.getItemRenderer(); + batchData.render(guiGraphics, minecraft, itemRenderer); + } + +} diff --git a/Library/src/main/java/mezz/jei/library/render/batch/LimitedQuadItemModel.java b/Library/src/main/java/mezz/jei/library/render/batch/LimitedQuadItemModel.java new file mode 100644 index 000000000..5322e89f0 --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/render/batch/LimitedQuadItemModel.java @@ -0,0 +1,71 @@ +package mezz.jei.library.render.batch; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class LimitedQuadItemModel implements BakedModel { + private final BakedModel bakedmodel; + private @Nullable List quads; + + public LimitedQuadItemModel(BakedModel bakedmodel) { + this.bakedmodel = bakedmodel; + } + + @Override + public List getQuads(@Nullable BlockState blockState, @Nullable Direction direction, RandomSource randomSource) { + if (direction == null) { + if (quads == null) { + quads = bakedmodel.getQuads(blockState, null, randomSource) + .stream() + .filter(q -> q.getDirection() == Direction.SOUTH) + .toList(); + } + return quads; + } + return List.of(); + } + + @Override + public boolean useAmbientOcclusion() { + return bakedmodel.useAmbientOcclusion(); + } + + @Override + public boolean isGui3d() { + return bakedmodel.isGui3d(); + } + + @Override + public boolean usesBlockLight() { + return bakedmodel.usesBlockLight(); + } + + @Override + public boolean isCustomRenderer() { + return bakedmodel.isCustomRenderer(); + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return bakedmodel.getParticleIcon(); + } + + @Override + public ItemTransforms getTransforms() { + return bakedmodel.getTransforms(); + } + + @Override + public ItemOverrides getOverrides() { + return bakedmodel.getOverrides(); + } +} diff --git a/Library/src/main/java/mezz/jei/library/render/batch/package-info.java b/Library/src/main/java/mezz/jei/library/render/batch/package-info.java new file mode 100644 index 000000000..f03518506 --- /dev/null +++ b/Library/src/main/java/mezz/jei/library/render/batch/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package mezz.jei.library.render.batch; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault;