Skip to content

Commit

Permalink
Batch-render itemstacks
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Aug 28, 2024
1 parent 60b212a commit f204c57
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +20,8 @@
import java.util.List;

public class ItemStackRenderer implements IIngredientRenderer<ItemStack> {
private final ItemStackBatchRendererCache batchRenderer = new ItemStackBatchRendererCache();

@Override
public void render(GuiGraphics guiGraphics, @Nullable ItemStack ingredient) {
render(guiGraphics, ingredient, 0, 0);
Expand All @@ -36,6 +40,11 @@ public void render(GuiGraphics guiGraphics, @Nullable ItemStack ingredient, int
}
}

@Override
public void renderBatch(GuiGraphics guiGraphics, List<BatchRenderElement<ItemStack>> batchRenderElements) {
batchRenderer.renderBatch(guiGraphics, batchRenderElements);
}

@SuppressWarnings("removal")
@Override
public List<Component> getTooltip(ItemStack ingredient, TooltipFlag tooltipFlag) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
}
Original file line number Diff line number Diff line change
@@ -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<BatchRenderElement<ItemStack>> elements;
private final List<ElementWithModel> useBlockLight;
private final List<ElementWithModel> noBlockLight;
private final List<ElementWithModel> customRender;

public ItemStackBatchRenderer(Minecraft minecraft, List<BatchRenderElement<ItemStack>> 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<ItemStack> 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<ItemStack> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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<List<BatchRenderElement<ItemStack>>, ItemStackBatchRenderer> cache =
CacheBuilder.newBuilder()
.maximumSize(6)
.build(new CacheLoader<>() {
@Override
public ItemStackBatchRenderer load(List<BatchRenderElement<ItemStack>> elements) {
Minecraft minecraft = Minecraft.getInstance();
return new ItemStackBatchRenderer(minecraft, elements);
}
});

public void renderBatch(GuiGraphics guiGraphics, List<BatchRenderElement<ItemStack>> elements) {
ItemStackBatchRenderer batchData = cache.getUnchecked(elements);

Minecraft minecraft = Minecraft.getInstance();
ItemRenderer itemRenderer = minecraft.getItemRenderer();
batchData.render(guiGraphics, minecraft, itemRenderer);
}

}
Original file line number Diff line number Diff line change
@@ -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<BakedQuad> quads;

public LimitedQuadItemModel(BakedModel bakedmodel) {
this.bakedmodel = bakedmodel;
}

@Override
public List<BakedQuad> 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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package mezz.jei.library.render.batch;

import net.minecraft.MethodsReturnNonnullByDefault;

import javax.annotation.ParametersAreNonnullByDefault;

0 comments on commit f204c57

Please sign in to comment.