diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBakedModel.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBakedModel.java
index 573fd91db..40b497d72 100644
--- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBakedModel.java
+++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBakedModel.java
@@ -29,6 +29,7 @@ import net.minecraft.util.math.random.Random;
 import net.minecraft.world.BlockRenderView;
 
 import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
+import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
 
 /**
  * Interface for baked models that output meshes with enhanced rendering features.
@@ -92,9 +93,8 @@ public interface FabricBakedModel {
 	 * Will not be thread-safe. Do not cache or retain a reference.
 	 * @param context Accepts model output.
 	 */
-	@SuppressWarnings("deprecation")
 	default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
-		context.bakedModelConsumer().accept((BakedModel) this, state);
+		VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context, context.getEmitter());
 	}
 
 	/**
@@ -124,9 +124,7 @@ public interface FabricBakedModel {
 	 * logic here, instead of returning every possible shape from {@link BakedModel#getOverrides}
 	 * as vanilla baked models.
 	 */
-	@SuppressWarnings("deprecation")
 	default void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
-		// Pass null state to enforce item quads in block render contexts
-		context.bakedModelConsumer().accept((BakedModel) this, null);
+		VanillaModelEncoder.emitItemQuads((BakedModel) this, null, randomSupplier, context);
 	}
 }
diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderContext.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderContext.java
index 41f8eab48..ab5c96b1d 100644
--- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderContext.java
+++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderContext.java
@@ -23,13 +23,16 @@ import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.block.BlockState;
 import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.json.ModelTransformationMode;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
 import net.minecraft.world.BlockRenderView;
 
 import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
 import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
+import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
 import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
 
 /**
@@ -54,6 +57,15 @@ public interface RenderContext {
 	 */
 	QuadEmitter getEmitter();
 
+	/**
+	 * Returns whether this context currently has at least one transform.
+	 *
+	 * @apiNote The default implementation will be removed in the next breaking release.
+	 */
+	default boolean hasTransform() {
+		return true;
+	}
+
 	/**
 	 * Causes all models/quads/meshes sent to this consumer to be transformed by the provided
 	 * {@link QuadTransform} that edits each quad before buffering. Quads in the mesh will
@@ -67,6 +79,8 @@ public interface RenderContext {
 	 *
 	 * <p>Meshes are never mutated by the transformer - only buffered quads. This ensures thread-safe
 	 * use of meshes/models across multiple chunk builders.
+	 *
+	 * <p>Using the {@linkplain #getEmitter() quad emitter of this context} from the inside of a quad transform is not supported.
 	 */
 	void pushTransform(QuadTransform transform);
 
@@ -76,6 +90,36 @@ public interface RenderContext {
 	 */
 	void popTransform();
 
+	/**
+	 * Returns {@code true} if the given face will be culled away.
+	 *
+	 * <p>This function can be used to skip complex transformations of quads that will be culled anyway.
+	 * The cull face of a quad is determined by {@link QuadView#cullFace()}.
+	 * Note that if {@linkplain #hasTransform() there is a transform}, no computation should be skipped,
+	 * because the cull face might be changed by the transform,
+	 * or the transform might wish to receive culled faces too.
+	 *
+	 * <p>This function can only be used on a block render context (i.e. in {@link FabricBakedModel#emitBlockQuads}).
+	 * Calling it on another context (e.g. in {@link FabricBakedModel#emitItemQuads}) will throw an exception.
+	 *
+	 * @apiNote The default implementation will be removed in the next breaking release.
+	 */
+	default boolean isFaceCulled(@Nullable Direction face) {
+		return false;
+	}
+
+	/**
+	 * Returns the current transformation mode.
+	 *
+	 * <p>This function can only be used on an item render context (i.e. in {@link FabricBakedModel#emitItemQuads}).
+	 * Calling it on another context (e.g. in {@link FabricBakedModel#emitBlockQuads}) will throw an exception.
+	 *
+	 * @apiNote The default implementation will be removed in the next breaking release.
+	 */
+	default ModelTransformationMode itemTransformationMode() {
+		return ModelTransformationMode.NONE;
+	}
+
 	@FunctionalInterface
 	interface QuadTransform {
 		/**
@@ -89,14 +133,16 @@ public interface RenderContext {
 	 * @deprecated Use {@link Mesh#outputTo(QuadEmitter)} instead.
 	 */
 	@Deprecated
-	Consumer<Mesh> meshConsumer();
+	default Consumer<Mesh> meshConsumer() {
+		return mesh -> mesh.outputTo(getEmitter());
+	}
 
 	/**
 	 * @deprecated Use {@link FabricBakedModel#emitBlockQuads(BlockRenderView, BlockState, BlockPos, Supplier, RenderContext) emitBlockQuads}
 	 * or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
 	 * that you want to consume instead.
 	 */
-	@Deprecated
+	@Deprecated(forRemoval = true)
 	BakedModelConsumer bakedModelConsumer();
 
 	/**
@@ -104,12 +150,12 @@ public interface RenderContext {
 	 * or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
 	 * that you want to consume instead.
 	 */
-	@Deprecated
+	@Deprecated(forRemoval = true)
 	default Consumer<BakedModel> fallbackConsumer() {
 		return bakedModelConsumer();
 	}
 
-	@Deprecated
+	@Deprecated(forRemoval = true)
 	interface BakedModelConsumer extends Consumer<BakedModel> {
 		/**
 		 * Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/VanillaModelEncoder.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/VanillaModelEncoder.java
new file mode 100644
index 000000000..af2a817ca
--- /dev/null
+++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/VanillaModelEncoder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.renderer;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.BakedQuad;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.random.Random;
+
+import net.fabricmc.fabric.api.renderer.v1.Renderer;
+import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
+import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
+import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
+import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
+import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
+import net.fabricmc.fabric.api.util.TriState;
+
+/**
+ * Routines for adaptation of vanilla {@link BakedModel}s to FRAPI pipelines.
+ * Even though Indigo calls them directly, they are not for use by third party renderers, and might change at any time.
+ */
+public class VanillaModelEncoder {
+	private static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer();
+	private static final RenderMaterial MATERIAL_STANDARD = RENDERER.materialFinder().find();
+	private static final RenderMaterial MATERIAL_NO_AO = RENDERER.materialFinder().ambientOcclusion(TriState.FALSE).find();
+
+	// Separate QuadEmitter parameter so that Indigo can pass its own emitter that handles vanilla quads differently.
+	public static void emitBlockQuads(BakedModel model, @Nullable BlockState state, Supplier<Random> randomSupplier, RenderContext context, QuadEmitter emitter) {
+		final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_STANDARD : MATERIAL_NO_AO;
+
+		for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
+			final Direction cullFace = ModelHelper.faceFromIndex(i);
+
+			if (!context.hasTransform() && context.isFaceCulled(cullFace)) {
+				// Skip entire quad list if possible.
+				continue;
+			}
+
+			final List<BakedQuad> quads = model.getQuads(state, cullFace, randomSupplier.get());
+			final int count = quads.size();
+
+			for (int j = 0; j < count; j++) {
+				final BakedQuad q = quads.get(j);
+				emitter.fromVanilla(q, defaultMaterial, cullFace);
+				emitter.emit();
+			}
+		}
+	}
+
+	public static void emitItemQuads(BakedModel model, @Nullable BlockState state, Supplier<Random> randomSupplier, RenderContext context) {
+		QuadEmitter emitter = context.getEmitter();
+
+		for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
+			final Direction cullFace = ModelHelper.faceFromIndex(i);
+			final List<BakedQuad> quads = model.getQuads(state, cullFace, randomSupplier.get());
+			final int count = quads.size();
+
+			for (int j = 0; j < count; j++) {
+				final BakedQuad q = quads.get(j);
+				emitter.fromVanilla(q, MATERIAL_STANDARD, cullFace);
+				emitter.emit();
+			}
+		}
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnUnbakedModel.java b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnUnbakedModel.java
index 7d63f22c9..6f564a6b7 100644
--- a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnUnbakedModel.java
+++ b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnUnbakedModel.java
@@ -30,6 +30,7 @@ import net.minecraft.client.texture.Sprite;
 import net.minecraft.client.texture.SpriteAtlasTexture;
 import net.minecraft.client.util.SpriteIdentifier;
 import net.minecraft.util.Identifier;
+import net.minecraft.util.math.Direction;
 
 import net.fabricmc.fabric.api.renderer.v1.Renderer;
 import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
@@ -79,6 +80,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0.5f, 1, 0.5f);
 		emitter.pos(2, 1, 1, A);
 		emitter.pos(3, B, 1, 0);
+		emitter.cullFace(Direction.UP);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -87,6 +89,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0, 1, B);
 		emitter.pos(2, 0.5f, 1, 0.5f);
 		emitter.pos(3, A, 1, 0);
+		emitter.cullFace(Direction.UP);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -95,6 +98,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, A, 1, 1);
 		emitter.pos(2, B, 1, 1);
 		emitter.pos(3, 0.5f, 1, 0.5f);
+		emitter.cullFace(Direction.UP);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -103,6 +107,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, B, 1, 1);
 		emitter.pos(2, 1, 1, B);
 		emitter.pos(3, 1, 1, A);
+		emitter.cullFace(Direction.UP);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -113,6 +118,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0.5f, 0, 0.5f);
 		emitter.pos(2, 1, 0, B);
 		emitter.pos(3, B, 0, 1);
+		emitter.cullFace(Direction.DOWN);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -121,6 +127,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0, 0, A);
 		emitter.pos(2, 0.5f, 0, 0.5f);
 		emitter.pos(3, A, 0, 1);
+		emitter.cullFace(Direction.DOWN);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -129,6 +136,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, A, 0, 0);
 		emitter.pos(2, B, 0, 0);
 		emitter.pos(3, 0.5f, 0, 0.5f);
+		emitter.cullFace(Direction.DOWN);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -137,6 +145,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, B, 0, 0);
 		emitter.pos(2, 1, 0, A);
 		emitter.pos(3, 1, 0, B);
+		emitter.cullFace(Direction.DOWN);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -146,6 +155,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, B, 0, 0);
 		emitter.pos(2, A, 0, 0);
 		emitter.pos(3, A, 1, 0);
+		emitter.cullFace(Direction.NORTH);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
@@ -156,7 +166,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, A, 0, 0);
 		emitter.pos(2, 0, 0, A);
 		emitter.pos(3, 0, 1, A);
-		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
+		cornerSprite(emitter, whiteConcreteSprite);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -166,6 +176,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0, 0, A);
 		emitter.pos(2, 0, 0, B);
 		emitter.pos(3, 0, 1, B);
+		emitter.cullFace(Direction.WEST);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
@@ -176,7 +187,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 0, 0, B);
 		emitter.pos(2, A, 0, 1);
 		emitter.pos(3, A, 1, 1);
-		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
+		cornerSprite(emitter, whiteConcreteSprite);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -186,6 +197,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, A, 0, 1);
 		emitter.pos(2, B, 0, 1);
 		emitter.pos(3, B, 1, 1);
+		emitter.cullFace(Direction.SOUTH);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
@@ -196,7 +208,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, B, 0, 1);
 		emitter.pos(2, 1, 0, B);
 		emitter.pos(3, 1, 1, B);
-		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
+		cornerSprite(emitter, whiteConcreteSprite);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
@@ -206,6 +218,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 1, 0, B);
 		emitter.pos(2, 1, 0, A);
 		emitter.pos(3, 1, 1, A);
+		emitter.cullFace(Direction.EAST);
 		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
@@ -216,11 +229,21 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
 		emitter.pos(1, 1, 0, A);
 		emitter.pos(2, B, 0, 0);
 		emitter.pos(3, B, 1, 0);
-		emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
+		cornerSprite(emitter, whiteConcreteSprite);
 		emitter.material(glintMaterial);
 		emitter.color(-1, -1, -1, -1);
 		emitter.emit();
 
 		return new SingleMeshBakedModel(builder.build(), whiteConcreteSprite);
 	}
+
+	private static void cornerSprite(QuadEmitter emitter, Sprite sprite) {
+		// Assign uvs for a corner face in such a way that the texture is not stretched, using coordinates in [0, 1].
+		emitter.uv(0, A, 0);
+		emitter.uv(1, A, 1);
+		emitter.uv(2, B, 1);
+		emitter.uv(3, B, 0);
+		// Map [0, 1] coordinates to sprite atlas coordinates. spriteBake assumes [0, 16] unless we pass the BAKE_NORMALIZED flag.
+		emitter.spriteBake(sprite, MutableQuadView.BAKE_NORMALIZED);
+	}
 }
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext.java
index 610549e16..38659bb2f 100644
--- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext.java
+++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext.java
@@ -19,8 +19,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
 import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
 import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
 
-import java.util.List;
-
 import org.jetbrains.annotations.Nullable;
 import org.joml.Vector3f;
 
@@ -30,21 +28,20 @@ import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.render.WorldRenderer;
 import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.BakedQuad;
+import net.minecraft.client.render.model.json.ModelTransformationMode;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Direction;
 
 import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
-import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
 import net.fabricmc.fabric.api.util.TriState;
 import net.fabricmc.fabric.impl.client.indigo.Indigo;
-import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
 import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
 import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoConfig;
 import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
 import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
 import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
+import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
 
 public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 	protected final BlockRenderInfo blockInfo = new BlockRenderInfo();
@@ -61,6 +58,17 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 			renderQuad(this, false);
 		}
 	};
+	private final MutableQuadViewImpl vanillaModelEditorQuad = new MutableQuadViewImpl() {
+		{
+			data = new int[EncodingFormat.TOTAL_STRIDE];
+			clear();
+		}
+
+		@Override
+		public void emitDirectly() {
+			renderQuad(this, true);
+		}
+	};
 
 	private final BakedModelConsumerImpl vanillaModelConsumer = new BakedModelConsumerImpl();
 
@@ -80,6 +88,21 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 		return editorQuad;
 	}
 
+	public QuadEmitter getVanillaModelEmitter() {
+		// Do not clear the editorQuad since it is not accessible to API users.
+		return vanillaModelEditorQuad;
+	}
+
+	@Override
+	public boolean isFaceCulled(@Nullable Direction face) {
+		return !blockInfo.shouldDrawFace(face);
+	}
+
+	@Override
+	public ModelTransformationMode itemTransformationMode() {
+		throw new IllegalStateException("itemTransformationMode() can only be called on an item render context.");
+	}
+
 	@Override
 	public BakedModelConsumer bakedModelConsumer() {
 		return vanillaModelConsumer;
@@ -90,7 +113,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 			return;
 		}
 
-		if (!blockInfo.shouldDrawFace(quad.cullFace())) {
+		if (isFaceCulled(quad.cullFace())) {
 			return;
 		}
 
@@ -275,21 +298,6 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 	 * them through vanilla logic would require additional hooks.
 	 */
 	private class BakedModelConsumerImpl implements BakedModelConsumer {
-		private static final RenderMaterial MATERIAL_SHADED = IndigoRenderer.INSTANCE.materialFinder().find();
-		private static final RenderMaterial MATERIAL_FLAT = IndigoRenderer.INSTANCE.materialFinder().ambientOcclusion(TriState.FALSE).find();
-
-		private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
-			{
-				data = new int[EncodingFormat.TOTAL_STRIDE];
-				clear();
-			}
-
-			@Override
-			public void emitDirectly() {
-				renderQuad(this, true);
-			}
-		};
-
 		@Override
 		public void accept(BakedModel model) {
 			accept(model, blockInfo.blockState);
@@ -297,23 +305,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
 
 		@Override
 		public void accept(BakedModel model, @Nullable BlockState state) {
-			MutableQuadViewImpl editorQuad = this.editorQuad;
-			final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
-
-			for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
-				final Direction cullFace = ModelHelper.faceFromIndex(i);
-				final List<BakedQuad> quads = model.getQuads(state, cullFace, blockInfo.randomSupplier.get());
-				final int count = quads.size();
-
-				for (int j = 0; j < count; j++) {
-					final BakedQuad q = quads.get(j);
-					editorQuad.fromVanilla(q, defaultMaterial, cullFace);
-					// Call renderQuad directly instead of emit for efficiency
-					renderQuad(editorQuad, true);
-				}
-			}
-
-			// Do not clear the editorQuad since it is not accessible to API users.
+			VanillaModelEncoder.emitBlockQuads(model, state, blockInfo.randomSupplier, AbstractBlockRenderContext.this, vanillaModelEditorQuad);
 		}
 	}
 }
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
index 967e6e867..81969f7a4 100644
--- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
+++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractRenderContext.java
@@ -61,7 +61,8 @@ abstract class AbstractRenderContext implements RenderContext {
 		return activeTransform.transform(q);
 	}
 
-	protected boolean hasTransform() {
+	@Override
+	public boolean hasTransform() {
 		return activeTransform != NO_TRANSFORM;
 	}
 
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/IndigoQuadHandler.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/IndigoQuadHandler.java
deleted file mode 100644
index 9ecc8e4a7..000000000
--- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/IndigoQuadHandler.java
+++ /dev/null
@@ -1,36 +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.minecraft.client.render.VertexConsumer;
-import net.minecraft.client.render.item.ItemRenderer;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.item.ItemStack;
-
-public class IndigoQuadHandler implements ItemRenderContext.VanillaQuadHandler {
-	private final ItemRenderer itemRenderer;
-
-	public IndigoQuadHandler(ItemRenderer itemRenderer) {
-		this.itemRenderer = itemRenderer;
-	}
-
-	@Override
-	public void accept(BakedModel model, ItemStack stack, int color, int overlay, MatrixStack matrixStack, VertexConsumer buffer) {
-		itemRenderer.renderBakedItemModel(model, stack, color, overlay, matrixStack, buffer);
-	}
-}
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
index f42d5c7a4..6badb4ce3 100644
--- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
+++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java
@@ -16,7 +16,6 @@
 
 package net.fabricmc.fabric.impl.client.indigo.renderer.render;
 
-import java.util.List;
 import java.util.function.Supplier;
 
 import org.jetbrains.annotations.Nullable;
@@ -32,7 +31,6 @@ import net.minecraft.client.render.VertexConsumer;
 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.ModelTransformationMode;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.BlockItem;
@@ -44,12 +42,11 @@ import net.minecraft.util.math.random.Random;
 import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
 import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
 import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
-import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
 import net.fabricmc.fabric.api.util.TriState;
-import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
 import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
 import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
 import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
+import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
 
 /**
  * The render context used for item rendering.
@@ -84,7 +81,6 @@ public class ItemRenderContext extends AbstractRenderContext {
 	private MatrixStack matrixStack;
 	private VertexConsumerProvider vertexConsumerProvider;
 	private int lightmap;
-	private VanillaQuadHandler vanillaHandler;
 
 	private boolean isDefaultTranslucent;
 	private boolean isTranslucentDirect;
@@ -94,7 +90,6 @@ public class ItemRenderContext extends AbstractRenderContext {
 	private VertexConsumer cutoutVertexConsumer;
 	private VertexConsumer translucentGlintVertexConsumer;
 	private VertexConsumer cutoutGlintVertexConsumer;
-	private VertexConsumer defaultVertexConsumer;
 
 	public ItemRenderContext(ItemColors colorMap) {
 		this.colorMap = colorMap;
@@ -106,19 +101,28 @@ public class ItemRenderContext extends AbstractRenderContext {
 		return editorQuad;
 	}
 
+	@Override
+	public boolean isFaceCulled(@Nullable Direction face) {
+		throw new IllegalStateException("isFaceCulled can only be called on a block render context.");
+	}
+
+	@Override
+	public ModelTransformationMode itemTransformationMode() {
+		return transformMode;
+	}
+
 	@Override
 	public BakedModelConsumer bakedModelConsumer() {
 		return vanillaModelConsumer;
 	}
 
-	public void renderModel(ItemStack itemStack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model, VanillaQuadHandler vanillaHandler) {
+	public void renderModel(ItemStack itemStack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model) {
 		this.itemStack = itemStack;
 		this.transformMode = transformMode;
 		this.matrixStack = matrixStack;
 		this.vertexConsumerProvider = vertexConsumerProvider;
 		this.lightmap = lightmap;
 		this.overlay = overlay;
-		this.vanillaHandler = vanillaHandler;
 		computeOutputInfo();
 
 		matrix = matrixStack.peek().getPositionMatrix();
@@ -129,13 +133,11 @@ public class ItemRenderContext extends AbstractRenderContext {
 		this.itemStack = null;
 		this.matrixStack = null;
 		this.vertexConsumerProvider = null;
-		this.vanillaHandler = null;
 
 		translucentVertexConsumer = null;
 		cutoutVertexConsumer = null;
 		translucentGlintVertexConsumer = null;
 		cutoutGlintVertexConsumer = null;
-		defaultVertexConsumer = null;
 	}
 
 	private void computeOutputInfo() {
@@ -158,8 +160,6 @@ public class ItemRenderContext extends AbstractRenderContext {
 		}
 
 		isDefaultGlint = itemStack.hasGlint();
-
-		defaultVertexConsumer = getVertexConsumer(BlendMode.DEFAULT, TriState.DEFAULT);
 	}
 
 	private void renderQuad(MutableQuadViewImpl quad) {
@@ -275,34 +275,7 @@ public class ItemRenderContext extends AbstractRenderContext {
 
 		@Override
 		public void accept(BakedModel model, @Nullable BlockState state) {
-			if (hasTransform()) {
-				MutableQuadViewImpl editorQuad = ItemRenderContext.this.editorQuad;
-
-				// 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(state, cullFace, random);
-					final int count = quads.size();
-
-					for (int j = 0; j < count; j++) {
-						final BakedQuad q = quads.get(j);
-						editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
-						// Call renderQuad directly instead of emit for efficiency
-						renderQuad(editorQuad);
-					}
-				}
-
-				editorQuad.clear();
-			} else {
-				vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, defaultVertexConsumer);
-			}
+			VanillaModelEncoder.emitItemQuads(model, state, randomSupplier, ItemRenderContext.this);
 		}
 	}
-
-	/** used to accept a method reference from the ItemRenderer. */
-	@FunctionalInterface
-	public interface VanillaQuadHandler {
-		void accept(BakedModel model, ItemStack stack, int color, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
-	}
 }
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BakedModelMixin.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BakedModelMixin.java
new file mode 100644
index 000000000..10218850f
--- /dev/null
+++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BakedModelMixin.java
@@ -0,0 +1,43 @@
+/*
+ * 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.mixin.client.indigo.renderer;
+
+import java.util.function.Supplier;
+
+import org.spongepowered.asm.mixin.Mixin;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.random.Random;
+import net.minecraft.world.BlockRenderView;
+
+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.render.AbstractBlockRenderContext;
+import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
+
+@Mixin(BakedModel.class)
+public interface BakedModelMixin extends FabricBakedModel {
+	/**
+	 * Override the fallback path to shade vanilla quads differently.
+	 */
+	@Override
+	default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
+		VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context, ((AbstractBlockRenderContext) context).getVanillaModelEmitter());
+	}
+}
diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/ItemRendererMixin.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/ItemRendererMixin.java
index 3b170efa9..40890cc21 100644
--- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/ItemRendererMixin.java
+++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/ItemRendererMixin.java
@@ -32,9 +32,7 @@ import net.minecraft.client.render.model.json.ModelTransformationMode;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.item.ItemStack;
 
-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 ItemRendererMixin {
@@ -45,13 +43,10 @@ public abstract class ItemRendererMixin {
 	@Unique
 	private final ThreadLocal<ItemRenderContext> fabric_contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(colors));
 
-	@Unique
-	private final VanillaQuadHandler fabric_vanillaHandler = new IndigoQuadHandler((ItemRenderer) (Object) this);
-
 	@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;isBuiltin()Z"), method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", cancellable = true)
 	public void hook_renderItem(ItemStack stack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) {
 		if (!model.isVanillaAdapter()) {
-			fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model, fabric_vanillaHandler);
+			fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model);
 			matrixStack.pop();
 			ci.cancel();
 		}
diff --git a/fabric-renderer-indigo/src/client/resources/fabric-renderer-indigo.mixins.json b/fabric-renderer-indigo/src/client/resources/fabric-renderer-indigo.mixins.json
index 929ca2145..f15cbeb36 100644
--- a/fabric-renderer-indigo/src/client/resources/fabric-renderer-indigo.mixins.json
+++ b/fabric-renderer-indigo/src/client/resources/fabric-renderer-indigo.mixins.json
@@ -6,6 +6,7 @@
   "mixins": [
   ],
   "client": [
+    "BakedModelMixin",
     "BlockModelRendererMixin",
     "ChunkBuilderBuiltChunkRebuildTaskMixin",
     "ChunkRendererRegionMixin",