From 4f5836e5d4a5090366f63df032034b9215332a53 Mon Sep 17 00:00:00 2001
From: PepperCode1 <44146161+peppercode1@users.noreply.github.com>
Date: Sun, 17 Apr 2022 19:23:55 +0100
Subject: [PATCH] Modernize Indigo (#2110)

* Modernize Indigo

- Restructure ItemRenderContext to be more efficient and consistent with block rendering
- Remove unnecessary code including CompatibilityHelper
- Add Unique annotations to fields added by Indigo
- Fix typos
- Organize imports

* Remove unused import

* Fix bugs

- Fix enchantment glint not rendering on item models
- Fix QuadView#copyTo not copying enough data
- Make ItemRenderContext vertex consumer calculation mirror vanilla and be more efficient

* Clear the target quad's material

* Tweaks

- Retain material during copyTo instead of clearing it
- Standardize mixins

* Direct return

- Return from ItemRenderContext#quadVertexConsumer instead of assigning value to variable
---
 .../indigo/renderer/mesh/QuadViewImpl.java    |   7 +-
 .../renderer/render/AbstractMeshConsumer.java |  28 +-
 .../renderer/render/AbstractQuadRenderer.java |  17 +-
 .../render/AbstractRenderContext.java         |  12 +-
 .../renderer/render/BlockRenderContext.java   |  69 ++--
 .../renderer/render/ChunkRenderInfo.java      |   2 +-
 .../renderer/render/CompatibilityHelper.java  |  44 ---
 .../renderer/render/ItemRenderContext.java    | 334 +++++++++++-------
 .../render/TerrainFallbackConsumer.java       |  37 +-
 .../renderer/render/TerrainRenderContext.java |  24 +-
 .../MixinAmbientOcclusionCalculator.java      |   6 +-
 .../renderer/MixinBlockModelRenderer.java     |   8 +-
 .../renderer/MixinChunkRebuildTask.java       |  13 +-
 .../indigo/renderer/MixinChunkRenderData.java |   2 +-
 .../renderer/MixinChunkRendererRegion.java    |   2 +
 .../indigo/renderer/MixinItemRenderer.java    |  25 +-
 16 files changed, 316 insertions(+), 314 deletions(-)
 delete mode 100644 fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/CompatibilityHelper.java

diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java
index a0f895cac..c386e3a39 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/QuadViewImpl.java
@@ -33,9 +33,10 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
 
 import com.google.common.base.Preconditions;
 
-import net.minecraft.util.math.Vec3f;
 import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.Vec3f;
 
+import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
 import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
 import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
@@ -163,7 +164,9 @@ public class QuadViewImpl implements QuadView {
 
 		final MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
 		// copy everything except the material
-		System.arraycopy(data, baseIndex + 1, quad.data, quad.baseIndex + 1, EncodingFormat.TOTAL_STRIDE - 1);
+		RenderMaterial material = quad.material();
+		System.arraycopy(data, baseIndex, quad.data, quad.baseIndex, EncodingFormat.TOTAL_STRIDE);
+		quad.material(material);
 		quad.faceNormal.set(faceNormal.getX(), faceNormal.getY(), faceNormal.getZ());
 		quad.nominalFace = this.nominalFace;
 		quad.isGeometryInvalid = false;
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java
index c33d27688..d62654c65 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractMeshConsumer.java
@@ -34,7 +34,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
 import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
 
 /**
- * Consumer for pre-baked meshes.  Works by copying the mesh data to a
+ * Consumer for pre-baked meshes.  Works by copying the mesh data to an
  * "editor" quad held in the instance, where all transformations are applied before buffering.
  */
 public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implements Consumer<Mesh> {
@@ -46,7 +46,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
 	 * Where we handle all pre-buffer coloring, lighting, transformation, etc.
 	 * Reused for all mesh quads. Fixed baking array sized to hold largest possible mesh quad.
 	 */
-	private class Maker extends MutableQuadViewImpl implements QuadEmitter {
+	private class Maker extends MutableQuadViewImpl {
 		{
 			data = new int[EncodingFormat.TOTAL_STRIDE];
 			material(IndigoRenderer.MATERIAL_STANDARD);
@@ -84,44 +84,44 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
 		return editorQuad;
 	}
 
-	private void renderQuad(MutableQuadViewImpl q) {
-		if (!transform.transform(editorQuad)) {
+	private void renderQuad(MutableQuadViewImpl quad) {
+		if (!transform.transform(quad)) {
 			return;
 		}
 
-		if (!blockInfo.shouldDrawFace(q.cullFace())) {
+		if (!blockInfo.shouldDrawFace(quad.cullFace())) {
 			return;
 		}
 
-		final RenderMaterialImpl.Value mat = q.material();
+		final RenderMaterialImpl.Value mat = quad.material();
 
 		if (!mat.disableAo(0) && MinecraftClient.isAmbientOcclusionEnabled()) {
 			// needs to happen before offsets are applied
-			aoCalc.compute(q, false);
+			aoCalc.compute(quad, false);
 		}
 
-		tesselateQuad(q, mat, 0);
+		tessellateQuad(quad, mat, 0);
 	}
 
 	/**
 	 * Determines color index and render layer, then routes to appropriate
-	 * tesselate routine based on material properties.
+	 * tessellate routine based on material properties.
 	 */
-	private void tesselateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) {
+	private void tessellateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) {
 		final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
 		final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
 
 		if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
 			if (mat.emissive(textureIndex)) {
-				tesselateSmoothEmissive(quad, renderLayer, colorIndex);
+				tessellateSmoothEmissive(quad, renderLayer, colorIndex);
 			} else {
-				tesselateSmooth(quad, renderLayer, colorIndex);
+				tessellateSmooth(quad, renderLayer, colorIndex);
 			}
 		} else {
 			if (mat.emissive(textureIndex)) {
-				tesselateFlatEmissive(quad, renderLayer, colorIndex);
+				tessellateFlatEmissive(quad, renderLayer, colorIndex);
 			} else {
-				tesselateFlat(quad, renderLayer, colorIndex);
+				tessellateFlat(quad, renderLayer, colorIndex);
 			}
 		}
 	}
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java
index b0f5a4343..409dde259 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractQuadRenderer.java
@@ -22,14 +22,15 @@ import java.util.function.Function;
 
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockState;
+import net.minecraft.client.render.LightmapTextureManager;
 import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.render.WorldRenderer;
-import net.minecraft.util.math.Vec3f;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Direction;
 import net.minecraft.util.math.Matrix3f;
 import net.minecraft.util.math.Matrix4f;
+import net.minecraft.util.math.Vec3f;
 
 import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
 import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
@@ -42,8 +43,6 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
  * Has most of the actual buffer-time lighting and coloring logic.
  */
 public abstract class AbstractQuadRenderer {
-	static final int FULL_BRIGHTNESS = 0xF000F0;
-
 	protected final Function<RenderLayer, VertexConsumer> bufferFunc;
 	protected final BlockRenderInfo blockInfo;
 	protected final AoCalculator aoCalc;
@@ -115,7 +114,7 @@ public abstract class AbstractQuadRenderer {
 	// routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop
 
 	/** for non-emissive mesh quads and all fallback quads with smooth lighting. */
-	protected void tesselateSmooth(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
+	protected void tessellateSmooth(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
 		colorizeQuad(q, blockColorIndex);
 
 		for (int i = 0; i < 4; i++) {
@@ -127,19 +126,19 @@ public abstract class AbstractQuadRenderer {
 	}
 
 	/** for emissive mesh quads with smooth lighting. */
-	protected void tesselateSmoothEmissive(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
+	protected void tessellateSmoothEmissive(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
 		colorizeQuad(q, blockColorIndex);
 
 		for (int i = 0; i < 4; i++) {
 			q.spriteColor(i, 0, ColorHelper.multiplyRGB(q.spriteColor(i, 0), aoCalc.ao[i]));
-			q.lightmap(i, FULL_BRIGHTNESS);
+			q.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
 		}
 
 		bufferQuad(q, renderLayer);
 	}
 
 	/** for non-emissive mesh quads and all fallback quads with flat lighting. */
-	protected void tesselateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
+	protected void tessellateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
 		colorizeQuad(quad, blockColorIndex);
 		shadeFlatQuad(quad);
 
@@ -153,12 +152,12 @@ public abstract class AbstractQuadRenderer {
 	}
 
 	/** for emissive mesh quads with flat lighting. */
-	protected void tesselateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
+	protected void tessellateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
 		colorizeQuad(quad, blockColorIndex);
 		shadeFlatQuad(quad);
 
 		for (int i = 0; i < 4; i++) {
-			quad.lightmap(i, FULL_BRIGHTNESS);
+			quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
 		}
 
 		bufferQuad(quad, renderLayer);
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
index 13b2625f1..145a97b26 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
@@ -18,19 +18,17 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
 
 import it.unimi.dsi.fastutil.objects.ObjectArrayList;
 
-import net.minecraft.util.math.Matrix4f;
 import net.minecraft.util.math.Matrix3f;
+import net.minecraft.util.math.Matrix4f;
 
 import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
 import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
 
 abstract class AbstractRenderContext implements RenderContext {
-	private final ObjectArrayList<QuadTransform> transformStack = new ObjectArrayList<>();
 	private static final QuadTransform NO_TRANSFORM = (q) -> true;
-	protected Matrix4f matrix;
-	protected Matrix3f normalMatrix;
-	protected int overlay;
 
+	private QuadTransform activeTransform = NO_TRANSFORM;
+	private final ObjectArrayList<QuadTransform> transformStack = new ObjectArrayList<>();
 	private final QuadTransform stackTransform = (q) -> {
 		int i = transformStack.size() - 1;
 
@@ -43,7 +41,9 @@ abstract class AbstractRenderContext implements RenderContext {
 		return true;
 	};
 
-	private QuadTransform activeTransform = NO_TRANSFORM;
+	protected Matrix4f matrix;
+	protected Matrix3f normalMatrix;
+	protected int overlay;
 
 	protected final boolean transform(MutableQuadView q) {
 		return activeTransform.transform(q);
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderContext.java
index a225ca584..896a32cd9 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderContext.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/BlockRenderContext.java
@@ -17,10 +17,10 @@
 package net.fabricmc.fabric.impl.client.indigo.renderer.render;
 
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.Supplier;
 
 import net.minecraft.block.BlockState;
+import net.minecraft.client.render.LightmapTextureManager;
 import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.render.WorldRenderer;
@@ -35,20 +35,19 @@ import net.minecraft.util.math.random.AbstractRandom;
 import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
 import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
-import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
 import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
 import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix;
 
 /**
  * Context for non-terrain block rendering.
  */
-public class BlockRenderContext extends AbstractRenderContext implements RenderContext {
+public class BlockRenderContext extends AbstractRenderContext {
 	private final BlockRenderInfo blockInfo = new BlockRenderInfo();
 	private final AoCalculator aoCalc = new AoCalculator(blockInfo, this::brightness, this::aoLevel);
-	private final MeshConsumer meshConsumer = new MeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform);
+
 	private VertexConsumer bufferBuilder;
 	private boolean didOutput = false;
-	// These are kept as fields to avoid avoid the heap allocation for a supplier.
+	// These are kept as fields to avoid the heap allocation for a supplier.
 	// BlockModelRenderer allows the caller to supply both the random object and seed.
 	private AbstractRandom random;
 	private long seed;
@@ -57,16 +56,7 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
 		return random;
 	};
 
-	/**
-	 * Reuse the fallback consumer from the render context used during chunk rebuild to make it properly
-	 * apply the current transforms to vanilla models.
-	 */
-	private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
-		@Override
-		protected int overlay() {
-			return overlay;
-		}
-
+	private final AbstractMeshConsumer meshConsumer = new AbstractMeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
 		@Override
 		protected Matrix4f matrix() {
 			return matrix;
@@ -76,11 +66,37 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
 		protected Matrix3f normalMatrix() {
 			return normalMatrix;
 		}
+
+		@Override
+		protected int overlay() {
+			return overlay;
+		}
+	};
+
+	/**
+	 * Reuse the fallback consumer from the render context used during chunk rebuild to make it properly
+	 * apply the current transforms to vanilla models.
+	 */
+	private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
+		@Override
+		protected Matrix4f matrix() {
+			return matrix;
+		}
+
+		@Override
+		protected Matrix3f normalMatrix() {
+			return normalMatrix;
+		}
+
+		@Override
+		protected int overlay() {
+			return overlay;
+		}
 	};
 
 	private int brightness(BlockPos pos) {
 		if (blockInfo.blockView == null) {
-			return 15 << 20 | 15 << 4;
+			return LightmapTextureManager.MAX_LIGHT_COORDINATE;
 		}
 
 		return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockView.getBlockState(pos), pos);
@@ -119,27 +135,6 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
 		return didOutput;
 	}
 
-	private class MeshConsumer extends AbstractMeshConsumer {
-		MeshConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
-			super(blockInfo, bufferFunc, aoCalc, transform);
-		}
-
-		@Override
-		protected Matrix4f matrix() {
-			return matrix;
-		}
-
-		@Override
-		protected Matrix3f normalMatrix() {
-			return normalMatrix;
-		}
-
-		@Override
-		protected int overlay() {
-			return overlay;
-		}
-	}
-
 	@Override
 	public Consumer<Mesh> meshConsumer() {
 		return meshConsumer;
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ChunkRenderInfo.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ChunkRenderInfo.java
index fe1b80426..2f4ea7615 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ChunkRenderInfo.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ChunkRenderInfo.java
@@ -25,8 +25,8 @@ import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.WorldRenderer;
 import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
-import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
 import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk;
+import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
 import net.minecraft.client.render.chunk.ChunkRendererRegion;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.BlockRenderView;
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/CompatibilityHelper.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/CompatibilityHelper.java
deleted file mode 100644
index 1c275603d..000000000
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/CompatibilityHelper.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.impl.client.indigo.renderer.render;
-
-import net.fabricmc.fabric.impl.client.indigo.Indigo;
-import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
-
-/**
- * Controls 1x warning for vanilla quad vertex format when running in compatibility mode.
- */
-public abstract class CompatibilityHelper {
-	private CompatibilityHelper() { }
-
-	private static boolean logCompatibilityWarning = true;
-
-	private static boolean isCompatible(int[] vertexData) {
-		final boolean result = vertexData.length == EncodingFormat.QUAD_STRIDE;
-
-		if (!result && logCompatibilityWarning) {
-			logCompatibilityWarning = false;
-			Indigo.LOGGER.warn("[Indigo] Encountered baked quad with non-standard vertex format. Some blocks will not be rendered");
-		}
-
-		return result;
-	}
-
-	public static boolean canRender(int[] vertexData) {
-		return !Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY || isCompatible(vertexData);
-	}
-}
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
index 2e51e4948..9410087d5 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
@@ -21,7 +21,9 @@ import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 import net.minecraft.block.BlockState;
+import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.color.item.ItemColors;
+import net.minecraft.client.render.LightmapTextureManager;
 import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.RenderLayers;
 import net.minecraft.client.render.TexturedRenderLayers;
@@ -30,12 +32,12 @@ import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.item.ItemRenderer;
 import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.render.model.BakedQuad;
-import net.minecraft.client.render.model.json.ModelTransformation;
 import net.minecraft.client.render.model.json.ModelTransformation.Mode;
 import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.BlockItem;
+import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.math.Direction;
-import net.minecraft.util.math.Matrix4f;
 import net.minecraft.util.math.Vec3f;
 import net.minecraft.util.math.random.AbstractRandom;
 
@@ -44,7 +46,6 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
 import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
 import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
-import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
 import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
 import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
 import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
@@ -54,10 +55,8 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
 
 /**
  * The render context used for item rendering.
- * Does not implement emissive lighting for sake
- * of simplicity in the default renderer.
  */
-public class ItemRenderContext extends AbstractRenderContext implements RenderContext {
+public class ItemRenderContext extends AbstractRenderContext {
 	/** Value vanilla uses for item rendering.  The only sensible choice, of course.  */
 	private static final long ITEM_RANDOM_SEED = 42L;
 
@@ -69,127 +68,82 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
 
 	private final ItemColors colorMap;
 	private final AbstractRandom random = AbstractRandom.createAtomic();
-	private final Consumer<BakedModel> fallbackConsumer;
 	private final Vec3f normalVec = new Vec3f();
 
-	private MatrixStack matrixStack;
-	private Matrix4f matrix;
-	private VertexConsumerProvider vertexConsumerProvider;
-	private VertexConsumer modelVertexConsumer;
-	private BlendMode quadBlendMode;
-	private VertexConsumer quadVertexConsumer;
-	private Mode transformMode;
-	private int lightmap;
-	private int overlay;
-	private ItemStack itemStack;
-	private VanillaQuadHandler vanillaHandler;
-
 	private final Supplier<AbstractRandom> randomSupplier = () -> {
-		final AbstractRandom result = random;
-		result.setSeed(ITEM_RANDOM_SEED);
+		random.setSeed(ITEM_RANDOM_SEED);
 		return random;
 	};
 
-	private final int[] quadData = new int[EncodingFormat.TOTAL_STRIDE];
+	private final Maker editorQuad = new Maker();
+	private final MeshConsumer meshConsumer = new MeshConsumer();
+	private final FallbackConsumer fallbackConsumer = new FallbackConsumer();
+
+	private ItemStack itemStack;
+	private Mode transformMode;
+	private MatrixStack matrixStack;
+	private VertexConsumerProvider vertexConsumerProvider;
+	private int lightmap;
+	private VanillaQuadHandler vanillaHandler;
+
+	private boolean isDefaultTranslucent;
+	private boolean isTranslucentDirect;
+	private VertexConsumer translucentVertexConsumer;
+	private VertexConsumer cutoutVertexConsumer;
+	private VertexConsumer modelVertexConsumer;
 
 	public ItemRenderContext(ItemColors colorMap) {
 		this.colorMap = colorMap;
-		fallbackConsumer = this::fallbackConsumer;
 	}
 
-	public void renderModel(ItemStack itemStack, Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, FabricBakedModel model, VanillaQuadHandler vanillaHandler) {
+	public void renderModel(ItemStack itemStack, Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model, VanillaQuadHandler vanillaHandler) {
+		this.itemStack = itemStack;
+		this.transformMode = transformMode;
+		this.matrixStack = matrixStack;
+		this.vertexConsumerProvider = vertexConsumerProvider;
 		this.lightmap = lightmap;
 		this.overlay = overlay;
-		this.itemStack = itemStack;
-		this.vertexConsumerProvider = vertexConsumerProvider;
-		this.matrixStack = matrixStack;
-		this.transformMode = transformMode;
 		this.vanillaHandler = vanillaHandler;
-		quadBlendMode = BlendMode.DEFAULT;
-		modelVertexConsumer = selectVertexConsumer(RenderLayers.getItemLayer(itemStack, transformMode != ModelTransformation.Mode.GROUND));
+		computeOutputInfo();
 
 		matrixStack.push();
-		((BakedModel) model).getTransformation().getTransformation(transformMode).apply(invert, matrixStack);
+		model.getTransformation().getTransformation(transformMode).apply(invert, matrixStack);
 		matrixStack.translate(-0.5D, -0.5D, -0.5D);
 		matrix = matrixStack.peek().getPositionMatrix();
 		normalMatrix = matrixStack.peek().getNormalMatrix();
 
-		model.emitItemQuads(itemStack, randomSupplier, this);
+		((FabricBakedModel) model).emitItemQuads(itemStack, randomSupplier, this);
 
 		matrixStack.pop();
 
-		this.matrixStack = null;
 		this.itemStack = null;
+		this.matrixStack = null;
 		this.vanillaHandler = null;
+		translucentVertexConsumer = null;
+		cutoutVertexConsumer = null;
 		modelVertexConsumer = null;
 	}
 
-	/**
-	 * Use non-culling translucent material in GUI to match vanilla behavior. If the item
-	 * is enchanted then also select a dual-output vertex consumer. For models with layered
-	 * coplanar polygons this means we will render the glint more than once. Indigo doesn't
-	 * support sprite layers, so this can't be helped in this implementation.
-	 */
-	private VertexConsumer selectVertexConsumer(RenderLayer layerIn) {
-		final RenderLayer layer = transformMode == ModelTransformation.Mode.GUI ? TexturedRenderLayers.getEntityTranslucentCull() : layerIn;
-		return ItemRenderer.getArmorGlintConsumer(vertexConsumerProvider, layer, true, itemStack.hasGlint());
-	}
+	private void computeOutputInfo() {
+		isDefaultTranslucent = true;
+		isTranslucentDirect = true;
 
-	private class Maker extends MutableQuadViewImpl implements QuadEmitter {
-		{
-			data = quadData;
-			clear();
+		Item item = itemStack.getItem();
+
+		if (item instanceof BlockItem blockItem) {
+			BlockState state = blockItem.getBlock().getDefaultState();
+			RenderLayer renderLayer = RenderLayers.getBlockLayer(state);
+
+			if (renderLayer != RenderLayer.getTranslucent()) {
+				isDefaultTranslucent = false;
+			}
+
+			if (transformMode != Mode.GUI && !transformMode.isFirstPerson()) {
+				isTranslucentDirect = false;
+			}
 		}
 
-		@Override
-		public Maker emit() {
-			computeGeometry();
-			renderQuad();
-			clear();
-			return this;
-		}
-	}
-
-	private final Maker editorQuad = new Maker();
-
-	private final Consumer<Mesh> meshConsumer = (mesh) -> {
-		final MeshImpl m = (MeshImpl) mesh;
-		final int[] data = m.data();
-		final int limit = data.length;
-		int index = 0;
-
-		while (index < limit) {
-			System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
-			editorQuad.load();
-			index += EncodingFormat.TOTAL_STRIDE;
-			renderQuad();
-		}
-	};
-
-	private int indexColor() {
-		final int colorIndex = editorQuad.colorIndex();
-		return colorIndex == -1 ? -1 : (colorMap.getColor(itemStack, colorIndex) | 0xFF000000);
-	}
-
-	private void renderQuad() {
-		final MutableQuadViewImpl quad = editorQuad;
-
-		if (!transform(editorQuad)) {
-			return;
-		}
-
-		final RenderMaterialImpl.Value mat = quad.material();
-		final int quadColor = mat.disableColorIndex(0) ? -1 : indexColor();
-		final int lightmap = mat.emissive(0) ? AbstractQuadRenderer.FULL_BRIGHTNESS : this.lightmap;
-
-		for (int i = 0; i < 4; i++) {
-			int c = quad.spriteColor(i, 0);
-			c = ColorHelper.multiplyColor(quadColor, c);
-			quad.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(c));
-			quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
-		}
-
-		AbstractQuadRenderer.bufferQuad(quadVertexConsumer(mat.blendMode(0)), quad, matrix, overlay, normalMatrix, normalVec);
+		modelVertexConsumer = quadVertexConsumer(BlendMode.DEFAULT);
 	}
 
 	/**
@@ -198,25 +152,157 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
 	 * translucent are mapped to cutout.
 	 */
 	private VertexConsumer quadVertexConsumer(BlendMode blendMode) {
+		boolean translucent;
+
 		if (blendMode == BlendMode.DEFAULT) {
-			return modelVertexConsumer;
-		}
-
-		if (blendMode != BlendMode.TRANSLUCENT) {
-			blendMode = BlendMode.CUTOUT;
-		}
-
-		if (blendMode == quadBlendMode) {
-			return quadVertexConsumer;
-		} else if (blendMode == BlendMode.TRANSLUCENT) {
-			quadVertexConsumer = selectVertexConsumer(TexturedRenderLayers.getEntityTranslucentCull());
-			quadBlendMode = BlendMode.TRANSLUCENT;
+			translucent = isDefaultTranslucent;
 		} else {
-			quadVertexConsumer = selectVertexConsumer(TexturedRenderLayers.getEntityCutout());
-			quadBlendMode = BlendMode.CUTOUT;
+			translucent = blendMode == BlendMode.TRANSLUCENT;
 		}
 
-		return quadVertexConsumer;
+		if (translucent) {
+			if (translucentVertexConsumer == null) {
+				if (isTranslucentDirect) {
+					translucentVertexConsumer = ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, itemStack.hasGlint());
+				} else if (MinecraftClient.isFabulousGraphicsOrBetter()) {
+					translucentVertexConsumer = ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getItemEntityTranslucentCull(), true, itemStack.hasGlint());
+				} else {
+					translucentVertexConsumer = ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, itemStack.hasGlint());
+				}
+			}
+
+			return translucentVertexConsumer;
+		} else {
+			if (cutoutVertexConsumer == null) {
+				cutoutVertexConsumer = ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityCutout(), true, itemStack.hasGlint());
+			}
+
+			return cutoutVertexConsumer;
+		}
+	}
+
+	private void bufferQuad(MutableQuadViewImpl quad, BlendMode blendMode) {
+		AbstractQuadRenderer.bufferQuad(quadVertexConsumer(blendMode), quad, matrix, overlay, normalMatrix, normalVec);
+	}
+
+	private void colorizeQuad(MutableQuadViewImpl q, int colorIndex) {
+		if (colorIndex == -1) {
+			for (int i = 0; i < 4; i++) {
+				q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(q.spriteColor(i, 0)));
+			}
+		} else {
+			final int itemColor = 0xFF000000 | colorMap.getColor(itemStack, colorIndex);
+
+			for (int i = 0; i < 4; i++) {
+				q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(itemColor, q.spriteColor(i, 0))));
+			}
+		}
+	}
+
+	private void renderQuad(MutableQuadViewImpl quad, BlendMode blendMode, int colorIndex) {
+		colorizeQuad(quad, colorIndex);
+
+		final int lightmap = this.lightmap;
+
+		for (int i = 0; i < 4; i++) {
+			quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
+		}
+
+		bufferQuad(quad, blendMode);
+	}
+
+	private void renderQuadEmissive(MutableQuadViewImpl quad, BlendMode blendMode, int colorIndex) {
+		colorizeQuad(quad, colorIndex);
+
+		for (int i = 0; i < 4; i++) {
+			quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
+		}
+
+		bufferQuad(quad, blendMode);
+	}
+
+	private void renderMeshQuad(MutableQuadViewImpl quad) {
+		if (!transform(quad)) {
+			return;
+		}
+
+		final RenderMaterialImpl.Value mat = quad.material();
+
+		final int colorIndex = mat.disableColorIndex(0) ? -1 : quad.colorIndex();
+		final BlendMode blendMode = mat.blendMode(0);
+
+		if (mat.emissive(0)) {
+			renderQuadEmissive(quad, blendMode, colorIndex);
+		} else {
+			renderQuad(quad, blendMode, colorIndex);
+		}
+	}
+
+	private class Maker extends MutableQuadViewImpl implements QuadEmitter {
+		{
+			data = new int[EncodingFormat.TOTAL_STRIDE];
+			clear();
+		}
+
+		@Override
+		public Maker emit() {
+			computeGeometry();
+			renderMeshQuad(this);
+			clear();
+			return this;
+		}
+	}
+
+	private class MeshConsumer implements Consumer<Mesh> {
+		@Override
+		public void accept(Mesh mesh) {
+			final MeshImpl m = (MeshImpl) mesh;
+			final int[] data = m.data();
+			final int limit = data.length;
+			int index = 0;
+
+			while (index < limit) {
+				System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
+				editorQuad.load();
+				index += EncodingFormat.TOTAL_STRIDE;
+				renderMeshQuad(editorQuad);
+			}
+		}
+	}
+
+	private class FallbackConsumer implements Consumer<BakedModel> {
+		@Override
+		public void accept(BakedModel model) {
+			if (hasTransform()) {
+				// if there's a transform in effect, convert to mesh-based quads so that we can apply it
+				for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
+					final Direction cullFace = ModelHelper.faceFromIndex(i);
+					random.setSeed(ITEM_RANDOM_SEED);
+					final List<BakedQuad> quads = model.getQuads(null, cullFace, random);
+					final int count = quads.size();
+
+					if (count != 0) {
+						for (int j = 0; j < count; j++) {
+							final BakedQuad q = quads.get(j);
+							renderQuadWithTransform(q, cullFace);
+						}
+					}
+				}
+			} else {
+				vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, modelVertexConsumer);
+			}
+		}
+
+		private void renderQuadWithTransform(BakedQuad quad, Direction cullFace) {
+			final Maker editorQuad = ItemRenderContext.this.editorQuad;
+			editorQuad.fromVanilla(quad, IndigoRenderer.MATERIAL_STANDARD, cullFace);
+
+			if (!transform(editorQuad)) {
+				return;
+			}
+
+			renderQuad(editorQuad, BlendMode.DEFAULT, editorQuad.colorIndex());
+		}
 	}
 
 	@Override
@@ -224,32 +310,6 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
 		return meshConsumer;
 	}
 
-	private void fallbackConsumer(BakedModel model) {
-		if (hasTransform()) {
-			// if there's a transform in effect, convert to mesh-based quads so that we can apply it
-			for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
-				random.setSeed(ITEM_RANDOM_SEED);
-				final Direction cullFace = ModelHelper.faceFromIndex(i);
-				renderFallbackWithTransform(model.getQuads((BlockState) null, cullFace, random), cullFace);
-			}
-		} else {
-			vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, modelVertexConsumer);
-		}
-	}
-
-	private void renderFallbackWithTransform(List<BakedQuad> quads, Direction cullFace) {
-		if (quads.isEmpty()) {
-			return;
-		}
-
-		final Maker editorQuad = this.editorQuad;
-
-		for (final BakedQuad q : quads) {
-			editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
-			renderQuad();
-		}
-	}
-
 	@Override
 	public Consumer<BakedModel> fallbackConsumer() {
 		return fallbackConsumer;
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainFallbackConsumer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainFallbackConsumer.java
index 520e2d9f7..720ef55e8 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainFallbackConsumer.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainFallbackConsumer.java
@@ -58,10 +58,8 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
  *  manipulating the data via NIO.
  */
 public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements Consumer<BakedModel> {
-	private static Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
-	private static Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
-
-	private final int[] editorBuffer = new int[EncodingFormat.TOTAL_STRIDE];
+	private static final Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
+	private static final Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
 
 	TerrainFallbackConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
 		super(blockInfo, bufferFunc, aoCalc, transform);
@@ -69,7 +67,7 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
 
 	private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
 		{
-			data = editorBuffer;
+			data = new int[EncodingFormat.TOTAL_STRIDE];
 			material(MATERIAL_SHADED);
 		}
 
@@ -86,36 +84,21 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
 		final Value defaultMaterial = blockInfo.defaultAo && model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
 		final BlockState blockState = blockInfo.blockState;
 
-		for (int i = 0; i < 6; i++) {
-			final Direction face = ModelHelper.faceFromIndex(i);
-			final List<BakedQuad> quads = model.getQuads(blockState, face, random.get());
+		for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
+			final Direction cullFace = ModelHelper.faceFromIndex(i);
+			final List<BakedQuad> quads = model.getQuads(blockState, cullFace, random.get());
 			final int count = quads.size();
 
 			if (count != 0) {
 				for (int j = 0; j < count; j++) {
 					final BakedQuad q = quads.get(j);
-					renderQuad(q, face, defaultMaterial);
+					renderQuad(q, cullFace, defaultMaterial);
 				}
 			}
 		}
-
-		final List<BakedQuad> quads = model.getQuads(blockState, null, random.get());
-		final int count = quads.size();
-
-		if (count != 0) {
-			for (int j = 0; j < count; j++) {
-				final BakedQuad q = quads.get(j);
-				renderQuad(q, null, defaultMaterial);
-			}
-		}
 	}
 
 	private void renderQuad(BakedQuad quad, Direction cullFace, Value defaultMaterial) {
-		// TODO: should remove in 1.17 cycle, was for OF compat only
-		if (!CompatibilityHelper.canRender(quad.getVertexData())) {
-			return;
-		}
-
 		final MutableQuadViewImpl editorQuad = this.editorQuad;
 		editorQuad.fromVanilla(quad, defaultMaterial, cullFace);
 
@@ -132,16 +115,16 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
 		if (!editorQuad.material().disableAo(0)) {
 			// needs to happen before offsets are applied
 			aoCalc.compute(editorQuad, true);
-			tesselateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
+			tessellateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
 		} else {
 			// Recomputing whether the quad has a light face is only needed if it doesn't also have a cull face,
 			// as in those cases, the cull face will always be used to offset the light sampling position
 			if (cullFace == null) {
-				// Can't rely on lazy computation in tesselateFlat() because needs to happen before offsets are applied
+				// Can't rely on lazy computation in tessellateFlat() because needs to happen before offsets are applied
 				editorQuad.geometryFlags();
 			}
 
-			tesselateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
+			tessellateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
 		}
 	}
 }
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainRenderContext.java
index d272dcee1..25dc07a23 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainRenderContext.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/TerrainRenderContext.java
@@ -20,17 +20,17 @@ import java.util.function.Consumer;
 
 import net.minecraft.block.BlockState;
 import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
-import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
 import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk;
+import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
 import net.minecraft.client.render.chunk.ChunkRendererRegion;
 import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.util.math.Matrix4f;
+import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.util.crash.CrashException;
 import net.minecraft.util.crash.CrashReport;
 import net.minecraft.util.crash.CrashReportSection;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Matrix3f;
-import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.Matrix4f;
 
 import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
@@ -43,8 +43,9 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
  * Dispatches calls from models during chunk rebuild to the appropriate consumer,
  * and holds/manages all of the state needed by them.
  */
-public class TerrainRenderContext extends AbstractRenderContext implements RenderContext {
+public class TerrainRenderContext extends AbstractRenderContext {
 	public static final ThreadLocal<TerrainRenderContext> POOL = ThreadLocal.withInitial(TerrainRenderContext::new);
+
 	private final TerrainBlockRenderInfo blockInfo = new TerrainBlockRenderInfo();
 	private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo();
 	private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel);
@@ -83,10 +84,9 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
 		}
 	};
 
-	public TerrainRenderContext prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkData chunkData, BlockBufferBuilderStorage builders) {
+	public void prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkData chunkData, BlockBufferBuilderStorage builders) {
 		blockInfo.setBlockView(blockView);
 		chunkInfo.prepare(blockView, chunkRenderer, chunkData, builders);
-		return this;
 	}
 
 	public void release() {
@@ -95,7 +95,7 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
 	}
 
 	/** Called from chunk renderer hook. */
-	public boolean tesselateBlock(BlockState blockState, BlockPos blockPos, final BakedModel model, MatrixStack matrixStack) {
+	public boolean tessellateBlock(BlockState blockState, BlockPos blockPos, final BakedModel model, MatrixStack matrixStack) {
 		this.matrix = matrixStack.peek().getPositionMatrix();
 		this.normalMatrix = matrixStack.peek().getNormalMatrix();
 
@@ -103,11 +103,11 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
 			aoCalc.clear();
 			blockInfo.prepareForBlock(blockState, blockPos, model.useAmbientOcclusion());
 			((FabricBakedModel) model).emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
-		} catch (Throwable var9) {
-			CrashReport crashReport_1 = CrashReport.create(var9, "Tesselating block in world - Indigo Renderer");
-			CrashReportSection crashReportElement_1 = crashReport_1.addElement("Block being tesselated");
-			CrashReportSection.addBlockInfo(crashReportElement_1, chunkInfo.blockView, blockPos, blockState);
-			throw new CrashException(crashReport_1);
+		} catch (Throwable throwable) {
+			CrashReport crashReport = CrashReport.create(throwable, "Tessellating block in world - Indigo Renderer");
+			CrashReportSection crashReportSection = crashReport.addElement("Block being tessellated");
+			CrashReportSection.addBlockInfo(crashReportSection, chunkInfo.blockView, blockPos, blockState);
+			throw new CrashException(crashReport);
 		}
 
 		// false because we've already marked the chunk as populated - caller doesn't need to
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinAmbientOcclusionCalculator.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinAmbientOcclusionCalculator.java
index 9233b7545..b893a1186 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinAmbientOcclusionCalculator.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinAmbientOcclusionCalculator.java
@@ -30,8 +30,10 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessAmbientOcc
 
 @Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator")
 public abstract class MixinAmbientOcclusionCalculator implements AccessAmbientOcclusionCalculator {
-	@Shadow private float[] brightness;
-	@Shadow private int[] light;
+	@Shadow
+	private float[] brightness;
+	@Shadow
+	private int[] light;
 
 	@Shadow
 	public abstract void apply(BlockRenderView blockRenderView, BlockState blockState, BlockPos pos, Direction face, float[] aoData, BitSet controlBits, boolean shade);
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinBlockModelRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinBlockModelRenderer.java
index 316a12957..aaf4a3ba2 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinBlockModelRenderer.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinBlockModelRenderer.java
@@ -20,6 +20,7 @@ import java.util.BitSet;
 
 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;
@@ -42,15 +43,16 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext
 
 @Mixin(BlockModelRenderer.class)
 public abstract class MixinBlockModelRenderer implements AccessBlockModelRenderer {
+	@Unique
+	private final ThreadLocal<BlockRenderContext> fabric_contexts = ThreadLocal.withInitial(BlockRenderContext::new);
+
 	@Shadow
 	protected abstract void getQuadDimensions(BlockRenderView blockView, BlockState blockState, BlockPos blockPos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits);
 
-	private final ThreadLocal<BlockRenderContext> CONTEXTS = ThreadLocal.withInitial(BlockRenderContext::new);
-
 	@Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/AbstractRandom;JI)Z", cancellable = true)
 	private void hookRender(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrix, VertexConsumer buffer, boolean checkSides, AbstractRandom rand, long seed, int overlay, CallbackInfoReturnable<Boolean> ci) {
 		if (!((FabricBakedModel) model).isVanillaAdapter()) {
-			BlockRenderContext context = CONTEXTS.get();
+			BlockRenderContext context = fabric_contexts.get();
 			// Note that we do not support face-culling here (so checkSides is ignored)
 			ci.setReturnValue(context.render(blockView, model, state, pos, matrix, buffer, rand, seed, overlay));
 		}
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRebuildTask.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRebuildTask.java
index f05dfd465..278e4d8f7 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRebuildTask.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRebuildTask.java
@@ -28,7 +28,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 import net.minecraft.block.BlockRenderType;
 import net.minecraft.block.BlockState;
 import net.minecraft.block.entity.BlockEntity;
-import net.minecraft.client.render.BufferBuilder;
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.render.block.BlockRenderManager;
 import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
@@ -39,8 +38,8 @@ import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Vec3d;
-import net.minecraft.world.BlockRenderView;
 import net.minecraft.util.math.random.AbstractRandom;
+import net.minecraft.world.BlockRenderView;
 
 import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
 import net.fabricmc.fabric.impl.client.indigo.Indigo;
@@ -64,14 +63,14 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderConte
  * (Though they can use these as an example if they wish.)
  */
 @Mixin(targets = "net.minecraft.client.render.chunk.ChunkBuilder$BuiltChunk$RebuildTask")
-public class MixinChunkRebuildTask {
+public abstract class MixinChunkRebuildTask {
 	@Shadow
 	protected ChunkRendererRegion region;
 	@Shadow
 	protected BuiltChunk field_20839;
 
 	@Inject(at = @At("HEAD"), method = "Lnet/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask;render(FFFLnet/minecraft/client/render/chunk/ChunkBuilder$ChunkData;Lnet/minecraft/client/render/chunk/BlockBufferBuilderStorage;)Ljava/util/Set;")
-	private void hookChunkBuild(float float_1, float float_2, float float_3, ChunkBuilder.ChunkData renderData, BlockBufferBuilderStorage builder, CallbackInfoReturnable<Set<BlockEntity>> ci) {
+	private void hookChunkBuild(float cameraX, float cameraY, float cameraZ, ChunkBuilder.ChunkData renderData, BlockBufferBuilderStorage builder, CallbackInfoReturnable<Set<BlockEntity>> ci) {
 		ChunkRendererRegion region = this.region;
 
 		if (region != null) {
@@ -85,14 +84,14 @@ public class MixinChunkRebuildTask {
 	 * This is the hook that actually implements the rendering API for terrain rendering.
 	 *
 	 * <p>It's unusual to have a @Redirect in a Fabric library, but in this case
-	 * it is our explicit intention that {@link BlockRenderManager#tesselateBlock(BlockState, BlockPos, BlockRenderView, BufferBuilder, AbstractRandom)}
+	 * it is our explicit intention that {@link BlockRenderManager#renderBlock(BlockState, BlockPos, BlockRenderView, MatrixStack, VertexConsumer, boolean, AbstractRandom)}
 	 * does not execute for models that will be rendered by our renderer.
 	 *
 	 * <p>Any mod that wants to redirect this specific call is likely also a renderer, in which case this
 	 * renderer should not be present, or the mod should probably instead be relying on the renderer API
 	 * which was specifically created to provide for enhanced terrain rendering.
 	 *
-	 * <p>Note also that {@link BlockRenderManager#tesselateBlock(BlockState, BlockPos, BlockRenderView, BufferBuilder, AbstractRandom)}
+	 * <p>Note also that {@link BlockRenderManager#renderBlock(BlockState, BlockPos, BlockRenderView, MatrixStack, VertexConsumer, boolean, AbstractRandom)}
 	 * IS called if the block render type is something other than {@link BlockRenderType#MODEL}.
 	 * Normally this does nothing but will allow mods to create rendering hooks that are
 	 * driven off of render type. (Not recommended or encouraged, but also not prevented.)
@@ -106,7 +105,7 @@ public class MixinChunkRebuildTask {
 			if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) {
 				Vec3d vec3d = blockState.getModelOffset(blockView, blockPos);
 				matrix.translate(vec3d.x, vec3d.y, vec3d.z);
-				return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tesselateBlock(blockState, blockPos, model, matrix);
+				return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tessellateBlock(blockState, blockPos, model, matrix);
 			}
 		}
 
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRenderData.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRenderData.java
index 383b54639..5e7e677ed 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRenderData.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRenderData.java
@@ -27,7 +27,7 @@ import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
 import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessChunkRendererData;
 
 @Mixin(ChunkData.class)
-public class MixinChunkRenderData implements AccessChunkRendererData {
+public abstract class MixinChunkRenderData implements AccessChunkRendererData {
 	@Shadow
 	private Set<RenderLayer> initializedLayers;
 	@Shadow
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRendererRegion.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRendererRegion.java
index ffebba643..7b973c71d 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRendererRegion.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinChunkRendererRegion.java
@@ -17,6 +17,7 @@
 package net.fabricmc.fabric.mixin.client.indigo.renderer;
 
 import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
 
 import net.minecraft.client.render.chunk.ChunkRendererRegion;
 
@@ -25,6 +26,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderConte
 
 @Mixin(ChunkRendererRegion.class)
 public abstract class MixinChunkRendererRegion implements AccessChunkRendererRegion {
+	@Unique
 	private TerrainRenderContext fabric_renderer;
 
 	@Override
diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinItemRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinItemRenderer.java
index 2bbb314bb..f88636795 100644
--- a/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinItemRenderer.java
+++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/mixin/client/indigo/renderer/MixinItemRenderer.java
@@ -18,6 +18,7 @@ package net.fabricmc.fabric.mixin.client.indigo.renderer;
 
 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;
@@ -32,29 +33,29 @@ import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.ItemStack;
 
 import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
-import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext;
-import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler;
 import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessItemRenderer;
 import net.fabricmc.fabric.impl.client.indigo.renderer.render.IndigoQuadHandler;
+import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext;
+import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler;
 
 @Mixin(ItemRenderer.class)
 public abstract class MixinItemRenderer implements AccessItemRenderer {
-	@Shadow
-	protected abstract void renderBakedItemModel(BakedModel model, ItemStack stack, int light, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
-
 	@Shadow
 	protected ItemColors colors;
 
-	private final VanillaQuadHandler vanillaHandler = new IndigoQuadHandler(this);
+	@Unique
+	private final ThreadLocal<ItemRenderContext> fabric_contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(colors));
 
-	private final ThreadLocal<ItemRenderContext> CONTEXTS = ThreadLocal.withInitial(() -> new ItemRenderContext(colors));
+	@Unique
+	private final VanillaQuadHandler fabric_vanillaHandler = new IndigoQuadHandler(this);
+
+	@Shadow
+	protected abstract void renderBakedItemModel(BakedModel model, ItemStack stack, int light, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
 
 	@Inject(at = @At("HEAD"), method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", cancellable = true)
-	public void hook_method_23179(ItemStack stack, ModelTransformation.Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) {
-		final FabricBakedModel fabricModel = (FabricBakedModel) model;
-
-		if (!(stack.isEmpty() || fabricModel.isVanillaAdapter())) {
-			CONTEXTS.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, fabricModel, vanillaHandler);
+	public void hook_renderItem(ItemStack stack, ModelTransformation.Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) {
+		if (!stack.isEmpty() && !((FabricBakedModel) model).isVanillaAdapter()) {
+			fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model, fabric_vanillaHandler);
 			ci.cancel();
 		}
 	}