diff --git a/fabric-model-loading-api-v1/build.gradle b/fabric-model-loading-api-v1/build.gradle
index 3d2819264b..74504e6057 100644
--- a/fabric-model-loading-api-v1/build.gradle
+++ b/fabric-model-loading-api-v1/build.gradle
@@ -3,8 +3,12 @@ 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 4712101bf1..f8623a2660 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,20 @@
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
-import net.minecraft.client.render.model.UnbakedModel;
+import net.minecraft.client.render.model.GroupableModel;
/**
- * Block state resolvers are responsible for mapping each {@link BlockState} of a block to an {@link UnbakedModel}.
+ * 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 can be used to create custom block state formats or dynamically resolve block state models.
*
- *
Use {@link ModelResolver} instead of this interface if interacting with the block and block states directly is not
- * necessary. This includes custom model deserializers and loaders.
+ *
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.
*
- * @see ModelResolver
* @see ModelModifier.OnLoad
+ * @see ModelModifier.OnLoadBlock
*/
@FunctionalInterface
public interface BlockStateResolver {
@@ -44,9 +44,7 @@ public interface BlockStateResolver {
* This method must be called exactly once for each block state.
*
*
Note that if multiple block states share the same unbaked model instance, it will be baked multiple times
- * (once per block state that has the model set), which is not efficient. To improve efficiency in this case, the
- * model should be delegated to using {@link DelegatingUnbakedModel} to ensure that it is only baked once. The inner
- * model can be loaded using {@link ModelResolver} if custom loading logic is necessary.
+ * (once per block state that has the model set).
*/
void resolveBlockStates(Context context);
@@ -66,6 +64,6 @@ interface Context {
* @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, UnbakedModel model);
+ void setModel(BlockState state, GroupableModel model);
}
}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/DelegatingUnbakedModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/DelegatingUnbakedModel.java
deleted file mode 100644
index a4fa129848..0000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/DelegatingUnbakedModel.java
+++ /dev/null
@@ -1,58 +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.Function;
-
-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.UnbakedModel;
-import net.minecraft.client.texture.Sprite;
-import net.minecraft.client.util.SpriteIdentifier;
-import net.minecraft.util.Identifier;
-
-/**
- * An unbaked model that returns another {@link BakedModel} at {@linkplain #bake bake time}.
- * This allows multiple {@link UnbakedModel}s to share the same {@link BakedModel} instance
- * and prevents baking the same model multiple times.
- */
-public final class DelegatingUnbakedModel implements UnbakedModel {
- private final Identifier delegate;
-
- /**
- * Constructs a new delegating model.
- *
- * @param delegate The identifier of the underlying baked model.
- */
- public DelegatingUnbakedModel(Identifier delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public void resolve(Resolver resolver) {
- resolver.resolve(delegate);
- }
-
- @Override
- @Nullable
- public BakedModel bake(Baker baker, Function textureGetter, ModelBakeSettings rotationContainer) {
- return baker.bake(delegate, rotationContainer);
- }
-}
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 ace79da597..615c69712c 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
@@ -21,7 +21,6 @@
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.block.Block;
-import net.minecraft.client.render.model.json.JsonUnbakedModel;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
@@ -71,28 +70,14 @@ interface Context {
*/
void registerBlockStateResolver(Block block, BlockStateResolver resolver);
- /**
- * Event access to register model resolvers.
- */
- Event resolveModel();
-
/**
* Event access to monitor unbaked model loads and replace the loaded model.
*/
Event modifyModelOnLoad();
/**
- * Event access to replace the unbaked model used for baking without replacing the cached model.
- *
- * This is useful for mods which wish to wrap a model without affecting other models that use it as a parent
- * (e.g. wrap a block's model into a non-{@link JsonUnbakedModel} class but still allow the item model to be
- * loaded and baked without exceptions).
- */
- Event modifyModelBeforeBake();
-
- /**
- * Event access to monitor baked model loads and replace the loaded model.
+ * Event access to monitor unbaked block model loads and replace the loaded model.
*/
- Event modifyModelAfterBake();
+ Event modifyBlockModelOnLoad();
}
}
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 9a86ec61d5..fdfc4f018e 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
@@ -16,19 +16,14 @@
package net.fabricmc.fabric.api.client.model.loading.v1;
-import java.util.function.Function;
-
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.UnknownNullability;
-import net.minecraft.client.render.model.BakedModel;
-import net.minecraft.client.render.model.Baker;
-import net.minecraft.client.render.model.ModelBakeSettings;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.render.model.GroupableModel;
+import net.minecraft.client.render.model.ResolvableModel;
import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.event.Event;
@@ -39,9 +34,8 @@
*
* Example use cases:
*
- * - Overriding a model for a particular block state - check if the given top-level identifier is not null,
- * and then check if it has the appropriate variant for that block state. If so, return your desired model,
- * otherwise return the given model.
+ * - 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.
* - Wrapping a model to override certain behaviors - simply return a new model instance and delegate calls
* to the original model as needed.
*
@@ -50,7 +44,7 @@
* 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.
*
- * These callbacks are invoked for every single model that is loaded or baked, so implementations should be
+ *
These callbacks are invoked for every single model that is loaded, so implementations should be
* as efficient as possible.
*/
public final class ModelModifier {
@@ -78,12 +72,18 @@ 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.
*
+ *
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.
+ *
* @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#modifyModelOnLoad
*/
- UnbakedModel modifyModelOnLoad(UnbakedModel model, Context context);
+ @Nullable
+ UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Context context);
/**
* The context for an on load model modification event.
@@ -91,146 +91,38 @@ public interface OnLoad {
@ApiStatus.NonExtendable
interface Context {
/**
- * Models with a resource ID are loaded directly from JSON or a {@link ModelModifier}.
- *
- * @return the identifier of the given model as an {@link Identifier}, or null if {@link #topLevelId()} is
- * not null
+ * The identifier of the model that was loaded.
*/
- @UnknownNullability("#topLevelId() != null")
- Identifier resourceId();
-
- /**
- * Models with a top-level ID are loaded from blockstate files, {@link BlockStateResolver}s, or by copying
- * a previously loaded model.
- *
- * @return the identifier of the given model as a {@link ModelIdentifier}, or null if {@link #resourceId()}
- * is not null
- */
- @UnknownNullability("#resourceId() != null")
- ModelIdentifier topLevelId();
+ Identifier id();
}
}
@FunctionalInterface
- public interface BeforeBake {
+ public interface OnLoadBlock {
/**
- * This handler is invoked to allow modification of the unbaked model instance right before it is baked.
+ * This handler is invoked to allow modification of an unbaked block model right after it is first loaded.
*
* @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 {
- /**
- * Models with a resource ID are loaded directly from JSON or a {@link ModelModifier}.
- *
- * @return the identifier of the given model as an {@link Identifier}, or null if {@link #topLevelId()} is
- * not null
- */
- @UnknownNullability("#topLevelId() != null")
- Identifier resourceId();
-
- /**
- * Models with a top-level ID are loaded from blockstate files, {@link BlockStateResolver}s, or by copying
- * a previously loaded model.
- *
- * @return the identifier of the given model as a {@link ModelIdentifier}, or null if {@link #resourceId()}
- * is not null
- */
- @UnknownNullability("#resourceId() != null")
- ModelIdentifier topLevelId();
-
- /**
- * The function that can be used to retrieve sprites.
- */
- Function textureGetter();
-
- /**
- * 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#getModel get unbaked models} and
- * {@linkplain Baker#bake bake models}.
- */
- 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.
- *
- * Note that the passed baked model may be null and that this handler may return a null baked model, since
- * {@link UnbakedModel#bake} and {@link Baker#bake} may also return null baked models. Null baked models are
- * automatically mapped to the missing model during model retrieval.
- *
- *
For further information, see the docs of {@link ModelLoadingPlugin.Context#modifyModelAfterBake()}.
- *
- * @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
+ * @see ModelLoadingPlugin.Context#modifyBlockModelOnLoad
*/
- @Nullable
- BakedModel modifyModelAfterBake(@Nullable BakedModel model, Context context);
+ GroupableModel modifyModelOnLoad(GroupableModel model, Context context);
/**
- * The context for an after bake model modification event.
+ * The context for an on load block model modification event.
*/
@ApiStatus.NonExtendable
interface Context {
/**
- * Models with a resource ID are loaded directly from JSON or a {@link ModelModifier}.
- *
- * @return the identifier of the given model as an {@link Identifier}, or null if {@link #topLevelId()} is
- * not null
- */
- @UnknownNullability("#topLevelId() != null")
- Identifier resourceId();
-
- /**
- * Models with a top-level ID are loaded from blockstate files, {@link BlockStateResolver}s, or by copying
- * a previously loaded model.
- *
- * @return the identifier of the given model as a {@link ModelIdentifier}, or null if {@link #resourceId()}
- * is not null
- */
- @UnknownNullability("#resourceId() != null")
- ModelIdentifier topLevelId();
-
- /**
- * The unbaked model that is being baked.
- */
- UnbakedModel sourceModel();
-
- /**
- * The function that can be used to retrieve sprites.
- */
- Function textureGetter();
-
- /**
- * The settings this model is being baked with.
+ * The identifier of the model that was loaded.
*/
- ModelBakeSettings settings();
+ ModelIdentifier id();
/**
- * The baker being used to bake this model.
- * It can be used to {@linkplain Baker#getModel get unbaked models} and
- * {@linkplain Baker#bake bake models}.
+ * The corresponding block state of the model that was loaded.
*/
- Baker baker();
+ BlockState state();
}
}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelResolver.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelResolver.java
deleted file mode 100644
index 9ee85033ff..0000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/ModelResolver.java
+++ /dev/null
@@ -1,64 +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 org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.Nullable;
-
-import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.util.Identifier;
-
-/**
- * Model resolvers are able to provide a custom model for specific {@link Identifier}s.
- * In vanilla, these {@link Identifier}s are converted to file paths and used to load
- * a model from JSON. Since model resolvers override this process, they can be used to
- * create custom model formats.
- *
- * Only one resolver may provide a custom model for a certain {@link Identifier}.
- * Thus, resolvers that load models using a custom format could conflict. To avoid
- * conflicts, such resolvers may want to only load files with a mod-suffixed name
- * or only load files that have been explicitly defined elsewhere.
- *
- *
If it is necessary to load and bake an arbitrary model that is not referenced
- * normally, a model resolver can be used in conjunction with
- * {@link ModelLoadingPlugin.Context#addModels} to directly load and bake custom model
- * instances.
- *
- *
Model resolvers are invoked for every single model that will be loaded,
- * so implementations should be as efficient as possible.
- *
- * @see ModelLoadingPlugin.Context#addModels
- */
-@FunctionalInterface
-public interface ModelResolver {
- /**
- * @return the resolved {@link UnbakedModel}, or {@code null} if this resolver does not handle the current {@link Identifier}
- */
- @Nullable
- UnbakedModel resolveModel(Context context);
-
- /**
- * The context for model resolution.
- */
- @ApiStatus.NonExtendable
- interface Context {
- /**
- * The identifier of the model to be loaded.
- */
- Identifier id();
- }
-}
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
new file mode 100644
index 0000000000..80a402643b
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/UnwrappableBakedModel.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ *
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.
+ */
+public interface UnwrappableBakedModel {
+ /**
+ * Return the wrapped model, if there is one at the moment, or {@code null} otherwise.
+ *
+ *
If there are multiple layers of wrapping, this method does not necessarily return the innermost model.
+ */
+ @Nullable
+ BakedModel getWrappedModel();
+
+ /**
+ * 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.
+ *
+ *
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 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/WrapperGroupableModel.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperGroupableModel.java
new file mode 100644
index 0000000000..8a65c11a67
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperGroupableModel.java
@@ -0,0 +1,52 @@
+/*
+ * 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.block.BlockState;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.Baker;
+import net.minecraft.client.render.model.GroupableModel;
+
+/**
+ * A simple implementation of {@link GroupableModel} 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;
+
+ protected WrapperGroupableModel() {
+ }
+
+ protected WrapperGroupableModel(GroupableModel wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public void resolve(Resolver resolver) {
+ wrapped.resolve(resolver);
+ }
+
+ @Override
+ public BakedModel bake(Baker baker) {
+ return wrapped.bake(baker);
+ }
+
+ @Override
+ public Object getEqualityGroup(BlockState state) {
+ return wrapped.getEqualityGroup(state);
+ }
+}
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/WrapperUnbakedModel.java
new file mode 100644
index 0000000000..7a4f382a2e
--- /dev/null
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/api/client/model/loading/v1/WrapperUnbakedModel.java
@@ -0,0 +1,80 @@
+/*
+ * 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 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;
+
+/**
+ * A simple implementation of {@link UnbakedModel} that delegates all method calls to the {@link #wrapped} field.
+ * Implementations must set the {@link #wrapped} field somehow.
+ */
+public abstract class WrapperUnbakedModel implements UnbakedModel {
+ protected UnbakedModel wrapped;
+
+ protected WrapperUnbakedModel() {
+ }
+
+ protected WrapperUnbakedModel(UnbakedModel wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @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);
+ }
+
+ @Override
+ @Nullable
+ public Boolean getAmbientOcclusion() {
+ return wrapped.getAmbientOcclusion();
+ }
+
+ @Override
+ @Nullable
+ public GuiLight getGuiLight() {
+ return wrapped.getGuiLight();
+ }
+
+ @Override
+ @Nullable
+ public ModelTransformation getTransformation() {
+ return wrapped.getTransformation();
+ }
+
+ @Override
+ public ModelTextures.Textures getTextures() {
+ return wrapped.getTextures();
+ }
+
+ @Override
+ @Nullable
+ public UnbakedModel getParent() {
+ return wrapped.getParent();
+ }
+}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BlockStateResolverHolder.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java
similarity index 70%
rename from fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BlockStateResolverHolder.java
rename to fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java
index 3b70bc224c..94f202ec1d 100644
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BlockStateResolverHolder.java
+++ b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakedModelsHooks.java
@@ -16,10 +16,16 @@
package net.fabricmc.fabric.impl.client.model.loading;
-import net.minecraft.block.Block;
+import java.util.Map;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.Identifier;
-import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
+public interface BakedModelsHooks {
+ @Nullable
+ Map fabric_getExtraModels();
-record BlockStateResolverHolder(BlockStateResolver resolver, Block block, Identifier blockId) {
+ void fabric_setExtraModels(@Nullable Map extraModels);
}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakerImplHooks.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakerImplHooks.java
deleted file mode 100644
index 8d088dee1d..0000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/BakerImplHooks.java
+++ /dev/null
@@ -1,26 +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.function.Function;
-
-import net.minecraft.client.texture.Sprite;
-import net.minecraft.client.util.SpriteIdentifier;
-
-public interface BakerImplHooks {
- Function fabric_getTextureGetter();
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoaderHooks.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoaderHooks.java
deleted file mode 100644
index 4a4a0b7476..0000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoaderHooks.java
+++ /dev/null
@@ -1,21 +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;
-
-public interface ModelLoaderHooks {
- ModelLoadingEventDispatcher fabric_getDispatcher();
-}
diff --git a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingConstants.java b/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingConstants.java
deleted file mode 100644
index 7d27e12e0f..0000000000
--- a/fabric-model-loading-api-v1/src/client/java/net/fabricmc/fabric/impl/client/model/loading/ModelLoadingConstants.java
+++ /dev/null
@@ -1,42 +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 net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.util.Identifier;
-
-public final class ModelLoadingConstants {
- /**
- * This variant is used to convert user-provided Identifiers for extra models to ModelIdentifiers, since top-level
- * models that will be baked must have a ModelIdentifier. Models corresponding to the Identifiers will go through
- * ModelModifier.OnLoad, but models corresponding to the ModelIdentifiers will not.
- *
- * This variant must be non-empty, must not contain "=", and must not be equal to "inventory" or "missingno".
- */
- public static final String RESOURCE_SPECIAL_VARIANT = "fabric_resource";
-
- private ModelLoadingConstants() {
- }
-
- public static ModelIdentifier toResourceModelId(Identifier id) {
- return new ModelIdentifier(id, RESOURCE_SPECIAL_VARIANT);
- }
-
- public static boolean isResourceModelId(ModelIdentifier id) {
- return id.variant().equals(RESOURCE_SPECIAL_VARIANT);
- }
-}
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 b80ad7263a..f6a75e21dc 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
@@ -23,28 +23,21 @@
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.function.Function;
import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.UnknownNullability;
import org.slf4j.Logger;
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.model.Baker;
import net.minecraft.client.render.model.BlockStatesLoader;
-import net.minecraft.client.render.model.ModelBakeSettings;
+import net.minecraft.client.render.model.GroupableModel;
import net.minecraft.client.render.model.UnbakedModel;
-import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.ModelIdentifier;
-import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.Identifier;
@@ -52,7 +45,6 @@
import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
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.ModelResolver;
public class ModelLoadingEventDispatcher {
private static final Logger LOGGER = LoggerFactory.getLogger(ModelLoadingEventDispatcher.class);
@@ -60,12 +52,10 @@ public class ModelLoadingEventDispatcher {
private final ModelLoadingPluginContextImpl pluginContext;
- private final ModelResolverContext modelResolverContext = new ModelResolverContext();
private final BlockStateResolverContext blockStateResolverContext = new BlockStateResolverContext();
private final OnLoadModifierContext onLoadModifierContext = new OnLoadModifierContext();
- private final ObjectArrayList beforeBakeModifierContextStack = new ObjectArrayList<>();
- private final ObjectArrayList afterBakeModifierContextStack = new ObjectArrayList<>();
+ private final OnLoadBlockModifierContext onLoadBlockModifierContext = new OnLoadBlockModifierContext();
public ModelLoadingEventDispatcher(List plugins) {
this.pluginContext = new ModelLoadingPluginContextImpl();
@@ -79,15 +69,41 @@ public ModelLoadingEventDispatcher(List plugins) {
}
}
- public void addExtraModels(Consumer extraModelConsumer) {
- for (Identifier id : pluginContext.extraModels) {
- extraModelConsumer.accept(id);
- }
+ public void forEachExtraModel(Consumer extraModelConsumer) {
+ pluginContext.extraModels.forEach(extraModelConsumer);
+ }
+
+ @Nullable
+ public UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Identifier id) {
+ onLoadModifierContext.prepare(id);
+ return pluginContext.modifyModelOnLoad().invoker().modifyModelOnLoad(model, onLoadModifierContext);
}
- public BlockStatesLoader.BlockStateDefinition loadBlockStateModels() {
- Map map = new HashMap<>();
+ public BlockStatesLoader.BlockStateDefinition modifyBlockModelsOnLoad(BlockStatesLoader.BlockStateDefinition models) {
+ Map map = models.models();
+
+ if (!(map instanceof HashMap)) {
+ map = new HashMap<>(map);
+ models = new BlockStatesLoader.BlockStateDefinition(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;
+ });
+
+ return models;
+ }
+
+ private void putResolvedBlockStates(Map map) {
pluginContext.blockStateResolvers.forEach((block, resolver) -> {
Optional> optionalKey = Registries.BLOCK.getKey(block);
@@ -97,22 +113,18 @@ public BlockStatesLoader.BlockStateDefinition loadBlockStateModels() {
Identifier blockId = optionalKey.get().getValue();
- BiConsumer output = (state, model) -> {
+ resolveBlockStates(resolver, block, (state, model) -> {
ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
map.put(modelId, new BlockStatesLoader.BlockModel(state, model));
- };
-
- resolveBlockStates(resolver, block, output);
+ });
});
-
- return new BlockStatesLoader.BlockStateDefinition(map);
}
- private void resolveBlockStates(BlockStateResolver resolver, Block block, BiConsumer output) {
+ private void resolveBlockStates(BlockStateResolver resolver, Block block, BiConsumer output) {
BlockStateResolverContext context = blockStateResolverContext;
context.prepare(block);
- Reference2ReferenceMap resolvedModels = context.models;
+ Reference2ReferenceMap resolvedModels = context.models;
ImmutableList allStates = block.getStateManager().getStates();
boolean thrown = false;
@@ -131,7 +143,7 @@ private void resolveBlockStates(BlockStateResolver resolver, Block block, BiCons
} else {
for (BlockState state : allStates) {
@Nullable
- UnbakedModel model = resolvedModels.get(state);
+ GroupableModel 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);
@@ -145,62 +157,14 @@ private void resolveBlockStates(BlockStateResolver resolver, Block block, BiCons
resolvedModels.clear();
}
- @Nullable
- public UnbakedModel resolveModel(Identifier id) {
- modelResolverContext.prepare(id);
- return pluginContext.resolveModel().invoker().resolveModel(modelResolverContext);
- }
-
- public UnbakedModel modifyModelOnLoad(UnbakedModel model, @UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId) {
- onLoadModifierContext.prepare(resourceId, topLevelId);
- return pluginContext.modifyModelOnLoad().invoker().modifyModelOnLoad(model, onLoadModifierContext);
- }
-
- public UnbakedModel modifyModelBeforeBake(UnbakedModel model, @UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId, Function textureGetter, ModelBakeSettings settings, Baker baker) {
- if (beforeBakeModifierContextStack.isEmpty()) {
- beforeBakeModifierContextStack.add(new BeforeBakeModifierContext());
- }
-
- BeforeBakeModifierContext context = beforeBakeModifierContextStack.pop();
- context.prepare(resourceId, topLevelId, textureGetter, settings, baker);
-
- model = pluginContext.modifyModelBeforeBake().invoker().modifyModelBeforeBake(model, context);
-
- beforeBakeModifierContextStack.push(context);
- return model;
- }
-
- @Nullable
- public BakedModel modifyModelAfterBake(@Nullable BakedModel model, @UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId, UnbakedModel sourceModel, Function textureGetter, ModelBakeSettings settings, Baker baker) {
- if (afterBakeModifierContextStack.isEmpty()) {
- afterBakeModifierContextStack.add(new AfterBakeModifierContext());
- }
-
- AfterBakeModifierContext context = afterBakeModifierContextStack.pop();
- context.prepare(resourceId, topLevelId, sourceModel, textureGetter, settings, baker);
-
- model = pluginContext.modifyModelAfterBake().invoker().modifyModelAfterBake(model, context);
-
- afterBakeModifierContextStack.push(context);
- return model;
- }
-
- private static class ModelResolverContext implements ModelResolver.Context {
- private Identifier id;
-
- private void prepare(Identifier id) {
- this.id = id;
- }
-
- @Override
- public Identifier id() {
- return id;
- }
+ private GroupableModel modifyBlockModelOnLoad(GroupableModel model, ModelIdentifier id, BlockState state) {
+ onLoadBlockModifierContext.prepare(id, state);
+ return pluginContext.modifyBlockModelOnLoad().invoker().modifyModelOnLoad(model, onLoadBlockModifierContext);
}
private static class BlockStateResolverContext implements BlockStateResolver.Context {
private Block block;
- private final Reference2ReferenceMap models = new Reference2ReferenceOpenHashMap<>();
+ private final Reference2ReferenceMap models = new Reference2ReferenceOpenHashMap<>();
private void prepare(Block block) {
this.block = block;
@@ -213,7 +177,7 @@ public Block block() {
}
@Override
- public void setModel(BlockState state, UnbakedModel model) {
+ public void setModel(BlockState state, GroupableModel model) {
Objects.requireNonNull(model, "state cannot be null");
Objects.requireNonNull(model, "model cannot be null");
@@ -228,123 +192,35 @@ public void setModel(BlockState state, UnbakedModel model) {
}
private static class OnLoadModifierContext implements ModelModifier.OnLoad.Context {
- @UnknownNullability
- private Identifier resourceId;
- @UnknownNullability
- private ModelIdentifier topLevelId;
-
- private void prepare(@UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId) {
- this.resourceId = resourceId;
- this.topLevelId = topLevelId;
- }
-
- @Override
- @UnknownNullability("#topLevelId() != null")
- public Identifier resourceId() {
- return resourceId;
- }
-
- @Override
- @UnknownNullability("#resourceId() != null")
- public ModelIdentifier topLevelId() {
- return topLevelId;
- }
- }
-
- private static class BeforeBakeModifierContext implements ModelModifier.BeforeBake.Context {
- @UnknownNullability
- private Identifier resourceId;
- @UnknownNullability
- private ModelIdentifier topLevelId;
- private Function textureGetter;
- private ModelBakeSettings settings;
- private Baker baker;
-
- private void prepare(@UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId, Function textureGetter, ModelBakeSettings settings, Baker baker) {
- this.resourceId = resourceId;
- this.topLevelId = topLevelId;
- this.textureGetter = textureGetter;
- this.settings = settings;
- this.baker = baker;
- }
-
- @Override
- @UnknownNullability("#topLevelId() != null")
- public Identifier resourceId() {
- return resourceId;
- }
-
- @Override
- @UnknownNullability("#resourceId() != null")
- public ModelIdentifier topLevelId() {
- return topLevelId;
- }
-
- @Override
- public Function textureGetter() {
- return textureGetter;
- }
+ private Identifier id;
- @Override
- public ModelBakeSettings settings() {
- return settings;
+ private void prepare(Identifier id) {
+ this.id = id;
}
@Override
- public Baker baker() {
- return baker;
+ public Identifier id() {
+ return id;
}
}
- private static class AfterBakeModifierContext implements ModelModifier.AfterBake.Context {
- @UnknownNullability
- private Identifier resourceId;
- @UnknownNullability
- private ModelIdentifier topLevelId;
- private UnbakedModel sourceModel;
- private Function textureGetter;
- private ModelBakeSettings settings;
- private Baker baker;
-
- private void prepare(@UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId, UnbakedModel sourceModel, Function textureGetter, ModelBakeSettings settings, Baker baker) {
- this.resourceId = resourceId;
- this.topLevelId = topLevelId;
- this.sourceModel = sourceModel;
- this.textureGetter = textureGetter;
- this.settings = settings;
- this.baker = baker;
- }
+ private static class OnLoadBlockModifierContext implements ModelModifier.OnLoadBlock.Context {
+ private ModelIdentifier id;
+ private BlockState state;
- @Override
- @UnknownNullability("#topLevelId() != null")
- public Identifier resourceId() {
- return resourceId;
- }
-
- @Override
- @UnknownNullability("#resourceId() != null")
- public ModelIdentifier topLevelId() {
- return topLevelId;
- }
-
- @Override
- public UnbakedModel sourceModel() {
- return sourceModel;
- }
-
- @Override
- public Function textureGetter() {
- return textureGetter;
+ private void prepare(ModelIdentifier id, BlockState state) {
+ this.id = id;
+ this.state = state;
}
@Override
- public ModelBakeSettings settings() {
- return settings;
+ public ModelIdentifier id() {
+ return id;
}
@Override
- public Baker baker() {
- return baker;
+ public BlockState state() {
+ return state;
}
}
}
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 bd7c7211bd..41c2d8ad7a 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
@@ -28,7 +28,6 @@
import org.slf4j.LoggerFactory;
import net.minecraft.block.Block;
-import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.Identifier;
@@ -36,7 +35,6 @@
import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
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.ModelResolver;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@@ -46,22 +44,6 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
final Set extraModels = new LinkedHashSet<>();
final Map blockStateResolvers = new IdentityHashMap<>();
- private final Event modelResolvers = EventFactory.createArrayBacked(ModelResolver.class, resolvers -> context -> {
- for (ModelResolver resolver : resolvers) {
- try {
- UnbakedModel model = resolver.resolveModel(context);
-
- if (model != null) {
- return model;
- }
- } catch (Exception exception) {
- LOGGER.error("Failed to resolve model", exception);
- }
- }
-
- return null;
- });
-
private static final Identifier[] MODEL_MODIFIER_PHASES = new Identifier[] { ModelModifier.OVERRIDE_PHASE, ModelModifier.DEFAULT_PHASE, ModelModifier.WRAP_PHASE, ModelModifier.WRAP_LAST_PHASE };
private final Event onLoadModifiers = EventFactory.createWithPhases(ModelModifier.OnLoad.class, modifiers -> (model, context) -> {
@@ -75,32 +57,18 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
return model;
}, MODEL_MODIFIER_PHASES);
- private final Event beforeBakeModifiers = EventFactory.createWithPhases(ModelModifier.BeforeBake.class, modifiers -> (model, context) -> {
- for (ModelModifier.BeforeBake modifier : modifiers) {
+ private final Event onLoadBlockModifiers = EventFactory.createWithPhases(ModelModifier.OnLoadBlock.class, modifiers -> (model, context) -> {
+ for (ModelModifier.OnLoadBlock 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 afterBakeModifiers = EventFactory.createWithPhases(ModelModifier.AfterBake.class, modifiers -> (model, context) -> {
- for (ModelModifier.AfterBake modifier : modifiers) {
- try {
- model = modifier.modifyModelAfterBake(model, context);
+ model = modifier.modifyModelOnLoad(model, context);
} catch (Exception exception) {
- LOGGER.error("Failed to modify baked model after bake", exception);
+ LOGGER.error("Failed to modify unbaked block model on load", exception);
}
}
return model;
}, MODEL_MODIFIER_PHASES);
- public ModelLoadingPluginContextImpl() {
- }
-
@Override
public void addModels(Identifier... ids) {
for (Identifier id : ids) {
@@ -129,23 +97,13 @@ public void registerBlockStateResolver(Block block, BlockStateResolver resolver)
}
}
- @Override
- public Event resolveModel() {
- return modelResolvers;
- }
-
@Override
public Event modifyModelOnLoad() {
return onLoadModifiers;
}
@Override
- public Event modifyModelBeforeBake() {
- return beforeBakeModifiers;
- }
-
- @Override
- public Event modifyModelAfterBake() {
- return afterBakeModifiers;
+ public Event modifyBlockModelOnLoad() {
+ return onLoadBlockModifiers;
}
}
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 b1cb3ac0c7..cc46cd6ad7 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
@@ -16,52 +16,60 @@
package net.fabricmc.fabric.mixin.client.model.loading;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
-import java.util.function.BiFunction;
import java.util.function.Function;
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.util.ModelIdentifier;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceReloader;
import net.minecraft.util.Identifier;
-import net.minecraft.util.Pair;
import net.fabricmc.fabric.api.client.model.loading.v1.FabricBakedModelManager;
-import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingConstants;
+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;
+
@Unique
+ @Nullable
private volatile CompletableFuture eventDispatcherFuture;
- @Shadow
- private Map models;
+ @Unique
+ @Nullable
+ private Map extraModels;
@Override
public BakedModel getModel(Identifier id) {
- return models.get(ModelLoadingConstants.toResourceModelId(id));
+ if (extraModels == null) {
+ return missingBlockModel;
+ }
+
+ return extraModels.getOrDefault(id, missingBlockModel);
}
@Inject(method = "reload", at = @At("HEAD"))
@@ -77,51 +85,45 @@ private CompletableFuture resetEventDispatcherFuture(CompletableFuture hookBlockStateModelLoading(CompletableFuture modelsFuture) {
- CompletableFuture resolvedModelsFuture = eventDispatcherFuture.thenApplyAsync(ModelLoadingEventDispatcher::loadBlockStateModels);
- return modelsFuture.thenCombine(resolvedModelsFuture, (models, resolvedModels) -> {
- Map map = models.models();
+ @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 hookBlockStateModels(CompletableFuture modelsFuture) {
+ return modelsFuture.thenCombine(eventDispatcherFuture, (models, eventDispatcher) -> eventDispatcher.modifyBlockModelsOnLoad(models));
+ }
- if (!(map instanceof HashMap)) {
- map = new HashMap<>(map);
- models = new BlockStatesLoader.BlockStateDefinition(map);
- }
+ @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 hookModelDiscovery(Function function) {
+ return v -> {
+ CompletableFuture future = eventDispatcherFuture;
- map.putAll(resolvedModels.models());
- return models;
- });
- }
+ if (future == null) {
+ return function.apply(v);
+ }
- @Redirect(
- method = "reload",
- at = @At(
- value = "INVOKE",
- target = "java/util/concurrent/CompletableFuture.thenCombineAsync(Ljava/util/concurrent/CompletionStage;Ljava/util/function/BiFunction;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;",
- ordinal = 0,
- remap = false
- ))
- private CompletableFuture hookModelDiscovery(
- CompletableFuture self,
- CompletionStage