From 6dc65d012e51f2c833436667897a9cb194be63ae Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 14 Jan 2025 18:21:42 +0100 Subject: [PATCH] Backface culling on items/item entities --- .../render/model/item/ItemRendererMixin.java | 130 ++++++++++++++++-- 1 file changed, 122 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java index 425480f504..da22129605 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/mixin/features/render/model/item/ItemRendererMixin.java @@ -13,26 +13,138 @@ import net.caffeinemc.mods.sodium.client.util.DirectionUtil; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.item.ItemStackRenderState; import net.minecraft.core.Direction; import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.level.levelgen.SingleThreadedRandomSource; +import org.joml.Matrix4f; +import org.joml.Vector3f; 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 java.util.List; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.renderer.RenderType; @Mixin(ItemRenderer.class) public abstract class ItemRendererMixin { @Unique private static final ThreadLocal random = ThreadLocal.withInitial(() -> new SingleThreadedRandomSource(42L)); + @Unique + private static final ThreadLocal currentRenderContext = ThreadLocal.withInitial(() -> ItemDisplayContext.NONE); + + @Unique + private static final Vector3f FACE_NORMAL = new Vector3f(); + @Unique + private static final Vector3f VERTEX_POS = new Vector3f(); + @Unique + private static final Matrix4f FACE_NORMAL_MATRIX = new Matrix4f(); + @Unique + private static final Vector3f EDGE1 = new Vector3f(); + @Unique + private static final Vector3f EDGE2 = new Vector3f(); + @Unique + private static final float[] VERTEX_CACHE = new float[24]; + @Shadow private static int getLayerColorSafe(int[] is, int i) { throw new AssertionError("Not shadowed"); } + @Inject(method = "renderItem", at = @At("HEAD")) + private static void onRenderItemStart(ItemDisplayContext displayContext, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, int[] tintLayers, BakedModel model, RenderType renderType, ItemStackRenderState.FoilType foilType, CallbackInfo ci) { + currentRenderContext.set(displayContext); + } + + @Inject(method = "renderItem", at = @At("RETURN")) + private static void onRenderItemEnd(ItemDisplayContext displayContext, PoseStack poseStack, MultiBufferSource bufferSource, + int packedLight, int packedOverlay, int[] tintLayers, BakedModel model, + RenderType renderType, ItemStackRenderState.FoilType foilType, CallbackInfo ci) { + currentRenderContext.set(ItemDisplayContext.NONE); + } + + @Unique + private static boolean isFacingAway(PoseStack.Pose matrices, BakedQuad quad) { + if (currentRenderContext.get() == ItemDisplayContext.GUI) { + return false; // Never cull faces in GUI + } + + int[] vertices = quad.getVertices(); + if (vertices.length < 32) { + return false; + } + + // Extract normal directly from first vertex - most common case + float nx = Float.intBitsToFloat(vertices[6]); + float ny = Float.intBitsToFloat(vertices[7]); + float nz = Float.intBitsToFloat(vertices[14]); + + // Fast path for quads with normals + if (nx != 0 || ny != 0 || nz != 0) { + matrices.pose().normal(FACE_NORMAL_MATRIX); + + FACE_NORMAL.set( + nx * FACE_NORMAL_MATRIX.m00() + ny * FACE_NORMAL_MATRIX.m01() + nz * FACE_NORMAL_MATRIX.m02(), + nx * FACE_NORMAL_MATRIX.m10() + ny * FACE_NORMAL_MATRIX.m11() + nz * FACE_NORMAL_MATRIX.m12(), + nx * FACE_NORMAL_MATRIX.m20() + ny * FACE_NORMAL_MATRIX.m21() + nz * FACE_NORMAL_MATRIX.m22() + ); + + VERTEX_POS.set( + Float.intBitsToFloat(vertices[0]), + Float.intBitsToFloat(vertices[1]), + Float.intBitsToFloat(vertices[2]) + ); + matrices.pose().transformPosition(VERTEX_POS); + + return (FACE_NORMAL.x * -VERTEX_POS.x + + FACE_NORMAL.y * -VERTEX_POS.y + + FACE_NORMAL.z * -VERTEX_POS.z) < 0.0f; + } + + // Fallback path for quads without normals + for (int i = 0; i < 24; i += 8) { + int baseIndex = (i / 8) * 8; + VERTEX_CACHE[i] = Float.intBitsToFloat(vertices[baseIndex]); + VERTEX_CACHE[i + 1] = Float.intBitsToFloat(vertices[baseIndex + 1]); + VERTEX_CACHE[i + 2] = Float.intBitsToFloat(vertices[baseIndex + 2]); + } + + EDGE1.set( + VERTEX_CACHE[8] - VERTEX_CACHE[0], + VERTEX_CACHE[9] - VERTEX_CACHE[1], + VERTEX_CACHE[10] - VERTEX_CACHE[2] + ); + + EDGE2.set( + VERTEX_CACHE[16] - VERTEX_CACHE[0], + VERTEX_CACHE[17] - VERTEX_CACHE[1], + VERTEX_CACHE[18] - VERTEX_CACHE[2] + ); + + FACE_NORMAL.set( + EDGE1.y * EDGE2.z - EDGE1.z * EDGE2.y, + EDGE1.z * EDGE2.x - EDGE1.x * EDGE2.z, + EDGE1.x * EDGE2.y - EDGE1.y * EDGE2.x + ); + + matrices.pose().normal(FACE_NORMAL_MATRIX); + FACE_NORMAL_MATRIX.transformDirection(FACE_NORMAL); + + VERTEX_POS.set(VERTEX_CACHE[0], VERTEX_CACHE[1], VERTEX_CACHE[2]); + matrices.pose().transformPosition(VERTEX_POS); + + return (FACE_NORMAL.x * -VERTEX_POS.x + + FACE_NORMAL.y * -VERTEX_POS.y + + FACE_NORMAL.z * -VERTEX_POS.z) < 0.0f; + } + /** * @reason Avoid Allocations * @return JellySquid @@ -80,17 +192,19 @@ private static void renderBakedItemQuads(PoseStack.Pose matrices, VertexBufferWr continue; // ignore bad quads } - BakedQuadView quad = (BakedQuadView) bakedQuad; + if (!isFacingAway(matrices, bakedQuad)) { + BakedQuadView quad = (BakedQuadView) bakedQuad; - int color = 0xFFFFFFFF; + int color = 0xFFFFFFFF; - if (bakedQuad.isTinted()) { - color = ColorARGB.toABGR(getLayerColorSafe(colors, bakedQuad.getTintIndex())); - } + if (bakedQuad.isTinted()) { + color = ColorARGB.toABGR(getLayerColorSafe(colors, bakedQuad.getTintIndex())); + } - BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, BakedModelEncoder.shouldMultiplyAlpha()); + BakedModelEncoder.writeQuadVertices(writer, matrices, quad, color, light, overlay, BakedModelEncoder.shouldMultiplyAlpha()); - SpriteUtil.markSpriteActive(quad.getSprite()); + SpriteUtil.markSpriteActive(quad.getSprite()); + } } } -} +} \ No newline at end of file