diff --git a/fabric-model-loading-api-v1/build.gradle b/fabric-model-loading-api-v1/build.gradle
index b2e004433..2f932885f 100644
--- a/fabric-model-loading-api-v1/build.gradle
+++ b/fabric-model-loading-api-v1/build.gradle
@@ -3,12 +3,8 @@ version = getSubprojectVersion(project)
 moduleDependencies(project, ['fabric-api-base'])
 
 testDependencies(project, [
-	':fabric-renderer-api-v1',
-	':fabric-renderer-indigo',
+//	':fabric-renderer-api-v1',
+//	':fabric-renderer-indigo',
 	':fabric-rendering-v1',
 	':fabric-resource-loader-v0'
 ])
-
-loom {
-	accessWidenerPath = file('src/client/resources/fabric-model-loading-api-v1.accesswidener')
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/BlockStateResolver.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/BlockStateResolver.java
index f8623a266..0b2cd29f9 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/BlockStateResolver.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/BlockStateResolver.java
@@ -20,20 +20,21 @@ import org.jetbrains.annotations.ApiStatus;
 
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockState;
-import net.minecraft.client.render.model.GroupableModel;
+import net.minecraft.client.render.model.BlockStateModel;
 
 /**
- * Block state resolvers are responsible for mapping each {@link BlockState} of a block to a {@link GroupableModel}.
- * They replace the {@code blockstates/} JSON files. One block can be mapped to only one block state resolver; multiple
- * resolvers will not receive the same block.
+ * Block state resolvers are responsible for mapping each {@link BlockState} of a block to a
+ * {@link BlockStateModel.UnbakedGrouped}. They replace the {@code blockstates/} JSON files. One block can be mapped to
+ * only one block state resolver; multiple resolvers will not receive the same block.
  *
  * <p>Block state resolvers can be used to create custom block state formats or dynamically resolve block state models.
  *
  * <p>Use {@link ModelModifier.OnLoad} instead of this interface if interacting with the block and block states directly
- * is not necessary. This includes custom model deserializers and loaders.
+ * is not necessary. Use {@link UnbakedModelDeserializer} for custom model deserializers and loaders.
  *
  * @see ModelModifier.OnLoad
  * @see ModelModifier.OnLoadBlock
+ * @see UnbakedModelDeserializer
  */
 @FunctionalInterface
 public interface BlockStateResolver {
@@ -64,6 +65,6 @@ public interface BlockStateResolver {
 		 * @param state the block state for which this model should be used
 		 * @param model the unbaked model for this block state
 		 */
-		void setModel(BlockState state, GroupableModel model);
+		void setModel(BlockState state, BlockStateModel.UnbakedGrouped model);
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/FabricBakedModelManager.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/FabricBakedModelManager.java
deleted file mode 100644
index e0b8caba0..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/FabricBakedModelManager.java
+++ /dev/null
@@ -1,46 +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.api.client.model.loading.v1;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.BakedModelManager;
-import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.util.Identifier;
-
-/**
- * Fabric-provided helper methods for {@link BakedModelManager}.
- *
- * <p>Note: This interface is automatically implemented on the {@link BakedModelManager} via Mixin and interface injection.
- */
-public interface FabricBakedModelManager {
-	/**
-	 * Similar to {@link BakedModelManager#getModel(ModelIdentifier)}, but accepts an {@link Identifier} instead of a
-	 * {@link ModelIdentifier}. Use this method to retrieve models loaded using
-	 * {@link ModelLoadingPlugin.Context#addModels}, since those models do not have corresponding
-	 * {@link ModelIdentifier}s.
-	 *
-	 * <p><b>This method, as well as its vanilla counterpart, should only be used after the
-	 * {@link BakedModelManager} has completed reloading.</b> Otherwise, the result will be
-	 * outdated or an exception will be thrown.
-	 *
-	 * @param id the id of the model
-	 * @return the model
-	 */
-	default BakedModel getModel(Identifier id) {
-		throw new UnsupportedOperationException("Implemented via mixin.");
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelLoadingPlugin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelLoadingPlugin.java
index a9138c92e..800e47cb5 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelLoadingPlugin.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelLoadingPlugin.java
@@ -16,7 +16,6 @@
 
 package net.fabricmc.fabric.api.client.model.loading.v1;
 
-import java.util.Collection;
 import java.util.List;
 
 import org.jetbrains.annotations.ApiStatus;
@@ -24,7 +23,6 @@ import org.jetbrains.annotations.UnmodifiableView;
 
 import net.minecraft.block.Block;
 import net.minecraft.resource.ResourceManager;
-import net.minecraft.util.Identifier;
 
 import net.fabricmc.fabric.api.event.Event;
 import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingPluginManager;
@@ -60,18 +58,6 @@ public interface ModelLoadingPlugin {
 
 	@ApiStatus.NonExtendable
 	interface Context {
-		/**
-		 * Adds one or more models that will be loaded, baked, and made available through
-		 * {@link FabricBakedModelManager#getModel(Identifier)}.
-		 */
-		void addModels(Identifier... ids);
-
-		/**
-		 * Adds multiple models that will be loaded, baked, and made available through
-		 * {@link FabricBakedModelManager#getModel(Identifier)}.
-		 */
-		void addModels(Collection<? extends Identifier> ids);
-
 		/**
 		 * Registers a block state resolver for a block.
 		 *
@@ -83,24 +69,11 @@ public interface ModelLoadingPlugin {
 		/**
 		 * Event access to monitor unbaked model loads and replace the loaded model.
 		 *
-		 * <p>Replacements done by listeners of this callback <b>do</b> affect child models (that is, models whose
-		 * parent hierarchy contains the replaced model), unlike {@link #modifyModelBeforeBake}.
+		 * <p>Replacements done by listeners of this callback affect child models (that is, models whose
+		 * parent hierarchy contains the replaced model).
 		 */
 		Event<ModelModifier.OnLoad> modifyModelOnLoad();
 
-		/**
-		 * Event access to replace the unbaked model used for baking without replacing the cached model.
-		 *
-		 * <p>Replacements done by listeners of this callback <b>do not</b> affect child models (that is, models whose
-		 * parent hierarchy contains the replaced model), unlike {@link #modifyModelOnLoad}.
-		 */
-		Event<ModelModifier.BeforeBake> modifyModelBeforeBake();
-
-		/**
-		 * Event access to replace the baked model.
-		 */
-		Event<ModelModifier.AfterBake> modifyModelAfterBake();
-
 		/**
 		 * Event access to monitor unbaked block model loads and replace the loaded model.
 		 */
@@ -115,5 +88,15 @@ public interface ModelLoadingPlugin {
 		 * Event access to replace the baked block model.
 		 */
 		Event<ModelModifier.AfterBakeBlock> modifyBlockModelAfterBake();
+
+		/**
+		 * Event access to replace the unbaked item model used for baking.
+		 */
+		Event<ModelModifier.BeforeBakeItem> modifyItemModelBeforeBake();
+
+		/**
+		 * Event access to replace the baked item model.
+		 */
+		Event<ModelModifier.AfterBakeItem> modifyItemModelAfterBake();
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelModifier.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelModifier.java
index 95ad0a464..93b0eb40a 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelModifier.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelModifier.java
@@ -17,16 +17,13 @@
 package net.fabricmc.fabric.api.client.model.loading.v1;
 
 import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.block.BlockState;
-import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.item.model.ItemModel;
 import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.GroupableModel;
-import net.minecraft.client.render.model.ModelBakeSettings;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.client.render.model.ResolvableModel;
 import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.util.ModelIdentifier;
 import net.minecraft.util.Identifier;
 
 import net.fabricmc.fabric.api.event.Event;
@@ -37,8 +34,8 @@ import net.fabricmc.fabric.api.event.Event;
  *
  * <p>Example use cases:
  * <ul>
- *     <li>Overriding the model for a particular block state - check if the given identifier matches the identifier
- *     for that block state. If so, return your desired model, otherwise return the given model.</li>
+ *     <li>Overriding the model for a particular block state - check if the given block state matches the desired block
+ *     state. If so, return your desired model, otherwise return the given model.</li>
  *     <li>Wrapping a model to override certain behaviors - simply return a new model instance and delegate calls
  *     to the original model as needed.</li>
  * </ul>
@@ -47,6 +44,10 @@ import net.fabricmc.fabric.api.event.Event;
  * and separate phases are provided for mods that wrap their own models and mods that need to wrap models of other mods
  * or wrap models arbitrarily.
  *
+ * <p>Any event may be invoked concurrently with other invocations of the same event or other events, subject to
+ * reasonable constraints. For example, a block/item model and its dependencies must be loaded before the block/item
+ * model is baked.
+ *
  * <p>These callbacks are invoked for <b>every single model that is loaded or baked</b>, so implementations should be
  * as efficient as possible.
  */
@@ -72,15 +73,7 @@ public final class ModelModifier {
 	@FunctionalInterface
 	public interface OnLoad {
 		/**
-		 * This handler is invoked to allow modification of an unbaked model right after it is first loaded and before
-		 * it is cached.
-		 *
-		 * <p>If the given model is {@code null}, its corresponding identifier was requested during
-		 * {@linkplain ResolvableModel#resolve resolution} but the model was not loaded normally; i.e. through a JSON
-		 * file, possibly because that file did not exist. If a non-{@code null} model is returned in this case,
-		 * resolution will continue without warnings or errors. This callback can return a {@code null} model, which
-		 * has the same meaning as described earlier, so it is unlikely that an implementor should need to return
-		 * {@code null} unless directly returning the given model.
+		 * This handler is invoked to allow modification of an unbaked model right after it is first loaded.
 		 *
 		 * <p>For further information, see the docs of {@link ModelLoadingPlugin.Context#modifyModelOnLoad()}.
 		 *
@@ -89,8 +82,7 @@ public final class ModelModifier {
 		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
 		 * @see ModelLoadingPlugin.Context#modifyModelOnLoad
 		 */
-		@Nullable
-		UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Context context);
+		UnbakedModel modifyModelOnLoad(UnbakedModel model, Context context);
 
 		/**
 		 * The context for an on load model modification event.
@@ -104,86 +96,6 @@ public final class ModelModifier {
 		}
 	}
 
-	@FunctionalInterface
-	public interface BeforeBake {
-		/**
-		 * This handler is invoked to allow modification of the unbaked model instance right before it is baked.
-		 *
-		 * <p>For further information, see the docs of {@link ModelLoadingPlugin.Context#modifyModelBeforeBake()}.
-		 *
-		 * @param model the current unbaked model instance
-		 * @param context context with additional information about the model/loader
-		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
-		 * @see ModelLoadingPlugin.Context#modifyModelBeforeBake
-		 */
-		UnbakedModel modifyModelBeforeBake(UnbakedModel model, Context context);
-
-		/**
-		 * The context for a before bake model modification event.
-		 */
-		@ApiStatus.NonExtendable
-		interface Context {
-			/**
-			 * The identifier of the model being baked.
-			 */
-			Identifier id();
-
-			/**
-			 * The settings this model is being baked with.
-			 */
-			ModelBakeSettings settings();
-
-			/**
-			 * The baker being used to bake this model. It can be used to {@linkplain Baker#bake bake models} and
-			 * {@linkplain Baker#getSpriteGetter get sprites}. Note that baking a model which was not previously
-			 * {@linkplain ResolvableModel.Resolver#resolve resolved} will log a warning and return the missing model.
-			 */
-			Baker baker();
-		}
-	}
-
-	@FunctionalInterface
-	public interface AfterBake {
-		/**
-		 * This handler is invoked to allow modification of the baked model instance right after it is baked and before
-		 * it is cached.
-		 *
-		 * @param model the current baked model instance
-		 * @param context context with additional information about the model/loader
-		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
-		 * @see ModelLoadingPlugin.Context#modifyModelAfterBake
-		 */
-		BakedModel modifyModelAfterBake(BakedModel model, Context context);
-
-		/**
-		 * The context for an after bake model modification event.
-		 */
-		@ApiStatus.NonExtendable
-		interface Context {
-			/**
-			 * The identifier of the model being baked.
-			 */
-			Identifier id();
-
-			/**
-			 * The unbaked model that is being baked.
-			 */
-			UnbakedModel sourceModel();
-
-			/**
-			 * The settings this model is being baked with.
-			 */
-			ModelBakeSettings settings();
-
-			/**
-			 * The baker being used to bake this model. It can be used to {@linkplain Baker#bake bake models} and
-			 * {@linkplain Baker#getSpriteGetter get sprites}. Note that baking a model which was not previously
-			 * {@linkplain ResolvableModel.Resolver#resolve resolved} will log a warning and return the missing model.
-			 */
-			Baker baker();
-		}
-	}
-
 	@FunctionalInterface
 	public interface OnLoadBlock {
 		/**
@@ -194,18 +106,13 @@ public final class ModelModifier {
 		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
 		 * @see ModelLoadingPlugin.Context#modifyBlockModelOnLoad
 		 */
-		GroupableModel modifyModelOnLoad(GroupableModel model, Context context);
+		BlockStateModel.UnbakedGrouped modifyModelOnLoad(BlockStateModel.UnbakedGrouped model, Context context);
 
 		/**
 		 * The context for an on load block model modification event.
 		 */
 		@ApiStatus.NonExtendable
 		interface Context {
-			/**
-			 * The identifier of the model that was loaded.
-			 */
-			ModelIdentifier id();
-
 			/**
 			 * The corresponding block state of the model that was loaded.
 			 */
@@ -223,7 +130,7 @@ public final class ModelModifier {
 		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
 		 * @see ModelLoadingPlugin.Context#modifyBlockModelBeforeBake
 		 */
-		GroupableModel modifyModelBeforeBake(GroupableModel model, Context context);
+		BlockStateModel.UnbakedGrouped modifyModelBeforeBake(BlockStateModel.UnbakedGrouped model, Context context);
 
 		/**
 		 * The context for a before bake block model modification event.
@@ -231,14 +138,16 @@ public final class ModelModifier {
 		@ApiStatus.NonExtendable
 		interface Context {
 			/**
-			 * The identifier of the model being baked.
+			 * The corresponding block state of the model being baked.
 			 */
-			ModelIdentifier id();
+			BlockState state();
 
 			/**
-			 * The baker being used to bake this model. It can be used to {@linkplain Baker#bake bake models} and
-			 * {@linkplain Baker#getSpriteGetter get sprites}. Note that baking a model which was not previously
-			 * {@linkplain ResolvableModel.Resolver#resolve resolved} will log a warning and return the missing model.
+			 * The baker being used to bake this model. It can be used to
+			 * {@linkplain Baker#getModel get resolved models} and {@linkplain Baker#getSpriteGetter get sprites}. Note
+			 * that retrieving a model which was not previously
+			 * {@linkplain ResolvableModel.Resolver#markDependency discovered} will log a warning and return the missing
+			 * model.
 			 */
 			Baker baker();
 		}
@@ -254,7 +163,7 @@ public final class ModelModifier {
 		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
 		 * @see ModelLoadingPlugin.Context#modifyBlockModelAfterBake
 		 */
-		BakedModel modifyModelAfterBake(BakedModel model, Context context);
+		BlockStateModel modifyModelAfterBake(BlockStateModel model, Context context);
 
 		/**
 		 * The context for an after bake block model modification event.
@@ -262,23 +171,88 @@ public final class ModelModifier {
 		@ApiStatus.NonExtendable
 		interface Context {
 			/**
-			 * The identifier of the model being baked.
+			 * The corresponding block state of the model being baked.
 			 */
-			ModelIdentifier id();
+			BlockState state();
 
 			/**
 			 * The unbaked model that is being baked.
 			 */
-			GroupableModel sourceModel();
+			BlockStateModel.UnbakedGrouped sourceModel();
 
 			/**
-			 * The baker being used to bake this model. It can be used to {@linkplain Baker#bake bake models} and
-			 * {@linkplain Baker#getSpriteGetter get sprites}. Note that baking a model which was not previously
-			 * {@linkplain ResolvableModel.Resolver#resolve resolved} will log a warning and return the missing model.
+			 * The baker being used to bake this model. It can be used to
+			 * {@linkplain Baker#getModel get resolved models} and {@linkplain Baker#getSpriteGetter get sprites}. Note
+			 * that retrieving a model which was not previously
+			 * {@linkplain ResolvableModel.Resolver#markDependency discovered} will log a warning and return the missing
+			 * model.
 			 */
 			Baker baker();
 		}
 	}
 
+	@FunctionalInterface
+	public interface BeforeBakeItem {
+		/**
+		 * This handler is invoked to allow modification of the unbaked item model instance right before it is baked.
+		 *
+		 * @param model the current unbaked model instance
+		 * @param context context with additional information about the model/loader
+		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
+		 * @see ModelLoadingPlugin.Context#modifyItemModelBeforeBake
+		 */
+		ItemModel.Unbaked modifyModelBeforeBake(ItemModel.Unbaked model, Context context);
+
+		/**
+		 * The context for a before bake item model modification event.
+		 */
+		@ApiStatus.NonExtendable
+		interface Context {
+			/**
+			 * The corresponding item ID of the model being baked.
+			 */
+			Identifier itemId();
+
+			/**
+			 * The vanilla context being used to bake this model.
+			 */
+			ItemModel.BakeContext bakeContext();
+		}
+	}
+
+	@FunctionalInterface
+	public interface AfterBakeItem {
+		/**
+		 * This handler is invoked to allow modification of the baked item model instance right after it is baked.
+		 *
+		 * @param model the current baked model instance
+		 * @param context context with additional information about the model/loader
+		 * @return the model that should be used in this scenario. If no changes are needed, just return {@code model} as-is.
+		 * @see ModelLoadingPlugin.Context#modifyItemModelAfterBake
+		 */
+		ItemModel modifyModelAfterBake(ItemModel model, Context context);
+
+		/**
+		 * The context for an after bake item model modification event.
+		 */
+		@ApiStatus.NonExtendable
+		interface Context {
+			/**
+			 * The corresponding item ID of the model being baked.
+			 */
+			Identifier itemId();
+
+			/**
+			 * The unbaked model that is being baked.
+			 */
+			ItemModel.Unbaked sourceModel();
+
+			/**
+			 * The vanilla context being used to bake this model.
+			 */
+			ItemModel.BakeContext bakeContext();
+		}
+	}
+
 	private ModelModifier() { }
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnbakedModelDeserializer.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnbakedModelDeserializer.java
index d975e6d2f..dbec5586d 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnbakedModelDeserializer.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnbakedModelDeserializer.java
@@ -27,7 +27,6 @@ import net.minecraft.client.render.model.UnbakedModel;
 import net.minecraft.client.render.model.json.JsonUnbakedModel;
 import net.minecraft.client.render.model.json.ModelElement;
 import net.minecraft.client.render.model.json.ModelElementFace;
-import net.minecraft.client.render.model.json.ModelElementTexture;
 import net.minecraft.client.render.model.json.ModelTransformation;
 import net.minecraft.client.render.model.json.Transformation;
 import net.minecraft.util.Identifier;
@@ -87,7 +86,6 @@ public interface UnbakedModelDeserializer {
 	 *     <li>{@link UnbakedModel}</li>
 	 *     <li>{@link ModelElement}</li>
 	 *     <li>{@link ModelElementFace}</li>
-	 *     <li>{@link ModelElementTexture}</li>
 	 *     <li>{@link Transformation}</li>
 	 *     <li>{@link ModelTransformation}</li>
 	 * </ul>
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnwrappableBakedModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnwrappableBakedModel.java
deleted file mode 100644
index 7ce825380..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnwrappableBakedModel.java
+++ /dev/null
@@ -1,95 +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.api.client.model.loading.v1;
-
-import java.util.function.Predicate;
-
-import org.jetbrains.annotations.Nullable;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.WrapperBakedModel;
-
-/**
- * An interface to be implemented by models that wrap and replace another model, such as {@link WrapperBakedModel}.
- * This allows mods to access the wrapped model without having to know the exact type of the wrapper model.
- *
- * <p>If you need to access data stored in one of your {@link BakedModel} subclasses,
- * and you would normally access the model by its identifier and then cast it:
- * call {@link #unwrap(BakedModel, Predicate)} on the model first, in case another
- * mod is wrapping your model to alter its rendering.
- *
- * <p>Note: This interface is automatically implemented on {@link WrapperBakedModel} and subclasses via Mixin and
- * interface injection.
- */
-public interface UnwrappableBakedModel {
-	/**
-	 * Return the wrapped model, if there is one at the moment, or {@code null} otherwise.
-	 *
-	 * <p>If there are multiple layers of wrapping, this method does not necessarily return the innermost model.
-	 */
-	@Nullable
-	default BakedModel getWrappedModel() {
-		return null;
-	}
-
-	/**
-	 * Iteratively unwrap the given model until the given condition returns true or all models in the hierarchy have
-	 * been tested. If no model passes the condition, null is returned.
-	 *
-	 * <p>A good use of this method is to safely cast a model to an expected type, by passing
-	 * {@code model -> model instanceof MyCustomBakedModel} as the condition.
-	 */
-	@Nullable
-	static BakedModel unwrap(BakedModel model, Predicate<BakedModel> condition) {
-		while (!condition.test(model)) {
-			if (model instanceof UnwrappableBakedModel wrapper) {
-				BakedModel wrapped = wrapper.getWrappedModel();
-
-				if (wrapped == null) {
-					return null;
-				} else if (wrapped == model) {
-					throw new IllegalArgumentException("Model " + model + " is wrapping itself!");
-				} else {
-					model = wrapped;
-				}
-			} else {
-				return null;
-			}
-		}
-
-		return model;
-	}
-
-	/**
-	 * Fully unwrap a model, i.e. return the innermost model.
-	 */
-	static BakedModel unwrap(BakedModel model) {
-		while (model instanceof UnwrappableBakedModel wrapper) {
-			BakedModel wrapped = wrapper.getWrappedModel();
-
-			if (wrapped == null) {
-				return model;
-			} else if (wrapped == model) {
-				throw new IllegalArgumentException("Model " + model + " is wrapping itself!");
-			} else {
-				model = wrapped;
-			}
-		}
-
-		return model;
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBakedItemModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBakedItemModel.java
new file mode 100644
index 000000000..c23df337b
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBakedItemModel.java
@@ -0,0 +1,47 @@
+/*
+ * 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.api.client.model.loading.v1.wrapper;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.client.item.ItemModelManager;
+import net.minecraft.client.render.item.ItemRenderState;
+import net.minecraft.client.render.item.model.ItemModel;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.item.ItemDisplayContext;
+import net.minecraft.item.ItemStack;
+
+/**
+ * A simple implementation of {@link ItemModel} that delegates all method calls to the {@link #wrapped} field.
+ * Implementations must set the {@link #wrapped} field somehow.
+ */
+public abstract class WrapperBakedItemModel implements ItemModel {
+	protected ItemModel wrapped;
+
+	protected WrapperBakedItemModel() {
+	}
+
+	protected WrapperBakedItemModel(ItemModel wrapped) {
+		this.wrapped = wrapped;
+	}
+
+	@Override
+	public void update(ItemRenderState state, ItemStack stack, ItemModelManager resolver, ItemDisplayContext displayContext, @Nullable ClientWorld world, @Nullable LivingEntity user, int seed) {
+		wrapped.update(state, stack, resolver, displayContext, world, user, seed);
+	}
+}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBlockStateModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBlockStateModel.java
new file mode 100644
index 000000000..8e204bb81
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperBlockStateModel.java
@@ -0,0 +1,55 @@
+/*
+ * 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.api.client.model.loading.v1.wrapper;
+
+import java.util.List;
+
+import net.minecraft.client.render.model.BlockModelPart;
+import net.minecraft.client.render.model.BlockStateModel;
+import net.minecraft.client.texture.Sprite;
+import net.minecraft.util.math.random.Random;
+
+// TODO: FRAPI overrides
+/**
+ * A simple implementation of {@link BlockStateModel} that delegates all method calls to the {@link #wrapped} field.
+ * Implementations must set the {@link #wrapped} field somehow.
+ */
+public abstract class WrapperBlockStateModel implements BlockStateModel {
+	protected BlockStateModel wrapped;
+
+	protected WrapperBlockStateModel() {
+	}
+
+	protected WrapperBlockStateModel(BlockStateModel wrapped) {
+		this.wrapped = wrapped;
+	}
+
+	@Override
+	public void addParts(Random random, List<BlockModelPart> parts) {
+		wrapped.addParts(random, parts);
+	}
+
+	@Override
+	public List<BlockModelPart> getParts(Random random) {
+		return wrapped.getParts(random);
+	}
+
+	@Override
+	public Sprite particleSprite() {
+		return wrapped.particleSprite();
+	}
+}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperGroupableModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedGroupedBlockStateModel.java
similarity index 57%
rename from fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperGroupableModel.java
rename to fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedGroupedBlockStateModel.java
index 8a65c11a6..5f81c9517 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperGroupableModel.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedGroupedBlockStateModel.java
@@ -14,39 +14,38 @@
  * limitations under the License.
  */
 
-package net.fabricmc.fabric.api.client.model.loading.v1;
+package net.fabricmc.fabric.api.client.model.loading.v1.wrapper;
 
 import net.minecraft.block.BlockState;
-import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.GroupableModel;
+import net.minecraft.client.render.model.BlockStateModel;
 
 /**
- * A simple implementation of {@link GroupableModel} that delegates all method calls to the {@link #wrapped} field.
- * Implementations must set the {@link #wrapped} field somehow.
+ * A simple implementation of {@link BlockStateModel.UnbakedGrouped} that delegates all method calls to the
+ * {@link #wrapped} field. Implementations must set the {@link #wrapped} field somehow.
  */
-public abstract class WrapperGroupableModel implements GroupableModel {
-	protected GroupableModel wrapped;
+public abstract class WrapperUnbakedGroupedBlockStateModel implements BlockStateModel.UnbakedGrouped {
+	protected BlockStateModel.UnbakedGrouped wrapped;
 
-	protected WrapperGroupableModel() {
+	protected WrapperUnbakedGroupedBlockStateModel() {
 	}
 
-	protected WrapperGroupableModel(GroupableModel wrapped) {
+	protected WrapperUnbakedGroupedBlockStateModel(BlockStateModel.UnbakedGrouped wrapped) {
 		this.wrapped = wrapped;
 	}
 
 	@Override
-	public void resolve(Resolver resolver) {
-		wrapped.resolve(resolver);
-	}
-
-	@Override
-	public BakedModel bake(Baker baker) {
-		return wrapped.bake(baker);
+	public BlockStateModel getModel(BlockState state, Baker baker) {
+		return wrapped.getModel(state, baker);
 	}
 
 	@Override
 	public Object getEqualityGroup(BlockState state) {
 		return wrapped.getEqualityGroup(state);
 	}
+
+	@Override
+	public void resolve(Resolver resolver) {
+		wrapped.resolve(resolver);
+	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedItemModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedItemModel.java
new file mode 100644
index 000000000..4669ca371
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedItemModel.java
@@ -0,0 +1,51 @@
+/*
+ * 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.api.client.model.loading.v1.wrapper;
+
+import com.mojang.serialization.MapCodec;
+
+import net.minecraft.client.render.item.model.ItemModel;
+
+/**
+ * A simple implementation of {@link ItemModel.Unbaked} that delegates all method calls to the {@link #wrapped} field.
+ * Implementations must set the {@link #wrapped} field somehow.
+ */
+public abstract class WrapperUnbakedItemModel implements ItemModel.Unbaked {
+	protected ItemModel.Unbaked wrapped;
+
+	protected WrapperUnbakedItemModel() {
+	}
+
+	protected WrapperUnbakedItemModel(ItemModel.Unbaked wrapped) {
+		this.wrapped = wrapped;
+	}
+
+	@Override
+	public void resolve(Resolver resolver) {
+		wrapped.resolve(resolver);
+	}
+
+	@Override
+	public MapCodec<? extends ItemModel.Unbaked> getCodec() {
+		return wrapped.getCodec();
+	}
+
+	@Override
+	public ItemModel bake(ItemModel.BakeContext context) {
+		return wrapped.bake(context);
+	}
+}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperUnbakedModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedModel.java
similarity index 59%
rename from fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperUnbakedModel.java
rename to fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedModel.java
index 7a4f382a2..3fbc52914 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperUnbakedModel.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/wrapper/WrapperUnbakedModel.java
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-package net.fabricmc.fabric.api.client.model.loading.v1;
+package net.fabricmc.fabric.api.client.model.loading.v1.wrapper;
 
 import org.jetbrains.annotations.Nullable;
 
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.ModelBakeSettings;
+import net.minecraft.client.render.model.Geometry;
 import net.minecraft.client.render.model.ModelTextures;
 import net.minecraft.client.render.model.UnbakedModel;
 import net.minecraft.client.render.model.json.ModelTransformation;
+import net.minecraft.util.Identifier;
 
 /**
  * A simple implementation of {@link UnbakedModel} that delegates all method calls to the {@link #wrapped} field.
@@ -40,41 +39,37 @@ public abstract class WrapperUnbakedModel implements UnbakedModel {
 	}
 
 	@Override
-	public void resolve(Resolver resolver) {
-		wrapped.resolve(resolver);
-	}
-
-	@Override
-	public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
-		return wrapped.bake(textures, baker, settings, ambientOcclusion, isSideLit, transformation);
+	@Nullable
+	public Boolean ambientOcclusion() {
+		return wrapped.ambientOcclusion();
 	}
 
 	@Override
 	@Nullable
-	public Boolean getAmbientOcclusion() {
-		return wrapped.getAmbientOcclusion();
+	public GuiLight guiLight() {
+		return wrapped.guiLight();
 	}
 
 	@Override
 	@Nullable
-	public GuiLight getGuiLight() {
-		return wrapped.getGuiLight();
+	public ModelTransformation transformations() {
+		return wrapped.transformations();
+	}
+
+	@Override
+	public ModelTextures.Textures textures() {
+		return wrapped.textures();
 	}
 
 	@Override
 	@Nullable
-	public ModelTransformation getTransformation() {
-		return wrapped.getTransformation();
-	}
-
-	@Override
-	public ModelTextures.Textures getTextures() {
-		return wrapped.getTextures();
+	public Geometry geometry() {
+		return wrapped.geometry();
 	}
 
 	@Override
 	@Nullable
-	public UnbakedModel getParent() {
-		return wrapped.getParent();
+	public Identifier parent() {
+		return wrapped.parent();
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java
deleted file mode 100644
index 94f202ec1..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java
+++ /dev/null
@@ -1,31 +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.model.loading;
-
-import java.util.Map;
-
-import org.jetbrains.annotations.Nullable;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.util.Identifier;
-
-public interface BakedModelsHooks {
-	@Nullable
-	Map<Identifier, BakedModel> fabric_getExtraModels();
-
-	void fabric_setExtraModels(@Nullable Map<Identifier, BakedModel> extraModels);
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelBakerHooks.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelBakerHooks.java
deleted file mode 100644
index c6a245e6e..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelBakerHooks.java
+++ /dev/null
@@ -1,24 +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.model.loading;
-
-import org.jetbrains.annotations.Nullable;
-
-public interface ModelBakerHooks {
-	@Nullable
-	ModelLoadingEventDispatcher fabric_getDispatcher();
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingEventDispatcher.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingEventDispatcher.java
index ad9be411c..8004dca95 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingEventDispatcher.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingEventDispatcher.java
@@ -20,12 +20,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 
 import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
 import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
 import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
 import org.jetbrains.annotations.Nullable;
@@ -34,16 +32,11 @@ import org.slf4j.LoggerFactory;
 
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockState;
-import net.minecraft.client.render.block.BlockModels;
-import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.item.model.ItemModel;
 import net.minecraft.client.render.model.Baker;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.client.render.model.BlockStatesLoader;
-import net.minecraft.client.render.model.GroupableModel;
-import net.minecraft.client.render.model.ModelBakeSettings;
 import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.registry.Registries;
-import net.minecraft.registry.RegistryKey;
 import net.minecraft.util.Identifier;
 
 import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
@@ -57,14 +50,8 @@ public class ModelLoadingEventDispatcher {
 	private final ModelLoadingPluginContextImpl pluginContext;
 
 	private final BlockStateResolverContext blockStateResolverContext = new BlockStateResolverContext();
-
 	private final OnLoadModifierContext onLoadModifierContext = new OnLoadModifierContext();
-	private final ObjectArrayList<BeforeBakeModifierContext> beforeBakeModifierContextStack = new ObjectArrayList<>();
-	private final ObjectArrayList<AfterBakeModifierContext> afterBakeModifierContextStack = new ObjectArrayList<>();
-
 	private final OnLoadBlockModifierContext onLoadBlockModifierContext = new OnLoadBlockModifierContext();
-	private final BeforeBakeBlockModifierContext beforeBakeBlockModifierContext = new BeforeBakeBlockModifierContext();
-	private final AfterBakeBlockModifierContext afterBakeBlockModifierContext = new AfterBakeBlockModifierContext();
 
 	public ModelLoadingEventDispatcher(List<ModelLoadingPlugin> plugins) {
 		this.pluginContext = new ModelLoadingPluginContextImpl();
@@ -78,90 +65,45 @@ public class ModelLoadingEventDispatcher {
 		}
 	}
 
-	public void forEachExtraModel(Consumer<Identifier> extraModelConsumer) {
-		pluginContext.extraModels.forEach(extraModelConsumer);
+	public Map<Identifier, UnbakedModel> modifyModelsOnLoad(Map<Identifier, UnbakedModel> models) {
+		if (!(models instanceof HashMap)) {
+			models = new HashMap<>(models);
+		}
+
+		models.replaceAll(this::modifyModelOnLoad);
+		return models;
 	}
 
-	@Nullable
-	public UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Identifier id) {
+	private UnbakedModel modifyModelOnLoad(Identifier id, UnbakedModel model) {
 		onLoadModifierContext.prepare(id);
 		return pluginContext.modifyModelOnLoad().invoker().modifyModelOnLoad(model, onLoadModifierContext);
 	}
 
-	public UnbakedModel modifyModelBeforeBake(UnbakedModel model, Identifier id, ModelBakeSettings settings, Baker baker) {
-		if (beforeBakeModifierContextStack.isEmpty()) {
-			beforeBakeModifierContextStack.add(new BeforeBakeModifierContext());
-		}
-
-		BeforeBakeModifierContext context = beforeBakeModifierContextStack.pop();
-		context.prepare(id, settings, baker);
-
-		model = pluginContext.modifyModelBeforeBake().invoker().modifyModelBeforeBake(model, context);
-
-		beforeBakeModifierContextStack.push(context);
-		return model;
-	}
-
-	public BakedModel modifyModelAfterBake(BakedModel model, Identifier id, UnbakedModel sourceModel, ModelBakeSettings settings, Baker baker) {
-		if (afterBakeModifierContextStack.isEmpty()) {
-			afterBakeModifierContextStack.add(new AfterBakeModifierContext());
-		}
-
-		AfterBakeModifierContext context = afterBakeModifierContextStack.pop();
-		context.prepare(id, sourceModel, settings, baker);
-
-		model = pluginContext.modifyModelAfterBake().invoker().modifyModelAfterBake(model, context);
-
-		afterBakeModifierContextStack.push(context);
-		return model;
-	}
-
-	public BlockStatesLoader.BlockStateDefinition modifyBlockModelsOnLoad(BlockStatesLoader.BlockStateDefinition models) {
-		Map<ModelIdentifier, BlockStatesLoader.BlockModel> map = models.models();
+	public BlockStatesLoader.LoadedModels modifyBlockModelsOnLoad(BlockStatesLoader.LoadedModels models) {
+		Map<BlockState, BlockStateModel.UnbakedGrouped> map = models.models();
 
 		if (!(map instanceof HashMap)) {
 			map = new HashMap<>(map);
-			models = new BlockStatesLoader.BlockStateDefinition(map);
+			models = new BlockStatesLoader.LoadedModels(map);
 		}
 
 		putResolvedBlockStates(map);
-
-		map.replaceAll((id, blockModel) -> {
-			GroupableModel original = blockModel.model();
-			GroupableModel modified = modifyBlockModelOnLoad(original, id, blockModel.state());
-
-			if (original != modified) {
-				return new BlockStatesLoader.BlockModel(blockModel.state(), modified);
-			}
-
-			return blockModel;
-		});
+		map.replaceAll(this::modifyBlockModelOnLoad);
 
 		return models;
 	}
 
-	private void putResolvedBlockStates(Map<ModelIdentifier, BlockStatesLoader.BlockModel> map) {
+	private void putResolvedBlockStates(Map<BlockState, BlockStateModel.UnbakedGrouped> map) {
 		pluginContext.blockStateResolvers.forEach((block, resolver) -> {
-			Optional<RegistryKey<Block>> optionalKey = Registries.BLOCK.getKey(block);
-
-			if (optionalKey.isEmpty()) {
-				return;
-			}
-
-			Identifier blockId = optionalKey.get().getValue();
-
-			resolveBlockStates(resolver, block, (state, model) -> {
-				ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
-				map.put(modelId, new BlockStatesLoader.BlockModel(state, model));
-			});
+			resolveBlockStates(resolver, block, map::put);
 		});
 	}
 
-	private void resolveBlockStates(BlockStateResolver resolver, Block block, BiConsumer<BlockState, GroupableModel> output) {
+	private void resolveBlockStates(BlockStateResolver resolver, Block block, BiConsumer<BlockState, BlockStateModel.UnbakedGrouped> output) {
 		BlockStateResolverContext context = blockStateResolverContext;
 		context.prepare(block);
 
-		Reference2ReferenceMap<BlockState, GroupableModel> resolvedModels = context.models;
+		Reference2ReferenceMap<BlockState, BlockStateModel.UnbakedGrouped> resolvedModels = context.models;
 		ImmutableList<BlockState> allStates = block.getStateManager().getStates();
 		boolean thrown = false;
 
@@ -180,7 +122,7 @@ public class ModelLoadingEventDispatcher {
 			} else {
 				for (BlockState state : allStates) {
 					@Nullable
-					GroupableModel model = resolvedModels.get(state);
+					BlockStateModel.UnbakedGrouped model = resolvedModels.get(state);
 
 					if (model == null) {
 						LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block);
@@ -194,24 +136,30 @@ public class ModelLoadingEventDispatcher {
 		resolvedModels.clear();
 	}
 
-	private GroupableModel modifyBlockModelOnLoad(GroupableModel model, ModelIdentifier id, BlockState state) {
-		onLoadBlockModifierContext.prepare(id, state);
+	private BlockStateModel.UnbakedGrouped modifyBlockModelOnLoad(BlockState state, BlockStateModel.UnbakedGrouped model) {
+		onLoadBlockModifierContext.prepare(state);
 		return pluginContext.modifyBlockModelOnLoad().invoker().modifyModelOnLoad(model, onLoadBlockModifierContext);
 	}
 
-	public GroupableModel modifyBlockModelBeforeBake(GroupableModel model, ModelIdentifier id, Baker baker) {
-		beforeBakeBlockModifierContext.prepare(id, baker);
-		return pluginContext.modifyBlockModelBeforeBake().invoker().modifyModelBeforeBake(model, beforeBakeBlockModifierContext);
+	public BlockStateModel modifyBlockModel(BlockStateModel.UnbakedGrouped unbakedModel, BlockState state, Baker baker, Operation<BlockStateModel> bakeOperation) {
+		BakeBlockModifierContext modifierContext = new BakeBlockModifierContext(state, baker);
+		unbakedModel = pluginContext.modifyBlockModelBeforeBake().invoker().modifyModelBeforeBake(unbakedModel, modifierContext);
+		BlockStateModel model = bakeOperation.call(unbakedModel, state, baker);
+		modifierContext.prepareAfterBake(unbakedModel);
+		return pluginContext.modifyBlockModelAfterBake().invoker().modifyModelAfterBake(model, modifierContext);
 	}
 
-	public BakedModel modifyBlockModelAfterBake(BakedModel model, ModelIdentifier id, GroupableModel sourceModel, Baker baker) {
-		afterBakeBlockModifierContext.prepare(id, sourceModel, baker);
-		return pluginContext.modifyBlockModelAfterBake().invoker().modifyModelAfterBake(model, afterBakeBlockModifierContext);
+	public ItemModel modifyItemModel(ItemModel.Unbaked unbakedModel, Identifier itemId, ItemModel.BakeContext bakeContext, Operation<ItemModel> bakeOperation) {
+		BakeItemModifierContext modifierContext = new BakeItemModifierContext(itemId, bakeContext);
+		unbakedModel = pluginContext.modifyItemModelBeforeBake().invoker().modifyModelBeforeBake(unbakedModel, modifierContext);
+		ItemModel model = bakeOperation.call(unbakedModel, bakeContext);
+		modifierContext.prepareAfterBake(unbakedModel);
+		return pluginContext.modifyItemModelAfterBake().invoker().modifyModelAfterBake(model, modifierContext);
 	}
 
 	private static class BlockStateResolverContext implements BlockStateResolver.Context {
 		private Block block;
-		private final Reference2ReferenceMap<BlockState, GroupableModel> models = new Reference2ReferenceOpenHashMap<>();
+		private final Reference2ReferenceMap<BlockState, BlockStateModel.UnbakedGrouped> models = new Reference2ReferenceOpenHashMap<>();
 
 		private void prepare(Block block) {
 			this.block = block;
@@ -224,7 +172,7 @@ public class ModelLoadingEventDispatcher {
 		}
 
 		@Override
-		public void setModel(BlockState state, GroupableModel model) {
+		public void setModel(BlockState state, BlockStateModel.UnbakedGrouped model) {
 			Objects.requireNonNull(state, "state cannot be null");
 			Objects.requireNonNull(model, "model cannot be null");
 
@@ -251,131 +199,76 @@ public class ModelLoadingEventDispatcher {
 		}
 	}
 
-	private static class BeforeBakeModifierContext implements ModelModifier.BeforeBake.Context {
-		private Identifier id;
-		private ModelBakeSettings settings;
-		private Baker baker;
-
-		private void prepare(Identifier id, ModelBakeSettings settings, Baker baker) {
-			this.id = id;
-			this.settings = settings;
-			this.baker = baker;
-		}
-
-		@Override
-		public Identifier id() {
-			return id;
-		}
-
-		@Override
-		public ModelBakeSettings settings() {
-			return settings;
-		}
-
-		@Override
-		public Baker baker() {
-			return baker;
-		}
-	}
-
-	private static class AfterBakeModifierContext implements ModelModifier.AfterBake.Context {
-		private Identifier id;
-		private UnbakedModel sourceModel;
-		private ModelBakeSettings settings;
-		private Baker baker;
-
-		private void prepare(Identifier id, UnbakedModel sourceModel, ModelBakeSettings settings, Baker baker) {
-			this.id = id;
-			this.sourceModel = sourceModel;
-			this.settings = settings;
-			this.baker = baker;
-		}
-
-		@Override
-		public Identifier id() {
-			return id;
-		}
-
-		@Override
-		public UnbakedModel sourceModel() {
-			return sourceModel;
-		}
-
-		@Override
-		public ModelBakeSettings settings() {
-			return settings;
-		}
-
-		@Override
-		public Baker baker() {
-			return baker;
-		}
-	}
-
 	private static class OnLoadBlockModifierContext implements ModelModifier.OnLoadBlock.Context {
-		private ModelIdentifier id;
 		private BlockState state;
 
-		private void prepare(ModelIdentifier id, BlockState state) {
-			this.id = id;
+		private void prepare(BlockState state) {
 			this.state = state;
 		}
 
-		@Override
-		public ModelIdentifier id() {
-			return id;
-		}
-
 		@Override
 		public BlockState state() {
 			return state;
 		}
 	}
 
-	private static class BeforeBakeBlockModifierContext implements ModelModifier.BeforeBakeBlock.Context {
-		private ModelIdentifier id;
-		private Baker baker;
+	private static class BakeBlockModifierContext implements ModelModifier.BeforeBakeBlock.Context, ModelModifier.AfterBakeBlock.Context {
+		private final BlockState state;
+		private final Baker baker;
+		private BlockStateModel.UnbakedGrouped sourceModel;
 
-		private void prepare(ModelIdentifier id, Baker baker) {
-			this.id = id;
+		private BakeBlockModifierContext(BlockState state, Baker baker) {
+			this.state = state;
 			this.baker = baker;
 		}
 
+		private void prepareAfterBake(BlockStateModel.UnbakedGrouped sourceModel) {
+			this.sourceModel = sourceModel;
+		}
+
 		@Override
-		public ModelIdentifier id() {
-			return id;
+		public BlockState state() {
+			return state;
 		}
 
 		@Override
 		public Baker baker() {
 			return baker;
 		}
+
+		@Override
+		public BlockStateModel.UnbakedGrouped sourceModel() {
+			return sourceModel;
+		}
 	}
 
-	private static class AfterBakeBlockModifierContext implements ModelModifier.AfterBakeBlock.Context {
-		private ModelIdentifier id;
-		private GroupableModel sourceModel;
-		private Baker baker;
+	private static class BakeItemModifierContext implements ModelModifier.BeforeBakeItem.Context, ModelModifier.AfterBakeItem.Context {
+		private final Identifier itemId;
+		private final ItemModel.BakeContext bakeContext;
+		private ItemModel.Unbaked sourceModel;
 
-		private void prepare(ModelIdentifier id, GroupableModel sourceModel, Baker baker) {
-			this.id = id;
+		private BakeItemModifierContext(Identifier itemId, ItemModel.BakeContext bakeContext) {
+			this.itemId = itemId;
+			this.bakeContext = bakeContext;
+		}
+
+		private void prepareAfterBake(ItemModel.Unbaked sourceModel) {
 			this.sourceModel = sourceModel;
-			this.baker = baker;
 		}
 
 		@Override
-		public ModelIdentifier id() {
-			return id;
+		public Identifier itemId() {
+			return itemId;
 		}
 
 		@Override
-		public GroupableModel sourceModel() {
+		public ItemModel.BakeContext bakeContext() {
+			return bakeContext;
+		}
+
+		@Override
+		public ItemModel.Unbaked sourceModel() {
 			return sourceModel;
 		}
-
-		@Override
-		public Baker baker() {
-			return baker;
-		}
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingPluginContextImpl.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingPluginContextImpl.java
index c0a605f88..c2f299949 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingPluginContextImpl.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingPluginContextImpl.java
@@ -16,13 +16,10 @@
 
 package net.fabricmc.fabric.impl.client.model.loading;
 
-import java.util.Collection;
 import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,7 +38,6 @@ import net.fabricmc.fabric.api.event.EventFactory;
 public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context {
 	private static final Logger LOGGER = LoggerFactory.getLogger(ModelLoadingPluginContextImpl.class);
 
-	final Set<Identifier> extraModels = new LinkedHashSet<>();
 	final Map<Block, BlockStateResolver> blockStateResolvers = new IdentityHashMap<>();
 
 	private static final Identifier[] MODEL_MODIFIER_PHASES = new Identifier[] { ModelModifier.OVERRIDE_PHASE, ModelModifier.DEFAULT_PHASE, ModelModifier.WRAP_PHASE, ModelModifier.WRAP_LAST_PHASE };
@@ -57,28 +53,6 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
 
 		return model;
 	}, MODEL_MODIFIER_PHASES);
-	private final Event<ModelModifier.BeforeBake> beforeBakeModifiers = EventFactory.createWithPhases(ModelModifier.BeforeBake.class, modifiers -> (model, context) -> {
-		for (ModelModifier.BeforeBake modifier : modifiers) {
-			try {
-				model = modifier.modifyModelBeforeBake(model, context);
-			} catch (Exception exception) {
-				LOGGER.error("Failed to modify unbaked model before bake", exception);
-			}
-		}
-
-		return model;
-	}, MODEL_MODIFIER_PHASES);
-	private final Event<ModelModifier.AfterBake> afterBakeModifiers = EventFactory.createWithPhases(ModelModifier.AfterBake.class, modifiers -> (model, context) -> {
-		for (ModelModifier.AfterBake modifier : modifiers) {
-			try {
-				model = modifier.modifyModelAfterBake(model, context);
-			} catch (Exception exception) {
-				LOGGER.error("Failed to modify baked model after bake", exception);
-			}
-		}
-
-		return model;
-	}, MODEL_MODIFIER_PHASES);
 	private final Event<ModelModifier.OnLoadBlock> onLoadBlockModifiers = EventFactory.createWithPhases(ModelModifier.OnLoadBlock.class, modifiers -> (model, context) -> {
 		for (ModelModifier.OnLoadBlock modifier : modifiers) {
 			try {
@@ -112,18 +86,28 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
 
 		return model;
 	}, MODEL_MODIFIER_PHASES);
-
-	@Override
-	public void addModels(Identifier... ids) {
-		for (Identifier id : ids) {
-			extraModels.add(id);
+	private final Event<ModelModifier.BeforeBakeItem> beforeBakeItemModifiers = EventFactory.createWithPhases(ModelModifier.BeforeBakeItem.class, modifiers -> (model, context) -> {
+		for (ModelModifier.BeforeBakeItem modifier : modifiers) {
+			try {
+				model = modifier.modifyModelBeforeBake(model, context);
+			} catch (Exception exception) {
+				LOGGER.error("Failed to modify unbaked item model before bake", exception);
+			}
 		}
-	}
 
-	@Override
-	public void addModels(Collection<? extends Identifier> ids) {
-		extraModels.addAll(ids);
-	}
+		return model;
+	}, MODEL_MODIFIER_PHASES);
+	private final Event<ModelModifier.AfterBakeItem> afterBakeItemModifiers = EventFactory.createWithPhases(ModelModifier.AfterBakeItem.class, modifiers -> (model, context) -> {
+		for (ModelModifier.AfterBakeItem modifier : modifiers) {
+			try {
+				model = modifier.modifyModelAfterBake(model, context);
+			} catch (Exception exception) {
+				LOGGER.error("Failed to modify baked item model after bake", exception);
+			}
+		}
+
+		return model;
+	}, MODEL_MODIFIER_PHASES);
 
 	@Override
 	public void registerBlockStateResolver(Block block, BlockStateResolver resolver) {
@@ -146,16 +130,6 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
 		return onLoadModifiers;
 	}
 
-	@Override
-	public Event<ModelModifier.BeforeBake> modifyModelBeforeBake() {
-		return beforeBakeModifiers;
-	}
-
-	@Override
-	public Event<ModelModifier.AfterBake> modifyModelAfterBake() {
-		return afterBakeModifiers;
-	}
-
 	@Override
 	public Event<ModelModifier.OnLoadBlock> modifyBlockModelOnLoad() {
 		return onLoadBlockModifiers;
@@ -170,4 +144,14 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
 	public Event<ModelModifier.AfterBakeBlock> modifyBlockModelAfterBake() {
 		return afterBakeBlockModifiers;
 	}
+
+	@Override
+	public Event<ModelModifier.BeforeBakeItem> modifyItemModelBeforeBake() {
+		return beforeBakeItemModifiers;
+	}
+
+	@Override
+	public Event<ModelModifier.AfterBakeItem> modifyItemModelAfterBake() {
+		return afterBakeItemModifiers;
+	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/BakedModelManagerMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/BakedModelManagerMixin.java
index 76fcaef9a..d5d404898 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/BakedModelManagerMixin.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/BakedModelManagerMixin.java
@@ -26,56 +26,32 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
 import com.llamalad7.mixinextras.injector.ModifyReturnValue;
 import com.llamalad7.mixinextras.sugar.Local;
 import org.jetbrains.annotations.Nullable;
-import org.spongepowered.asm.mixin.Final;
 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.ModifyArg;
 import org.spongepowered.asm.mixin.injection.Redirect;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
-import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.render.model.BakedModelManager;
 import net.minecraft.client.render.model.BlockStatesLoader;
-import net.minecraft.client.render.model.ModelBaker;
-import net.minecraft.client.render.model.ReferencedModelsCollector;
+import net.minecraft.client.render.model.UnbakedModel;
 import net.minecraft.client.render.model.json.JsonUnbakedModel;
 import net.minecraft.resource.ResourceManager;
 import net.minecraft.resource.ResourceReloader;
 import net.minecraft.util.Identifier;
 
-import net.fabricmc.fabric.api.client.model.loading.v1.FabricBakedModelManager;
 import net.fabricmc.fabric.api.client.model.loading.v1.UnbakedModelDeserializer;
-import net.fabricmc.fabric.impl.client.model.loading.BakedModelsHooks;
 import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
 import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingPluginManager;
 
 @Mixin(BakedModelManager.class)
-abstract class BakedModelManagerMixin implements FabricBakedModelManager {
-	@Shadow
-	@Final
-	private BakedModel missingBlockModel;
-
+abstract class BakedModelManagerMixin {
 	@Unique
 	@Nullable
 	private volatile CompletableFuture<ModelLoadingEventDispatcher> eventDispatcherFuture;
 
-	@Unique
-	@Nullable
-	private Map<Identifier, BakedModel> extraModels;
-
-	@Override
-	public BakedModel getModel(Identifier id) {
-		if (extraModels == null) {
-			return missingBlockModel;
-		}
-
-		return extraModels.getOrDefault(id, missingBlockModel);
-	}
-
 	@Inject(method = "reload", at = @At("HEAD"))
 	private void onHeadReload(ResourceReloader.Synchronizer synchronizer, ResourceManager manager, Executor prepareExecutor, Executor applyExecutor, CallbackInfoReturnable<CompletableFuture<Void>> cir) {
 		eventDispatcherFuture = ModelLoadingPluginManager.preparePlugins(manager, prepareExecutor).thenApplyAsync(ModelLoadingEventDispatcher::new);
@@ -89,13 +65,18 @@ abstract class BakedModelManagerMixin implements FabricBakedModelManager {
 		});
 	}
 
-	@ModifyExpressionValue(method = "reload", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/BlockStatesLoader.load(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/resource/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
-	private CompletableFuture<BlockStatesLoader.BlockStateDefinition> hookBlockStateModels(CompletableFuture<BlockStatesLoader.BlockStateDefinition> modelsFuture) {
+	@ModifyExpressionValue(method = "reload", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/BakedModelManager.reloadModels(Lnet/minecraft/resource/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
+	private CompletableFuture<Map<Identifier, UnbakedModel>> hookModels(CompletableFuture<Map<Identifier, UnbakedModel>> modelsFuture) {
+		return modelsFuture.thenCombine(eventDispatcherFuture, (models, eventDispatcher) -> eventDispatcher.modifyModelsOnLoad(models));
+	}
+
+	@ModifyExpressionValue(method = "reload", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/BlockStatesLoader.load(Lnet/minecraft/resource/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
+	private CompletableFuture<BlockStatesLoader.LoadedModels> hookBlockStateModels(CompletableFuture<BlockStatesLoader.LoadedModels> modelsFuture) {
 		return modelsFuture.thenCombine(eventDispatcherFuture, (models, eventDispatcher) -> eventDispatcher.modifyBlockModelsOnLoad(models));
 	}
 
-	@ModifyArg(method = "reload", at = @At(value = "INVOKE", target = "java/util/concurrent/CompletableFuture.thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 1), index = 0)
-	private Function<Void, ReferencedModelsCollector> hookModelDiscovery(Function<Void, ReferencedModelsCollector> function) {
+	@ModifyArg(method = "reload", at = @At(value = "INVOKE", target = "java/util/concurrent/CompletableFuture.thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 0), index = 0)
+	private Function<Void, CompletableFuture<?>> hookModelBaking(Function<Void, CompletableFuture<?>> function) {
 		return v -> {
 			CompletableFuture<ModelLoadingEventDispatcher> future = eventDispatcherFuture;
 
@@ -104,25 +85,9 @@ abstract class BakedModelManagerMixin implements FabricBakedModelManager {
 			}
 
 			ModelLoadingEventDispatcher.CURRENT.set(future.join());
-			ReferencedModelsCollector referencedModelsCollector = function.apply(v);
+			CompletableFuture<?> bakingResultFuture = function.apply(v);
 			ModelLoadingEventDispatcher.CURRENT.remove();
-			return referencedModelsCollector;
-		};
-	}
-
-	@ModifyArg(method = "reload", at = @At(value = "INVOKE", target = "java/util/concurrent/CompletableFuture.thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 3), index = 0)
-	private Function<Void, Object> hookModelBaking(Function<Void, Object> function) {
-		return v -> {
-			CompletableFuture<ModelLoadingEventDispatcher> future = eventDispatcherFuture;
-
-			if (future == null) {
-				return function.apply(v);
-			}
-
-			ModelLoadingEventDispatcher.CURRENT.set(future.join());
-			Object bakingResult = function.apply(v);
-			ModelLoadingEventDispatcher.CURRENT.remove();
-			return bakingResult;
+			return bakingResultFuture;
 		};
 	}
 
@@ -142,9 +107,4 @@ abstract class BakedModelManagerMixin implements FabricBakedModelManager {
 	private static Object actuallyDeserializeModel(Object originalModel, @Local Reader reader) {
 		return UnbakedModelDeserializer.deserialize(reader);
 	}
-
-	@Inject(method = "upload", at = @At(value = "INVOKE_STRING", target = "net/minecraft/util/profiler/Profiler.swap(Ljava/lang/String;)V", args = "ldc=cache"))
-	private void onUpload(CallbackInfo ci, @Local ModelBaker.BakedModels bakedModels) {
-		extraModels = ((BakedModelsHooks) (Object) bakedModels).fabric_getExtraModels();
-	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakedModelsMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakedModelsMixin.java
deleted file mode 100644
index 9cbc3b6cb..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakedModelsMixin.java
+++ /dev/null
@@ -1,47 +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.mixin.client.model.loading;
-
-import java.util.Map;
-
-import org.jetbrains.annotations.Nullable;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.ModelBaker;
-import net.minecraft.util.Identifier;
-
-import net.fabricmc.fabric.impl.client.model.loading.BakedModelsHooks;
-
-@Mixin(ModelBaker.BakedModels.class)
-abstract class ModelBakerBakedModelsMixin implements BakedModelsHooks {
-	@Unique
-	@Nullable
-	private Map<Identifier, BakedModel> extraModels;
-
-	@Override
-	@Nullable
-	public Map<Identifier, BakedModel> fabric_getExtraModels() {
-		return extraModels;
-	}
-
-	@Override
-	public void fabric_setExtraModels(@Nullable Map<Identifier, BakedModel> extraModels) {
-		this.extraModels = extraModels;
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakerImplMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakerImplMixin.java
deleted file mode 100644
index fb3b6629f..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerBakerImplMixin.java
+++ /dev/null
@@ -1,55 +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.mixin.client.model.loading;
-
-import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
-import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Coerce;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.ModelBakeSettings;
-import net.minecraft.client.render.model.ModelBaker;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.util.Identifier;
-
-import net.fabricmc.fabric.impl.client.model.loading.ModelBakerHooks;
-import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
-
-@Mixin(ModelBaker.BakerImpl.class)
-abstract class ModelBakerBakerImplMixin {
-	@Shadow
-	@Final
-	private ModelBaker field_40571;
-
-	@WrapOperation(method = "bake(Lnet/minecraft/util/Identifier;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/UnbakedModel.bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/Baker;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;"))
-	private BakedModel wrapModelBake(UnbakedModel unbakedModel, @Coerce Baker baker, ModelBakeSettings settings, Operation<BakedModel> operation, Identifier id) {
-		ModelLoadingEventDispatcher dispatcher = ((ModelBakerHooks) this.field_40571).fabric_getDispatcher();
-
-		if (dispatcher == null) {
-			return operation.call(unbakedModel, baker, settings);
-		}
-
-		unbakedModel = dispatcher.modifyModelBeforeBake(unbakedModel, id, settings, baker);
-		BakedModel model = operation.call(unbakedModel, baker, settings);
-		return dispatcher.modifyModelAfterBake(model, id, unbakedModel, settings, baker);
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerMixin.java
index cd0e04a49..6666e8ebd 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerMixin.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ModelBakerMixin.java
@@ -16,11 +16,12 @@
 
 package net.fabricmc.fabric.mixin.client.model.loading;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.function.BiFunction;
 
 import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
 import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
+import com.llamalad7.mixinextras.sugar.Local;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.spongepowered.asm.mixin.Final;
@@ -29,27 +30,29 @@ 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.ModifyArg;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
-import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.render.item.model.ItemModel;
+import net.minecraft.client.render.model.BakedSimpleModel;
 import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.GroupableModel;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.client.render.model.ModelBaker;
-import net.minecraft.client.render.model.ModelRotation;
-import net.minecraft.client.util.ModelIdentifier;
 import net.minecraft.util.Identifier;
 
-import net.fabricmc.fabric.impl.client.model.loading.BakedModelsHooks;
-import net.fabricmc.fabric.impl.client.model.loading.ModelBakerHooks;
 import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
 
 @Mixin(ModelBaker.class)
-abstract class ModelBakerMixin implements ModelBakerHooks {
+abstract class ModelBakerMixin {
 	@Shadow
 	@Final
 	static Logger LOGGER;
 
+	@Shadow
+	@Final
+	Map<Identifier, BakedSimpleModel> simpleModels;
+
 	@Unique
 	@Nullable
 	private ModelLoadingEventDispatcher fabric_eventDispatcher;
@@ -59,39 +62,37 @@ abstract class ModelBakerMixin implements ModelBakerHooks {
 		fabric_eventDispatcher = ModelLoadingEventDispatcher.CURRENT.get();
 	}
 
-	@WrapOperation(method = "method_65737", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/GroupableModel.bake(Lnet/minecraft/client/render/model/Baker;)Lnet/minecraft/client/render/model/BakedModel;"))
-	private BakedModel wrapBlockModelBake(GroupableModel unbakedModel, Baker baker, Operation<BakedModel> operation, ModelBaker.ErrorCollectingSpriteGetter spriteGetter, Map<ModelIdentifier, BakedModel> map, ModelIdentifier id) {
+	@ModifyArg(method = "bake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/FutureModel;newTask(Ljava/util/Map;Ljava/util/function/BiFunction;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 0), index = 1)
+	private BiFunction<BlockState, BlockStateModel.UnbakedGrouped, BlockStateModel> hookBlockModelBake(BiFunction<BlockState, BlockStateModel.UnbakedGrouped, BlockStateModel> bifunction) {
 		if (fabric_eventDispatcher == null) {
-			return operation.call(unbakedModel, baker);
+			return bifunction;
 		}
 
-		unbakedModel = fabric_eventDispatcher.modifyBlockModelBeforeBake(unbakedModel, id, baker);
-		BakedModel model = operation.call(unbakedModel, baker);
-		return fabric_eventDispatcher.modifyBlockModelAfterBake(model, id, unbakedModel, baker);
+		return (state, unbakedModel) -> {
+			ModelLoadingEventDispatcher.CURRENT.set(fabric_eventDispatcher);
+			BlockStateModel model = bifunction.apply(state, unbakedModel);
+			ModelLoadingEventDispatcher.CURRENT.remove();
+			return model;
+		};
 	}
 
-	@Inject(method = "bake", at = @At("RETURN"))
-	private void onReturnBake(ModelBaker.ErrorCollectingSpriteGetter spriteGetter, CallbackInfoReturnable<ModelBaker.BakedModels> cir) {
-		if (fabric_eventDispatcher == null) {
-			return;
+	@WrapOperation(method = "method_68018", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/BlockStateModel$UnbakedGrouped.getModel(Lnet/minecraft/block/BlockState;Lnet/minecraft/client/render/model/Baker;)Lnet/minecraft/client/render/model/BlockStateModel;"))
+	private static BlockStateModel wrapBlockModelBake(BlockStateModel.UnbakedGrouped unbakedModel, BlockState state, Baker baker, Operation<BlockStateModel> operation) {
+		ModelLoadingEventDispatcher eventDispatcher = ModelLoadingEventDispatcher.CURRENT.get();
+
+		if (eventDispatcher == null) {
+			return operation.call(unbakedModel, state, baker);
 		}
 
-		ModelBaker.BakedModels models = cir.getReturnValue();
-		Map<Identifier, BakedModel> extraModels = new HashMap<>();
-		fabric_eventDispatcher.forEachExtraModel(id -> {
-			try {
-				BakedModel model = ((ModelBaker) (Object) this).new BakerImpl(spriteGetter, id::toString).bake(id, ModelRotation.X0_Y0);
-				extraModels.put(id, model);
-			} catch (Exception e) {
-				LOGGER.warn("Unable to bake extra model: '{}': {}", id, e);
-			}
-		});
-		((BakedModelsHooks) (Object) models).fabric_setExtraModels(extraModels);
+		return eventDispatcher.modifyBlockModel(unbakedModel, state, baker, operation);
 	}
 
-	@Override
-	@Nullable
-	public ModelLoadingEventDispatcher fabric_getDispatcher() {
-		return fabric_eventDispatcher;
+	@WrapOperation(method = "method_68019", at = @At(value = "INVOKE", target = "net/minecraft/client/render/item/model/ItemModel$Unbaked.bake(Lnet/minecraft/client/render/item/model/ItemModel$BakeContext;)Lnet/minecraft/client/render/item/model/ItemModel;"))
+	private ItemModel wrapItemModelBake(ItemModel.Unbaked unbakedModel, ItemModel.BakeContext bakeContext, Operation<ItemModel> operation, @Local Identifier itemId) {
+		if (fabric_eventDispatcher == null) {
+			return operation.call(unbakedModel, bakeContext);
+		}
+
+		return fabric_eventDispatcher.modifyItemModel(unbakedModel, itemId, bakeContext, operation);
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ReferencedModelsCollectorMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ReferencedModelsCollectorMixin.java
deleted file mode 100644
index 900eadfee..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/ReferencedModelsCollectorMixin.java
+++ /dev/null
@@ -1,65 +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.mixin.client.model.loading;
-
-import org.jetbrains.annotations.Nullable;
-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.ModifyVariable;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import net.minecraft.client.render.model.ReferencedModelsCollector;
-import net.minecraft.client.render.model.ResolvableModel;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.util.Identifier;
-
-import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
-
-@Mixin(ReferencedModelsCollector.class)
-abstract class ReferencedModelsCollectorMixin {
-	@Unique
-	@Nullable
-	private ModelLoadingEventDispatcher fabric_eventDispatcher;
-
-	@Shadow
-	public abstract void add(ResolvableModel model);
-
-	@Shadow
-	abstract UnbakedModel computeResolvedModel(Identifier id);
-
-	@Inject(method = "<init>", at = @At("RETURN"))
-	private void onReturnInit(CallbackInfo ci) {
-		fabric_eventDispatcher = ModelLoadingEventDispatcher.CURRENT.get();
-
-		if (fabric_eventDispatcher != null) {
-			fabric_eventDispatcher.forEachExtraModel(id -> add(computeResolvedModel(id)));
-		}
-	}
-
-	@ModifyVariable(method = "getModel", at = @At(value = "STORE", ordinal = 0), ordinal = 0)
-	@Nullable
-	private UnbakedModel onLoadModel(@Nullable UnbakedModel model, Identifier id) {
-		if (fabric_eventDispatcher == null) {
-			return model;
-		}
-
-		return fabric_eventDispatcher.modifyModelOnLoad(model, id);
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/WrapperBakedModelMixin.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/WrapperBakedModelMixin.java
deleted file mode 100644
index 6fd7417a1..000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/mixin/client/model/loading/WrapperBakedModelMixin.java
+++ /dev/null
@@ -1,38 +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.mixin.client.model.loading;
-
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.WrapperBakedModel;
-
-import net.fabricmc.fabric.api.client.model.loading.v1.UnwrappableBakedModel;
-
-@Mixin(WrapperBakedModel.class)
-abstract class WrapperBakedModelMixin implements UnwrappableBakedModel {
-	@Shadow
-	@Final
-	protected BakedModel wrapped;
-
-	@Override
-	public BakedModel getWrappedModel() {
-		return wrapped;
-	}
-}
diff --git a/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.accesswidener b/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.accesswidener
deleted file mode 100644
index 12de3c5c2..000000000
--- a/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.accesswidener
+++ /dev/null
@@ -1,3 +0,0 @@
-accessWidener v2 named
-accessible class net/minecraft/client/render/model/ModelBaker$BakerImpl
-accessible method net/minecraft/client/render/model/ModelBaker$BakerImpl <init> (Lnet/minecraft/client/render/model/ModelBaker;Lnet/minecraft/client/render/model/ModelBaker$ErrorCollectingSpriteGetter;Lnet/minecraft/client/model/ModelNameSupplier;)V
diff --git a/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.mixins.json b/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.mixins.json
index d689224f6..0f5410879 100644
--- a/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.mixins.json
+++ b/fabric-model-loading-api-v1/src/client/resources/fabric-model-loading-api-v1.mixins.json
@@ -6,11 +6,7 @@
     "BakedModelManagerMixin",
     "JsonUnbakedModelAccessor",
     "JsonUnbakedModelMixin",
-    "ModelBakerBakedModelsMixin",
-    "ModelBakerBakerImplMixin",
-    "ModelBakerMixin",
-    "ReferencedModelsCollectorMixin",
-    "WrapperBakedModelMixin"
+    "ModelBakerMixin"
   ],
   "injectors": {
     "defaultRequire": 1
diff --git a/fabric-model-loading-api-v1/src/client/resources/fabric.mod.json b/fabric-model-loading-api-v1/src/client/resources/fabric.mod.json
index 7f8660e30..aaef40d68 100644
--- a/fabric-model-loading-api-v1/src/client/resources/fabric.mod.json
+++ b/fabric-model-loading-api-v1/src/client/resources/fabric.mod.json
@@ -25,13 +25,5 @@
       "environment": "client",
       "config": "fabric-model-loading-api-v1.mixins.json"
     }
-  ],
-  "accessWidener": "fabric-model-loading-api-v1.accesswidener",
-  "custom": {
-    "fabric-api:module-lifecycle": "stable",
-    "loom:injected_interfaces": {
-      "net/minecraft/class_1092": [ "net/fabricmc/fabric/api/client/model/loading/v1/FabricBakedModelManager" ],
-      "net/minecraft/class_10200": [ "net/fabricmc/fabric/api/client/model/loading/v1/UnwrappableBakedModel" ]
-    }
-  }
+  ]
 }
diff --git a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java
index aaa3e5d7b..9bb7d1bb1 100644
--- a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java
+++ b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java
@@ -30,28 +30,28 @@ import net.minecraft.client.render.entity.feature.FeatureRenderer;
 import net.minecraft.client.render.entity.feature.FeatureRendererContext;
 import net.minecraft.client.render.entity.model.EntityModel;
 import net.minecraft.client.render.entity.state.LivingEntityRenderState;
-import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.util.math.MathHelper;
 
 public class BakedModelFeatureRenderer<S extends LivingEntityRenderState, M extends EntityModel<S>> extends FeatureRenderer<S, M> {
-	private final Supplier<BakedModel> modelSupplier;
+	private final Supplier<BlockStateModel> modelSupplier;
 
-	public BakedModelFeatureRenderer(FeatureRendererContext<S, M> context, Supplier<BakedModel> modelSupplier) {
+	public BakedModelFeatureRenderer(FeatureRendererContext<S, M> context, Supplier<BlockStateModel> modelSupplier) {
 		super(context);
 		this.modelSupplier = modelSupplier;
 	}
 
 	@Override
 	public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, S state, float limbAngle, float limbDistance) {
-		BakedModel model = modelSupplier.get();
+		BlockStateModel model = modelSupplier.get();
 		VertexConsumer vertices = vertexConsumers.getBuffer(TexturedRenderLayers.getEntityCutout());
 		matrices.push();
 		matrices.multiply(new Quaternionf(new AxisAngle4f(state.age * 0.07F - state.bodyYaw * MathHelper.RADIANS_PER_DEGREE, 0, 1, 0)));
 		matrices.scale(-0.75F, -0.75F, 0.75F);
 		float aboveHead = (float) (Math.sin(state.age * 0.08F)) * 0.5F + 0.5F;
 		matrices.translate(-0.5F, 0.75F + aboveHead, -0.5F);
-		MinecraftClient.getInstance().getBlockRenderManager().getModelRenderer().render(matrices.peek(), vertices, null, model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV);
+		MinecraftClient.getInstance().getBlockRenderManager().getModelRenderer().render(matrices.peek(), vertices, model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV);
 		matrices.pop();
 	}
 }
diff --git a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/ModelTestModClient.java b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/ModelTestModClient.java
index e18336e12..17d5efaad 100644
--- a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/ModelTestModClient.java
+++ b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/ModelTestModClient.java
@@ -16,42 +16,22 @@
 
 package net.fabricmc.fabric.test.model.loading;
 
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-
-import org.jetbrains.annotations.Nullable;
-
 import net.minecraft.block.BlockState;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.CropBlock;
 import net.minecraft.block.HorizontalConnectingBlock;
 import net.minecraft.client.render.entity.PlayerEntityRenderer;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.GroupableModel;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.client.render.model.MissingModel;
-import net.minecraft.client.render.model.ModelBakeSettings;
-import net.minecraft.client.render.model.ModelTextures;
-import net.minecraft.client.render.model.WrapperBakedModel;
-import net.minecraft.client.render.model.json.ModelTransformation;
+import net.minecraft.client.render.model.SimpleBlockStateModel;
 import net.minecraft.client.render.model.json.ModelVariant;
-import net.minecraft.client.render.model.json.WeightedUnbakedModel;
 import net.minecraft.resource.ResourceType;
 import net.minecraft.util.Identifier;
-import net.minecraft.util.math.AffineTransformation;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
-import net.minecraft.util.math.random.Random;
-import net.minecraft.world.BlockRenderView;
 
 import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
 import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier;
-import net.fabricmc.fabric.api.client.model.loading.v1.WrapperUnbakedModel;
 import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback;
-import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
-import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
 import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
 
 public class ModelTestModClient implements ClientModInitializer {
@@ -64,7 +44,7 @@ public class ModelTestModClient implements ClientModInitializer {
 	@Override
 	public void onInitializeClient() {
 		ModelLoadingPlugin.register(pluginContext -> {
-			pluginContext.addModels(HALF_RED_SAND_MODEL_ID);
+			//pluginContext.addModels(HALF_RED_SAND_MODEL_ID);
 
 			// Make wheat stages 1->6 use the same model as stage 0. This can be done with resource packs, this is just a test.
 			pluginContext.registerBlockStateResolver(Blocks.WHEAT, context -> {
@@ -72,8 +52,8 @@ public class ModelTestModClient implements ClientModInitializer {
 
 				Identifier wheatStage0Id = Identifier.ofVanilla("block/wheat_stage0");
 				Identifier wheatStage7Id = Identifier.ofVanilla("block/wheat_stage7");
-				GroupableModel wheatStage0Model = simpleGroupableModel(wheatStage0Id);
-				GroupableModel wheatStage7Model = simpleGroupableModel(wheatStage7Id);
+				BlockStateModel.UnbakedGrouped wheatStage0Model = simpleUnbakedGroupedBlockStateModel(wheatStage0Id);
+				BlockStateModel.UnbakedGrouped wheatStage7Model = simpleUnbakedGroupedBlockStateModel(wheatStage7Id);
 
 				for (int age = 0; age <= 6; age++) {
 					context.setModel(state.with(CropBlock.AGE, age), wheatStage0Model);
@@ -82,52 +62,54 @@ public class ModelTestModClient implements ClientModInitializer {
 				context.setModel(state.with(CropBlock.AGE, 7), wheatStage7Model);
 			});
 
+			// FIXME
 			// Replace the brown glazed terracotta model with a missing model without affecting child models.
 			// Since 1.21.4, the item model is not a child model, so it is also affected.
-			pluginContext.modifyModelOnLoad().register(ModelModifier.WRAP_PHASE, (model, context) -> {
-				if (context.id().equals(BROWN_GLAZED_TERRACOTTA_MODEL_ID)) {
-					return new WrapperUnbakedModel(model) {
-						@Override
-						public void resolve(Resolver resolver) {
-							super.resolve(resolver);
-							resolver.resolve(MissingModel.ID);
-						}
-
-						@Override
-						public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
-							return baker.bake(MissingModel.ID, settings);
-						}
-					};
-				}
-
-				return model;
-			});
+			//pluginContext.modifyModelOnLoad().register(ModelModifier.WRAP_PHASE, (model, context) -> {
+			//	if (context.id().equals(BROWN_GLAZED_TERRACOTTA_MODEL_ID)) {
+			//		return new WrapperUnbakedModel(model) {
+			//			@Override
+			//			public void resolve(Resolver resolver) {
+			//				super.resolve(resolver);
+			//				resolver.resolve(MissingModel.ID);
+			//			}
+			//
+			//			@Override
+			//			public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
+			//				return baker.bake(MissingModel.ID, settings);
+			//			}
+			//		};
+			//	}
+			//
+			//	return model;
+			//});
 
 			// Make oak fences with west: true and everything else false appear to be a missing model visually.
 			BlockState westOakFence = Blocks.OAK_FENCE.getDefaultState().with(HorizontalConnectingBlock.WEST, true);
 			pluginContext.modifyBlockModelOnLoad().register(ModelModifier.OVERRIDE_PHASE, (model, context) -> {
 				if (context.state() == westOakFence) {
-					return simpleGroupableModel(MissingModel.ID);
+					return simpleUnbakedGroupedBlockStateModel(MissingModel.ID);
 				}
 
 				return model;
 			});
 
+			// FIXME
 			// Remove bottom face of gold blocks
-			pluginContext.modifyModelAfterBake().register(ModelModifier.WRAP_PHASE, (model, context) -> {
-				if (context.id().equals(GOLD_BLOCK_MODEL_ID)) {
-					return new DownQuadRemovingModel(model);
-				}
-
-				return model;
-			});
+			//pluginContext.modifyModelAfterBake().register(ModelModifier.WRAP_PHASE, (model, context) -> {
+			//	if (context.id().equals(GOLD_BLOCK_MODEL_ID)) {
+			//		return new DownQuadRemovingModel(model);
+			//	}
+			//
+			//	return model;
+			//});
 		});
 
 		ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(SpecificModelReloadListener.INSTANCE);
 
 		LivingEntityFeatureRendererRegistrationCallback.EVENT.register((entityType, entityRenderer, registrationHelper, context) -> {
 			if (entityRenderer instanceof PlayerEntityRenderer playerRenderer) {
-				registrationHelper.register(new BakedModelFeatureRenderer<>(playerRenderer, SpecificModelReloadListener.INSTANCE::getSpecificModel));
+				//registrationHelper.register(new BakedModelFeatureRenderer<>(playerRenderer, SpecificModelReloadListener.INSTANCE::getSpecificModel));
 			}
 		});
 	}
@@ -136,20 +118,20 @@ public class ModelTestModClient implements ClientModInitializer {
 		return Identifier.of(ID, path);
 	}
 
-	private static GroupableModel simpleGroupableModel(Identifier model) {
-		return new WeightedUnbakedModel(List.of(new ModelVariant(model, AffineTransformation.identity(), false, 1)));
+	private static BlockStateModel.UnbakedGrouped simpleUnbakedGroupedBlockStateModel(Identifier model) {
+		return new SimpleBlockStateModel.Unbaked(new ModelVariant(model)).cached();
 	}
 
-	private static class DownQuadRemovingModel extends WrapperBakedModel implements FabricBakedModel {
-		DownQuadRemovingModel(BakedModel model) {
-			super(model);
-		}
-
-		@Override
-		public void emitBlockQuads(QuadEmitter emitter, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, Predicate<@Nullable Direction> cullTest) {
-			emitter.pushTransform(q -> q.cullFace() != Direction.DOWN);
-			((FabricBakedModel) wrapped).emitBlockQuads(emitter, blockView, state, pos, randomSupplier, cullTest);
-			emitter.popTransform();
-		}
-	}
+	//private static class DownQuadRemovingModel extends WrapperBakedModel implements FabricBakedModel {
+	//	DownQuadRemovingModel(BakedModel model) {
+	//		super(model);
+	//	}
+	//
+	//	@Override
+	//	public void emitBlockQuads(QuadEmitter emitter, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, Predicate<@Nullable Direction> cullTest) {
+	//		emitter.pushTransform(q -> q.cullFace() != Direction.DOWN);
+	//		((FabricBakedModel) wrapped).emitBlockQuads(emitter, blockView, state, pos, randomSupplier, cullTest);
+	//		emitter.popTransform();
+	//	}
+	//}
 }
diff --git a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/SpecificModelReloadListener.java b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/SpecificModelReloadListener.java
index 0e2d19afe..77b0b631b 100644
--- a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/SpecificModelReloadListener.java
+++ b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/SpecificModelReloadListener.java
@@ -19,27 +19,27 @@ package net.fabricmc.fabric.test.model.loading;
 import java.util.Collection;
 import java.util.List;
 
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.BlockStateModel;
 import net.minecraft.resource.ResourceManager;
 import net.minecraft.util.Identifier;
 
 import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys;
 import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
 
+// FIXME
 public class SpecificModelReloadListener implements SimpleSynchronousResourceReloadListener {
 	public static final SpecificModelReloadListener INSTANCE = new SpecificModelReloadListener();
 	public static final Identifier ID = Identifier.of(ModelTestModClient.ID, "specific_model");
 
-	private BakedModel specificModel;
+	private BlockStateModel specificModel;
 
-	public BakedModel getSpecificModel() {
+	public BlockStateModel getSpecificModel() {
 		return specificModel;
 	}
 
 	@Override
 	public void reload(ResourceManager manager) {
-		specificModel = MinecraftClient.getInstance().getBakedModelManager().getModel(ModelTestModClient.HALF_RED_SAND_MODEL_ID);
+		//specificModel = MinecraftClient.getInstance().getBakedModelManager().getModel(ModelTestModClient.HALF_RED_SAND_MODEL_ID);
 	}
 
 	@Override
diff --git a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/UnbakedModelDeserializerTest.java b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/UnbakedModelDeserializerTest.java
index 6451758d5..9c4dde25b 100644
--- a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/UnbakedModelDeserializerTest.java
+++ b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/UnbakedModelDeserializerTest.java
@@ -16,100 +16,82 @@
 
 package net.fabricmc.fabric.test.model.loading;
 
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSyntaxException;
-import com.mojang.serialization.JsonOps;
-import org.jetbrains.annotations.Nullable;
-
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.ModelBakeSettings;
-import net.minecraft.client.render.model.ModelTextures;
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.render.model.json.ModelTransformation;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.JsonHelper;
-import net.minecraft.util.math.AffineTransformation;
-
 import net.fabricmc.api.ClientModInitializer;
-import net.fabricmc.fabric.api.client.model.loading.v1.UnbakedModelDeserializer;
 
+// FIXME
 public class UnbakedModelDeserializerTest implements ClientModInitializer {
 	@Override
 	public void onInitializeClient() {
-		UnbakedModelDeserializer.register(ModelTestModClient.id("transformed"), TransformedModelDeserializer.INSTANCE);
+		//UnbakedModelDeserializer.register(ModelTestModClient.id("transformed"), TransformedModelDeserializer.INSTANCE);
 	}
 
-	private static class TransformedModelDeserializer implements UnbakedModelDeserializer {
-		public static final TransformedModelDeserializer INSTANCE = new TransformedModelDeserializer();
-
-		@Override
-		public UnbakedModel deserialize(JsonObject jsonObject, JsonDeserializationContext context) throws JsonParseException {
-			JsonElement transformationElement = JsonHelper.getElement(jsonObject, "transformation");
-			AffineTransformation transformation = AffineTransformation.ANY_CODEC.parse(JsonOps.INSTANCE, transformationElement).getOrThrow();
-
-			JsonElement parentElement = JsonHelper.getElement(jsonObject, "parent");
-
-			if (JsonHelper.isString(parentElement)) {
-				Identifier parentId = Identifier.of(parentElement.getAsString());
-				return new TransformedUnbakedModel(transformation, parentId);
-			} else if (parentElement.isJsonObject()) {
-				UnbakedModel parent = context.deserialize(parentElement, UnbakedModel.class);
-				return new TransformedUnbakedModel(transformation, parent);
-			} else {
-				throw new JsonSyntaxException("parent must be string or object");
-			}
-		}
-	}
-
-	private static class TransformedUnbakedModel implements UnbakedModel {
-		private final AffineTransformation transformation;
-		@Nullable
-		private final Identifier parentId;
-		private UnbakedModel parent;
-
-		private TransformedUnbakedModel(AffineTransformation transformation, Identifier parentId) {
-			this.transformation = transformation;
-			this.parentId = parentId;
-		}
-
-		private TransformedUnbakedModel(AffineTransformation transformation, UnbakedModel parent) {
-			this.transformation = transformation;
-			parentId = null;
-			this.parent = parent;
-		}
-
-		@Override
-		public void resolve(Resolver resolver) {
-			if (parentId != null) {
-				parent = resolver.resolve(parentId);
-			}
-		}
-
-		@Override
-		public UnbakedModel getParent() {
-			return parent;
-		}
-
-		@Override
-		public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
-			settings = new SimpleModelBakeSettings(settings.getRotation().multiply(this.transformation), settings.isUvLocked());
-			return parent.bake(textures, baker, settings, ambientOcclusion, isSideLit, transformation);
-		}
-	}
-
-	private record SimpleModelBakeSettings(AffineTransformation transformation, boolean uvLocked) implements ModelBakeSettings {
-		@Override
-		public AffineTransformation getRotation() {
-			return transformation;
-		}
-
-		@Override
-		public boolean isUvLocked() {
-			return uvLocked;
-		}
-	}
+	//private static class TransformedModelDeserializer implements UnbakedModelDeserializer {
+	//	public static final TransformedModelDeserializer INSTANCE = new TransformedModelDeserializer();
+	//
+	//	@Override
+	//	public UnbakedModel deserialize(JsonObject jsonObject, JsonDeserializationContext context) throws JsonParseException {
+	//		JsonElement transformationElement = JsonHelper.getElement(jsonObject, "transformation");
+	//		AffineTransformation transformation = AffineTransformation.ANY_CODEC.parse(JsonOps.INSTANCE, transformationElement).getOrThrow();
+	//
+	//		JsonElement parentElement = JsonHelper.getElement(jsonObject, "parent");
+	//
+	//		if (JsonHelper.isString(parentElement)) {
+	//			Identifier parentId = Identifier.of(parentElement.getAsString());
+	//			return new TransformedUnbakedModel(transformation, parentId);
+	//		} else if (parentElement.isJsonObject()) {
+	//			UnbakedModel parent = context.deserialize(parentElement, UnbakedModel.class);
+	//			return new TransformedUnbakedModel(transformation, parent);
+	//		} else {
+	//			throw new JsonSyntaxException("parent must be string or object");
+	//		}
+	//	}
+	//}
+	//
+	//private static class TransformedUnbakedModel implements UnbakedModel {
+	//	private final AffineTransformation transformation;
+	//	@Nullable
+	//	private final Identifier parentId;
+	//	private UnbakedModel parent;
+	//
+	//	private TransformedUnbakedModel(AffineTransformation transformation, Identifier parentId) {
+	//		this.transformation = transformation;
+	//		this.parentId = parentId;
+	//	}
+	//
+	//	private TransformedUnbakedModel(AffineTransformation transformation, UnbakedModel parent) {
+	//		this.transformation = transformation;
+	//		parentId = null;
+	//		this.parent = parent;
+	//	}
+	//
+	//	@Override
+	//	public void resolve(Resolver resolver) {
+	//		if (parentId != null) {
+	//			parent = resolver.resolve(parentId);
+	//		}
+	//	}
+	//
+	//	@Override
+	//	public UnbakedModel getParent() {
+	//		return parent;
+	//	}
+	//
+	//	@Override
+	//	public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
+	//		settings = new SimpleModelBakeSettings(settings.getRotation().multiply(this.transformation), settings.isUvLocked());
+	//		return parent.bake(textures, baker, settings, ambientOcclusion, isSideLit, transformation);
+	//	}
+	//}
+	//
+	//private record SimpleModelBakeSettings(AffineTransformation transformation, boolean uvLocked) implements ModelBakeSettings {
+	//	@Override
+	//	public AffineTransformation getRotation() {
+	//		return transformation;
+	//	}
+	//
+	//	@Override
+	//	public boolean isUvLocked() {
+	//		return uvLocked;
+	//	}
+	//}
 }
diff --git a/settings.gradle b/settings.gradle
index df08bdf09..8fce68333 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -49,7 +49,7 @@ include 'fabric-key-binding-api-v1'
 include 'fabric-lifecycle-events-v1'
 include 'fabric-loot-api-v3'
 include 'fabric-message-api-v1'
-//include 'fabric-model-loading-api-v1'
+include 'fabric-model-loading-api-v1'
 include 'fabric-networking-api-v1'
 include 'fabric-object-builder-api-v1'
 include 'fabric-particles-v1'