mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-02 02:00:14 -04:00
Update Model Loading API to 1.21.5 (#4450)
* Update Model Loading API to 25w07a * Update Model Loading API to 25w08a * Fix merge * Move model wrappers to separate package * Remove UnwrappableBlockStateModel * Disallow ModelModifier.OnLoad from receiving or returning null models - Move the point at which OnLoad is invoked earlier to be inline with how OnLoadBlock is invoked * Temporarily remove extra models API --------- Co-authored-by: modmuss <modmuss50@gmail.com>
This commit is contained in:
parent
f16bad8479
commit
4e7a6c5738
30 changed files with 581 additions and 1096 deletions
fabric-model-loading-api-v1
build.gradle
settings.gradlesrc
client
java/net/fabricmc/fabric
api/client/model/loading/v1
BlockStateResolver.javaFabricBakedModelManager.javaModelLoadingPlugin.javaModelModifier.javaUnbakedModelDeserializer.javaUnwrappableBakedModel.java
wrapper
impl/client/model/loading
BakedModelsHooks.javaModelBakerHooks.javaModelLoadingEventDispatcher.javaModelLoadingPluginContextImpl.java
mixin/client/model/loading
resources
testmodClient/java/net/fabricmc/fabric/test/model/loading
|
@ -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')
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() { }
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -6,11 +6,7 @@
|
|||
"BakedModelManagerMixin",
|
||||
"JsonUnbakedModelAccessor",
|
||||
"JsonUnbakedModelMixin",
|
||||
"ModelBakerBakedModelsMixin",
|
||||
"ModelBakerBakerImplMixin",
|
||||
"ModelBakerMixin",
|
||||
"ReferencedModelsCollectorMixin",
|
||||
"WrapperBakedModelMixin"
|
||||
"ModelBakerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue