From 4309b17175d2ae6ee2420b1bab05eba2e1bb1bde Mon Sep 17 00:00:00 2001 From: XFactHD Date: Mon, 20 Jan 2025 03:05:49 +0100 Subject: [PATCH 1/4] Rough implementation of model loading plugins --- .../resources/model/ModelBakery.java.patch | 22 ++ .../resources/model/ModelDiscovery.java.patch | 8 + .../resources/model/ModelManager.java.patch | 9 +- .../neoforge/client/ClientHooks.java | 2 + .../neoforge/client/event/ModelEvent.java | 33 ++- .../loadingplugin/BlockStateResolver.java | 24 ++ .../loadingplugin/ModelLoadingPlugin.java | 71 +++++ .../ModelLoadingPluginManager.java | 252 ++++++++++++++++++ .../model/loadingplugin/package-info.java | 13 + .../oldtest/client/model/MegaModelTest.java | 16 +- .../client/model/TRSRTransformerTest.java | 24 +- 11 files changed, 460 insertions(+), 14 deletions(-) create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java diff --git a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch index 694309384e..4ec9e1014a 100644 --- a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch @@ -38,6 +38,18 @@ } public ModelBakery.BakingResult bakeModels(ModelBakery.TextureGetter p_352431_) { +@@ -68,7 +_,10 @@ + Map map = new HashMap<>(this.unbakedBlockStateModels.size()); + this.unbakedBlockStateModels.forEach((p_386257_, p_386258_) -> { + try { +- BakedModel bakedmodel1 = p_386258_.bake(new ModelBakery.ModelBakerImpl(p_352431_, p_386257_::toString)); ++ ModelBakerImpl baker = new ModelBakerImpl(p_352431_, p_386257_::toString); ++ p_386258_ = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyBlockModelBeforeBake(p_386257_, p_386258_, baker); ++ BakedModel bakedmodel1 = p_386258_.bake(baker); ++ bakedmodel1 = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyBlockModelAfterBake(p_386257_, bakedmodel1, p_386258_, baker); + map.put(p_386257_, bakedmodel1); + } catch (Exception exception) { + LOGGER.warn("Unable to bake model: '{}': {}", p_386257_, exception); @@ -92,7 +_,18 @@ LOGGER.warn("Unable to bake item model: '{}'", p_390101_, exception); } @@ -82,3 +94,13 @@ } @OnlyIn(Dist.CLIENT) +@@ -142,7 +_,9 @@ + return bakedmodel; + } else { + UnbakedModel unbakedmodel = this.getModel(p_252176_); ++ unbakedmodel = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelBeforeBake(p_252176_, unbakedmodel, p_249765_, this); + BakedModel bakedmodel1 = UnbakedModel.bakeWithTopModelValues(unbakedmodel, this, p_249765_); ++ bakedmodel1 = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelAfterBake(p_252176_, bakedmodel1, unbakedmodel, p_249765_, this); + ModelBakery.this.bakedCache.put(modelbakery$bakedcachekey, bakedmodel1); + return bakedmodel1; + } diff --git a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch index dba9824817..04d22e85b1 100644 --- a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch @@ -21,3 +21,11 @@ } public void addRoot(ResolvableModel p_388596_) { +@@ -55,6 +_,7 @@ + + private UnbakedModel loadBlockModel(ResourceLocation p_361274_) { + UnbakedModel unbakedmodel = this.inputModels.get(p_361274_); ++ unbakedmodel = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelOnLoad(p_361274_, unbakedmodel); + if (unbakedmodel == null) { + LOGGER.warn("Missing block model: '{}'", p_361274_); + return this.missingModel; diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index a5710ab8e9..6ca8f519e3 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -32,7 +32,7 @@ } public ClientItem.Properties getItemProperties(ResourceLocation p_390438_) { -@@ -115,6 +_,7 @@ +@@ -115,11 +_,13 @@ public final CompletableFuture reload( PreparableReloadListener.PreparationBarrier p_249079_, ResourceManager p_251134_, Executor p_250550_, Executor p_249221_ ) { @@ -40,6 +40,13 @@ UnbakedModel unbakedmodel = MissingBlockModel.missingModel(); CompletableFuture completablefuture = CompletableFuture.supplyAsync(EntityModelSet::vanilla, p_250550_); CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); + CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); +- CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(unbakedmodel, p_251134_, p_250550_); ++ CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(unbakedmodel, p_251134_, p_250550_) ++ .thenApplyAsync(net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager::modifyBlockModelsOnLoad, p_250550_); + CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); + CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) + .thenApplyAsync( @@ -154,8 +_,10 @@ completablefuture3.join().plainModels(), completablefuture4.join().contents(), diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 40d8b2c25e..b70c115021 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -183,6 +183,7 @@ import net.neoforged.neoforge.client.gui.GuiLayerManager; import net.neoforged.neoforge.client.gui.map.MapDecorationRendererManager; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager; import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForgeConfig; @@ -998,6 +999,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou DimensionTransitionScreenManager.init(); AnimationTypeManager.init(); CoreShaderManager.init(); + ModelLoadingPluginManager.init(); } /** diff --git a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java index a3a234a883..8e56fb0d61 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -8,6 +8,7 @@ import com.google.common.base.Preconditions; import java.util.Collections; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -15,12 +16,16 @@ import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.client.model.UnbakedModelLoader; +import net.neoforged.neoforge.client.model.loadingplugin.BlockStateResolver; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; /** * Houses events related to models. @@ -43,7 +48,10 @@ protected ModelEvent() {} *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

+ * + * @deprecated Use {@link BlockStateResolver}s or {@link ModelLoadingPlugin}s instead */ + @Deprecated public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { private final ModelBakery.BakingResult bakingResult; private final Function textureGetter; @@ -86,7 +94,7 @@ public ModelBakery getModelBakery() { * Fired when the {@link ModelManager} is notified of the resource manager reloading. * Called after the model registry is set up and cached in the {@link net.minecraft.client.renderer.block.BlockModelShaper}.
* The model registry given by this event is unmodifiable. To modify the model registry, use - * {@link ModelEvent.ModifyBakingResult} instead. + * {@link BlockStateResolver}s or {@link ModelLoadingPlugin}s instead. * *

This event is not {@linkplain ICancellableEvent cancellable}.

* @@ -182,4 +190,27 @@ public void register(ResourceLocation key, UnbakedModelLoader loader) { loaders.put(key, loader); } } + + public static class RegisterLoadingPlugins extends ModelEvent implements IModBusEvent { + private final Map resolvers; + private final BiFunction pluginRegistrar; + + @ApiStatus.Internal + public RegisterLoadingPlugins(Map resolvers, BiFunction pluginRegistrar) { + this.resolvers = resolvers; + this.pluginRegistrar = pluginRegistrar; + } + + public void registerResolver(Block block, BlockStateResolver resolver) { + if (resolvers.putIfAbsent(block, resolver) != null) { + throw new IllegalStateException("Duplicate BlockStateResolver registration for block " + block); + } + } + + public void registerPlugin(ResourceLocation id, ModelLoadingPlugin plugin) { + if (pluginRegistrar.apply(id, plugin) != null) { + throw new IllegalStateException("Duplicate ModelLoadingPlugin registration with ID " + id); + } + } + } } diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java new file mode 100644 index 0000000000..ac544bfbc3 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public interface BlockStateResolver { + void resolveBlockStates(Context context); + + interface Context { + Block getBlock(); + + @Nullable + UnbakedBlockStateModel getModel(BlockState state); + + void setModel(BlockState state, UnbakedBlockStateModel model); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java new file mode 100644 index 0000000000..fb1ba62607 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public sealed interface ModelLoadingPlugin { + non-sealed interface ModifyOnLoad extends ModelLoadingPlugin { + @Nullable + default UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Context context) { + return model; + } + + record Context(ResourceLocation id) {} + } + + non-sealed interface ModifyBeforeBake extends ModelLoadingPlugin { + default UnbakedModel modifyModelBeforeBake(UnbakedModel model, Context context) { + return model; + } + + record Context(ResourceLocation id, ModelState modelState, ModelBaker baker) {} + } + + non-sealed interface ModifyAfterBake extends ModelLoadingPlugin { + default BakedModel modifyModelAfterBake(BakedModel model, Context context) { + return model; + } + + record Context(ResourceLocation id, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) {} + } + + non-sealed interface ModifyBlockOnLoad extends ModelLoadingPlugin { + default UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, Context context) { + return model; + } + + interface Context { + ModelResourceLocation id(); + + BlockState state(); + } + } + + non-sealed interface ModifyBlockBeforeBake extends ModelLoadingPlugin { + default UnbakedBlockStateModel modifyBlockModelBeforeBake(UnbakedBlockStateModel model, Context context) { + return model; + } + + record Context(ModelResourceLocation id, ModelBaker baker) {} + } + + non-sealed interface ModifyBlockAfterBake extends ModelLoadingPlugin { + default BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { + return model; + } + + record Context(ModelResourceLocation id, UnbakedBlockStateModel sourceModel, ModelBaker baker) {} + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java new file mode 100644 index 0000000000..e2d3cbddeb --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import com.google.common.collect.ImmutableList; +import com.mojang.logging.LogUtils; +import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.BlockStateModelLoader; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.client.resources.model.ModelState; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.fml.ModLoader; +import net.neoforged.neoforge.client.event.ModelEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +@ApiStatus.Internal +public final class ModelLoadingPluginManager { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Map RESOLVERS = new Reference2ReferenceLinkedOpenHashMap<>(); + private static final Map ON_LOAD_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map BEFORE_BAKE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map AFTER_BAKE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map ON_LOAD_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map BEFORE_BAKE_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map AFTER_BAKE_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + + private ModelLoadingPluginManager() {} + + public static void init() { + ModLoader.postEvent(new ModelEvent.RegisterLoadingPlugins(RESOLVERS, (id, plugin) -> switch (plugin) { + case ModelLoadingPlugin.ModifyOnLoad onLoad -> ON_LOAD_PLUGINS.putIfAbsent(id, onLoad); + case ModelLoadingPlugin.ModifyBeforeBake beforeBake -> BEFORE_BAKE_PLUGINS.putIfAbsent(id, beforeBake); + case ModelLoadingPlugin.ModifyAfterBake afterBake -> AFTER_BAKE_PLUGINS.putIfAbsent(id, afterBake); + case ModelLoadingPlugin.ModifyBlockOnLoad onLoadBlock -> ON_LOAD_BLOCK_PLUGINS.putIfAbsent(id, onLoadBlock); + case ModelLoadingPlugin.ModifyBlockBeforeBake beforeBakeBlock -> BEFORE_BAKE_BLOCK_PLUGINS.putIfAbsent(id, beforeBakeBlock); + case ModelLoadingPlugin.ModifyBlockAfterBake afterBakeBlock -> AFTER_BAKE_BLOCK_PLUGINS.putIfAbsent(id, afterBakeBlock); + })); + } + + @Nullable + public static UnbakedModel modifyModelOnLoad(ResourceLocation id, @Nullable UnbakedModel model) { + if (ON_LOAD_PLUGINS.isEmpty()) return model; + + ModelLoadingPlugin.ModifyOnLoad.Context context = new ModelLoadingPlugin.ModifyOnLoad.Context(id); + for (Map.Entry entry : ON_LOAD_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in on-load modifier", entry.getKey()); + } + } + return model; + } + + public static UnbakedModel modifyModelBeforeBake(ResourceLocation id, UnbakedModel model, ModelState modelState, ModelBaker baker) { + if (BEFORE_BAKE_PLUGINS.isEmpty()) return model; + + ModelLoadingPlugin.ModifyBeforeBake.Context context = new ModelLoadingPlugin.ModifyBeforeBake.Context(id, modelState, baker); + for (Map.Entry entry : BEFORE_BAKE_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyModelBeforeBake(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in before-bake modifier", entry.getKey()); + } + } + return model; + } + + public static BakedModel modifyModelAfterBake(ResourceLocation id, BakedModel model, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) { + if (AFTER_BAKE_PLUGINS.isEmpty()) return model; + + ModelLoadingPlugin.ModifyAfterBake.Context context = new ModelLoadingPlugin.ModifyAfterBake.Context(id, sourceModel, modelState, baker); + for (Map.Entry entry : AFTER_BAKE_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyModelAfterBake(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in after-bake modifier", entry.getKey()); + } + } + return model; + } + + public static BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { + if (RESOLVERS.isEmpty() && ON_LOAD_BLOCK_PLUGINS.isEmpty()) return models; + + Map map = models.models(); + if (!(map instanceof HashMap)) { + map = new HashMap<>(map); + models = new BlockStateModelLoader.LoadedModels(map); + } + + if (!RESOLVERS.isEmpty()) { + resolveBlockStates(map); + } + if (!ON_LOAD_BLOCK_PLUGINS.isEmpty()) { + modifyBlockModelsOnLoad(map); + } + + return models; + } + + private static void resolveBlockStates(Map map) { + var context = new BlockStateResolver.Context() { + private final Map models = new Reference2ReferenceOpenHashMap<>(); + private Block block; + + @Override + public Block getBlock() { + return block; + } + + @Override + @Nullable + public UnbakedBlockStateModel getModel(BlockState state) { + ModelResourceLocation id = BlockModelShaper.stateToModelLocation(state); + BlockStateModelLoader.LoadedModel model = map.get(id); + return model != null ? model.model() : null; + } + + @Override + public void setModel(BlockState state, UnbakedBlockStateModel model) { + Objects.requireNonNull(state, "State cannot be null"); + Objects.requireNonNull(model, "Model cannot be null"); + + if (!state.is(this.block)) { + throw new IllegalArgumentException("Attempted to set model for state " + state + " on block " + this.block); + } + + if (this.models.putIfAbsent(state, model) != null) { + throw new IllegalStateException("Duplicate model for state " + state + " on block " + this.block); + } + } + }; + for (Map.Entry entry : RESOLVERS.entrySet()) { + Block block = entry.getKey(); + context.block = block; + + boolean errored = false; + try { + entry.getValue().resolveBlockStates(context); + } catch (Throwable t) { + LOGGER.error("Failed to resolve block state models for block {}. Using missing model for all states.", block, t); + errored = true; + } + + Map resolvedModels = context.models; + if (!errored) { + ResourceLocation blockId = BuiltInRegistries.BLOCK.getResourceKey(block).orElseThrow().location(); + ImmutableList states = block.getStateDefinition().getPossibleStates(); + BiConsumer output = (state, model) -> { + ModelResourceLocation modelId = BlockModelShaper.stateToModelLocation(blockId, state); + map.put(modelId, new BlockStateModelLoader.LoadedModel(state, model)); + }; + + if (resolvedModels.size() == states.size()) { + resolvedModels.forEach(output); + } else { + for (BlockState state : states) { + UnbakedBlockStateModel model = resolvedModels.get(state); + if (model != null) { + output.accept(state, model); + } else { + LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block); + } + } + } + } + resolvedModels.clear(); + } + } + + private static void modifyBlockModelsOnLoad(Map map) { + var context = new ModelLoadingPlugin.ModifyBlockOnLoad.Context() { + private ModelResourceLocation id; + private BlockState state; + + @Override + public ModelResourceLocation id() { + return id; + } + + @Override + public BlockState state() { + return state; + } + }; + map.replaceAll((id, loadedModel) -> { + context.id = id; + context.state = loadedModel.state(); + + UnbakedBlockStateModel model = loadedModel.model(); + for (Map.Entry entry : ON_LOAD_BLOCK_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyBlockModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in on-load-block modifier", entry.getKey()); + } + } + if (loadedModel.model() != model) { + return new BlockStateModelLoader.LoadedModel(loadedModel.state(), model); + } + return loadedModel; + }); + } + + public static UnbakedBlockStateModel modifyBlockModelBeforeBake(ModelResourceLocation id, UnbakedBlockStateModel model, ModelBaker baker) { + if (BEFORE_BAKE_BLOCK_PLUGINS.isEmpty()) return model; + + ModelLoadingPlugin.ModifyBlockBeforeBake.Context context = new ModelLoadingPlugin.ModifyBlockBeforeBake.Context(id, baker); + for (Map.Entry entry : BEFORE_BAKE_BLOCK_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyBlockModelBeforeBake(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in before-bake-block modifier", entry.getKey()); + } + } + return model; + } + + public static BakedModel modifyBlockModelAfterBake(ModelResourceLocation id, BakedModel model, UnbakedBlockStateModel sourceModel, ModelBaker baker) { + if (AFTER_BAKE_BLOCK_PLUGINS.isEmpty()) return model; + + ModelLoadingPlugin.ModifyBlockAfterBake.Context context = new ModelLoadingPlugin.ModifyBlockAfterBake.Context(id, sourceModel, baker); + for (Map.Entry entry : AFTER_BAKE_BLOCK_PLUGINS.entrySet()) { + try { + model = entry.getValue().modifyBlockModelAfterBake(model, context); + } catch (Throwable t) { + LOGGER.error("Model loading plugin {} threw an exception in after-bake-block modifier", entry.getKey()); + } + } + return model; + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java new file mode 100644 index 0000000000..f09f18901e --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.model.loadingplugin; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java index 54fc9d6a20..36130661e0 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java @@ -43,6 +43,7 @@ import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.ModelProperty; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredHolder; @@ -95,10 +96,19 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { @EventBusSubscriber(value = Dist.CLIENT, modid = MOD_ID, bus = EventBusSubscriber.Bus.MOD) public static class ClientEvents { + private static final ModelResourceLocation BLOCK_MODEL_LOC = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath(MOD_ID, REG_NAME), ""); + @SubscribeEvent - public static void onModelBakingCompleted(ModelEvent.ModifyBakingResult event) { - var name = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath(MOD_ID, REG_NAME), ""); - event.getBakingResult().blockStateModels().computeIfPresent(name, (n, m) -> new TransformingModelWrapper(m)); + public static void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { + event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), new ModelLoadingPlugin.ModifyBlockAfterBake() { + @Override + public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { + if (context.id().equals(BLOCK_MODEL_LOC)) { + return new TransformingModelWrapper(model); + } + return model; + } + }); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java index aed6f66b18..324330c8c1 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java @@ -7,7 +7,6 @@ import com.mojang.math.Transformation; import java.util.List; -import java.util.Map; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemTransforms; @@ -15,6 +14,7 @@ import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.CreativeModeTabs; @@ -28,6 +28,7 @@ import net.neoforged.neoforge.client.model.IDynamicBakedModel; import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; import net.neoforged.neoforge.common.util.TransformationHelper; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; @@ -49,7 +50,7 @@ public class TRSRTransformerTest { public TRSRTransformerTest(IEventBus modEventBus) { if (FMLEnvironment.dist.isClient()) { - modEventBus.addListener(this::onModelBake); + modEventBus.addListener(this::onRegisterModelLoadingPlugins); } BLOCKS.register(modEventBus); ITEMS.register(modEventBus); @@ -61,16 +62,21 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { event.accept(TEST_ITEM); } - public void onModelBake(ModelEvent.ModifyBakingResult e) { - Map models = e.getBakingResult().blockStateModels(); - for (ModelResourceLocation id : models.keySet()) { - if (MODID.equals(id.id().getNamespace()) && "test".equals(id.id().getPath())) { - models.put(id, new MyBakedModel(models.get(id))); + private void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins e) { + e.registerPlugin(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), new ModelLoadingPlugin.ModifyBlockAfterBake() { + private static final ModelResourceLocation BLOCK_MODEL_LOC = new ModelResourceLocation(TEST_BLOCK.getId(), ""); + + @Override + public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { + if (context.id().equals(BLOCK_MODEL_LOC)) { + return new MyBakedModel(model); + } + return model; } - } + }); } - public class MyBakedModel implements IDynamicBakedModel { + private static class MyBakedModel implements IDynamicBakedModel { private final BakedModel base; public MyBakedModel(BakedModel base) { From 6135c07ee1b9a31eca80a2485c93179d237e16d4 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Tue, 21 Jan 2025 03:56:44 +0100 Subject: [PATCH 2/4] Rename plugins to modifiers --- .../neoforge/client/event/ModelEvent.java | 16 ++--- .../ModelLoadingPluginManager.java | 62 +++++++++---------- ...lLoadingPlugin.java => ModelModifier.java} | 14 ++--- .../oldtest/client/model/MegaModelTest.java | 4 +- .../client/model/TRSRTransformerTest.java | 4 +- 5 files changed, 50 insertions(+), 50 deletions(-) rename src/main/java/net/neoforged/neoforge/client/model/loadingplugin/{ModelLoadingPlugin.java => ModelModifier.java} (81%) diff --git a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java index 8e56fb0d61..ee7a12b422 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -23,7 +23,7 @@ import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.client.model.UnbakedModelLoader; import net.neoforged.neoforge.client.model.loadingplugin.BlockStateResolver; -import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -49,7 +49,7 @@ protected ModelEvent() {} * *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

* - * @deprecated Use {@link BlockStateResolver}s or {@link ModelLoadingPlugin}s instead + * @deprecated Use {@link BlockStateResolver}s or {@link ModelModifier}s instead */ @Deprecated public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { @@ -94,7 +94,7 @@ public ModelBakery getModelBakery() { * Fired when the {@link ModelManager} is notified of the resource manager reloading. * Called after the model registry is set up and cached in the {@link net.minecraft.client.renderer.block.BlockModelShaper}.
* The model registry given by this event is unmodifiable. To modify the model registry, use - * {@link BlockStateResolver}s or {@link ModelLoadingPlugin}s instead. + * {@link BlockStateResolver}s or {@link ModelModifier}s instead. * *

This event is not {@linkplain ICancellableEvent cancellable}.

* @@ -193,12 +193,12 @@ public void register(ResourceLocation key, UnbakedModelLoader loader) { public static class RegisterLoadingPlugins extends ModelEvent implements IModBusEvent { private final Map resolvers; - private final BiFunction pluginRegistrar; + private final BiFunction modifierRegistrar; @ApiStatus.Internal - public RegisterLoadingPlugins(Map resolvers, BiFunction pluginRegistrar) { + public RegisterLoadingPlugins(Map resolvers, BiFunction modifierRegistrar) { this.resolvers = resolvers; - this.pluginRegistrar = pluginRegistrar; + this.modifierRegistrar = modifierRegistrar; } public void registerResolver(Block block, BlockStateResolver resolver) { @@ -207,8 +207,8 @@ public void registerResolver(Block block, BlockStateResolver resolver) { } } - public void registerPlugin(ResourceLocation id, ModelLoadingPlugin plugin) { - if (pluginRegistrar.apply(id, plugin) != null) { + public void registerModifier(ResourceLocation id, ModelModifier modifier) { + if (modifierRegistrar.apply(id, modifier) != null) { throw new IllegalStateException("Duplicate ModelLoadingPlugin registration with ID " + id); } } diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index e2d3cbddeb..b6d94b6d4d 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -36,32 +36,32 @@ public final class ModelLoadingPluginManager { private static final Logger LOGGER = LogUtils.getLogger(); private static final Map RESOLVERS = new Reference2ReferenceLinkedOpenHashMap<>(); - private static final Map ON_LOAD_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map BEFORE_BAKE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map AFTER_BAKE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map ON_LOAD_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map BEFORE_BAKE_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map AFTER_BAKE_BLOCK_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map ON_LOAD_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map BEFORE_BAKE_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map AFTER_BAKE_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map ON_LOAD_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map BEFORE_BAKE_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map AFTER_BAKE_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); private ModelLoadingPluginManager() {} public static void init() { ModLoader.postEvent(new ModelEvent.RegisterLoadingPlugins(RESOLVERS, (id, plugin) -> switch (plugin) { - case ModelLoadingPlugin.ModifyOnLoad onLoad -> ON_LOAD_PLUGINS.putIfAbsent(id, onLoad); - case ModelLoadingPlugin.ModifyBeforeBake beforeBake -> BEFORE_BAKE_PLUGINS.putIfAbsent(id, beforeBake); - case ModelLoadingPlugin.ModifyAfterBake afterBake -> AFTER_BAKE_PLUGINS.putIfAbsent(id, afterBake); - case ModelLoadingPlugin.ModifyBlockOnLoad onLoadBlock -> ON_LOAD_BLOCK_PLUGINS.putIfAbsent(id, onLoadBlock); - case ModelLoadingPlugin.ModifyBlockBeforeBake beforeBakeBlock -> BEFORE_BAKE_BLOCK_PLUGINS.putIfAbsent(id, beforeBakeBlock); - case ModelLoadingPlugin.ModifyBlockAfterBake afterBakeBlock -> AFTER_BAKE_BLOCK_PLUGINS.putIfAbsent(id, afterBakeBlock); + case ModelModifier.ModifyOnLoad onLoad -> ON_LOAD_MODIFIERS.putIfAbsent(id, onLoad); + case ModelModifier.ModifyBeforeBake beforeBake -> BEFORE_BAKE_MODIFIERS.putIfAbsent(id, beforeBake); + case ModelModifier.ModifyAfterBake afterBake -> AFTER_BAKE_MODIFIERS.putIfAbsent(id, afterBake); + case ModelModifier.ModifyBlockOnLoad onLoadBlock -> ON_LOAD_BLOCK_MODIFIERS.putIfAbsent(id, onLoadBlock); + case ModelModifier.ModifyBlockBeforeBake beforeBakeBlock -> BEFORE_BAKE_BLOCK_MODIFIERS.putIfAbsent(id, beforeBakeBlock); + case ModelModifier.ModifyBlockAfterBake afterBakeBlock -> AFTER_BAKE_BLOCK_MODIFIERS.putIfAbsent(id, afterBakeBlock); })); } @Nullable public static UnbakedModel modifyModelOnLoad(ResourceLocation id, @Nullable UnbakedModel model) { - if (ON_LOAD_PLUGINS.isEmpty()) return model; + if (ON_LOAD_MODIFIERS.isEmpty()) return model; - ModelLoadingPlugin.ModifyOnLoad.Context context = new ModelLoadingPlugin.ModifyOnLoad.Context(id); - for (Map.Entry entry : ON_LOAD_PLUGINS.entrySet()) { + ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); + for (Map.Entry entry : ON_LOAD_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyModelOnLoad(model, context); } catch (Throwable t) { @@ -72,10 +72,10 @@ public static UnbakedModel modifyModelOnLoad(ResourceLocation id, @Nullable Unba } public static UnbakedModel modifyModelBeforeBake(ResourceLocation id, UnbakedModel model, ModelState modelState, ModelBaker baker) { - if (BEFORE_BAKE_PLUGINS.isEmpty()) return model; + if (BEFORE_BAKE_MODIFIERS.isEmpty()) return model; - ModelLoadingPlugin.ModifyBeforeBake.Context context = new ModelLoadingPlugin.ModifyBeforeBake.Context(id, modelState, baker); - for (Map.Entry entry : BEFORE_BAKE_PLUGINS.entrySet()) { + ModelModifier.ModifyBeforeBake.Context context = new ModelModifier.ModifyBeforeBake.Context(id, modelState, baker); + for (Map.Entry entry : BEFORE_BAKE_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyModelBeforeBake(model, context); } catch (Throwable t) { @@ -86,10 +86,10 @@ public static UnbakedModel modifyModelBeforeBake(ResourceLocation id, UnbakedMod } public static BakedModel modifyModelAfterBake(ResourceLocation id, BakedModel model, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) { - if (AFTER_BAKE_PLUGINS.isEmpty()) return model; + if (AFTER_BAKE_MODIFIERS.isEmpty()) return model; - ModelLoadingPlugin.ModifyAfterBake.Context context = new ModelLoadingPlugin.ModifyAfterBake.Context(id, sourceModel, modelState, baker); - for (Map.Entry entry : AFTER_BAKE_PLUGINS.entrySet()) { + ModelModifier.ModifyAfterBake.Context context = new ModelModifier.ModifyAfterBake.Context(id, sourceModel, modelState, baker); + for (Map.Entry entry : AFTER_BAKE_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyModelAfterBake(model, context); } catch (Throwable t) { @@ -100,7 +100,7 @@ public static BakedModel modifyModelAfterBake(ResourceLocation id, BakedModel mo } public static BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { - if (RESOLVERS.isEmpty() && ON_LOAD_BLOCK_PLUGINS.isEmpty()) return models; + if (RESOLVERS.isEmpty() && ON_LOAD_BLOCK_MODIFIERS.isEmpty()) return models; Map map = models.models(); if (!(map instanceof HashMap)) { @@ -111,7 +111,7 @@ public static BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockSt if (!RESOLVERS.isEmpty()) { resolveBlockStates(map); } - if (!ON_LOAD_BLOCK_PLUGINS.isEmpty()) { + if (!ON_LOAD_BLOCK_MODIFIERS.isEmpty()) { modifyBlockModelsOnLoad(map); } @@ -189,7 +189,7 @@ public void setModel(BlockState state, UnbakedBlockStateModel model) { } private static void modifyBlockModelsOnLoad(Map map) { - var context = new ModelLoadingPlugin.ModifyBlockOnLoad.Context() { + var context = new ModelModifier.ModifyBlockOnLoad.Context() { private ModelResourceLocation id; private BlockState state; @@ -208,7 +208,7 @@ public BlockState state() { context.state = loadedModel.state(); UnbakedBlockStateModel model = loadedModel.model(); - for (Map.Entry entry : ON_LOAD_BLOCK_PLUGINS.entrySet()) { + for (Map.Entry entry : ON_LOAD_BLOCK_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyBlockModelOnLoad(model, context); } catch (Throwable t) { @@ -223,10 +223,10 @@ public BlockState state() { } public static UnbakedBlockStateModel modifyBlockModelBeforeBake(ModelResourceLocation id, UnbakedBlockStateModel model, ModelBaker baker) { - if (BEFORE_BAKE_BLOCK_PLUGINS.isEmpty()) return model; + if (BEFORE_BAKE_BLOCK_MODIFIERS.isEmpty()) return model; - ModelLoadingPlugin.ModifyBlockBeforeBake.Context context = new ModelLoadingPlugin.ModifyBlockBeforeBake.Context(id, baker); - for (Map.Entry entry : BEFORE_BAKE_BLOCK_PLUGINS.entrySet()) { + ModelModifier.ModifyBlockBeforeBake.Context context = new ModelModifier.ModifyBlockBeforeBake.Context(id, baker); + for (Map.Entry entry : BEFORE_BAKE_BLOCK_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyBlockModelBeforeBake(model, context); } catch (Throwable t) { @@ -237,10 +237,10 @@ public static UnbakedBlockStateModel modifyBlockModelBeforeBake(ModelResourceLoc } public static BakedModel modifyBlockModelAfterBake(ModelResourceLocation id, BakedModel model, UnbakedBlockStateModel sourceModel, ModelBaker baker) { - if (AFTER_BAKE_BLOCK_PLUGINS.isEmpty()) return model; + if (AFTER_BAKE_BLOCK_MODIFIERS.isEmpty()) return model; - ModelLoadingPlugin.ModifyBlockAfterBake.Context context = new ModelLoadingPlugin.ModifyBlockAfterBake.Context(id, sourceModel, baker); - for (Map.Entry entry : AFTER_BAKE_BLOCK_PLUGINS.entrySet()) { + ModelModifier.ModifyBlockAfterBake.Context context = new ModelModifier.ModifyBlockAfterBake.Context(id, sourceModel, baker); + for (Map.Entry entry : AFTER_BAKE_BLOCK_MODIFIERS.entrySet()) { try { model = entry.getValue().modifyBlockModelAfterBake(model, context); } catch (Throwable t) { diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java similarity index 81% rename from src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java rename to src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java index fb1ba62607..2e30a7c45e 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java @@ -15,8 +15,8 @@ import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; -public sealed interface ModelLoadingPlugin { - non-sealed interface ModifyOnLoad extends ModelLoadingPlugin { +public sealed interface ModelModifier { + non-sealed interface ModifyOnLoad extends ModelModifier { @Nullable default UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Context context) { return model; @@ -25,7 +25,7 @@ default UnbakedModel modifyModelOnLoad(@Nullable UnbakedModel model, Context con record Context(ResourceLocation id) {} } - non-sealed interface ModifyBeforeBake extends ModelLoadingPlugin { + non-sealed interface ModifyBeforeBake extends ModelModifier { default UnbakedModel modifyModelBeforeBake(UnbakedModel model, Context context) { return model; } @@ -33,7 +33,7 @@ default UnbakedModel modifyModelBeforeBake(UnbakedModel model, Context context) record Context(ResourceLocation id, ModelState modelState, ModelBaker baker) {} } - non-sealed interface ModifyAfterBake extends ModelLoadingPlugin { + non-sealed interface ModifyAfterBake extends ModelModifier { default BakedModel modifyModelAfterBake(BakedModel model, Context context) { return model; } @@ -41,7 +41,7 @@ default BakedModel modifyModelAfterBake(BakedModel model, Context context) { record Context(ResourceLocation id, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) {} } - non-sealed interface ModifyBlockOnLoad extends ModelLoadingPlugin { + non-sealed interface ModifyBlockOnLoad extends ModelModifier { default UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, Context context) { return model; } @@ -53,7 +53,7 @@ interface Context { } } - non-sealed interface ModifyBlockBeforeBake extends ModelLoadingPlugin { + non-sealed interface ModifyBlockBeforeBake extends ModelModifier { default UnbakedBlockStateModel modifyBlockModelBeforeBake(UnbakedBlockStateModel model, Context context) { return model; } @@ -61,7 +61,7 @@ default UnbakedBlockStateModel modifyBlockModelBeforeBake(UnbakedBlockStateModel record Context(ModelResourceLocation id, ModelBaker baker) {} } - non-sealed interface ModifyBlockAfterBake extends ModelLoadingPlugin { + non-sealed interface ModifyBlockAfterBake extends ModelModifier { default BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { return model; } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java index 36130661e0..29c7c0402c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java @@ -43,7 +43,7 @@ import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.model.data.ModelProperty; -import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredHolder; @@ -100,7 +100,7 @@ public static class ClientEvents { @SubscribeEvent public static void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { - event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), new ModelLoadingPlugin.ModifyBlockAfterBake() { + event.registerModifier(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), new ModelModifier.ModifyBlockAfterBake() { @Override public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { if (context.id().equals(BLOCK_MODEL_LOC)) { diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java index 324330c8c1..d4cb006947 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java @@ -28,7 +28,7 @@ import net.neoforged.neoforge.client.model.IDynamicBakedModel; import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import net.neoforged.neoforge.common.util.TransformationHelper; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; @@ -63,7 +63,7 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { } private void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins e) { - e.registerPlugin(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), new ModelLoadingPlugin.ModifyBlockAfterBake() { + e.registerModifier(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), new ModelModifier.ModifyBlockAfterBake() { private static final ModelResourceLocation BLOCK_MODEL_LOC = new ModelResourceLocation(TEST_BLOCK.getId(), ""); @Override From 1530b5bfd43eb5e5b74a74aaa2b85dcb809bd240 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Tue, 21 Jan 2025 22:42:32 +0100 Subject: [PATCH 3/4] Mark ModelEvent.BakingCompleted for removal --- .../java/net/neoforged/neoforge/client/event/ModelEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java index ee7a12b422..eb5b27ab79 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -51,7 +51,7 @@ protected ModelEvent() {} * * @deprecated Use {@link BlockStateResolver}s or {@link ModelModifier}s instead */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.21.4") public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { private final ModelBakery.BakingResult bakingResult; private final Function textureGetter; From fcbc5bb1d28d0d9ca8fcaa0700b1797e151282e5 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Sun, 2 Feb 2025 01:21:51 +0100 Subject: [PATCH 4/4] Add preparable loading plugins Rewrite modifier registration to support registration based on data loaded by a preparable loading plugin --- .../resources/model/ModelBakery.java.patch | 32 ++-- .../resources/model/ModelDiscovery.java.patch | 23 ++- .../resources/model/ModelManager.java.patch | 56 +++++- .../neoforge/client/event/ModelEvent.java | 27 ++- .../loadingplugin/ModelLoadingPlugin.java | 22 +++ .../ModelLoadingPluginManager.java | 162 ++++++++++++------ .../model/loadingplugin/ModelModifier.java | 7 + .../PreparableModelLoadingPlugin.java | 16 ++ .../oldtest/client/model/MegaModelTest.java | 4 +- .../client/model/TRSRTransformerTest.java | 6 +- 10 files changed, 265 insertions(+), 90 deletions(-) create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java create mode 100644 src/main/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java diff --git a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch index 4ec9e1014a..8d9adab369 100644 --- a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch @@ -1,13 +1,15 @@ --- a/net/minecraft/client/resources/model/ModelBakery.java +++ b/net/minecraft/client/resources/model/ModelBakery.java -@@ -46,19 +_,36 @@ +@@ -46,19 +_,40 @@ private final Map clientInfos; final Map unbakedPlainModels; final UnbakedModel missingModel; + private final Map standaloneModels; ++ @org.jetbrains.annotations.Nullable ++ private final net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager; + + /** -+ * @deprecated Neo: use {@link #ModelBakery(EntityModelSet, Map, Map, Map, UnbakedModel, Map)} ModelBakery instead} ++ * @deprecated Neo: use {@link #ModelBakery(EntityModelSet, Map, Map, Map, UnbakedModel, Map, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager)} ModelBakery instead} + */ + @Deprecated + public ModelBakery( @@ -17,7 +19,7 @@ + Map p_388404_, + UnbakedModel p_360944_ + ) { -+ this(p_388903_, p_251087_, p_250416_, p_388404_, p_360944_, Map.of()); ++ this(p_388903_, p_251087_, p_250416_, p_388404_, p_360944_, Map.of(), null); + } public ModelBakery( @@ -27,7 +29,8 @@ Map p_388404_, - UnbakedModel p_360944_ + UnbakedModel p_360944_, -+ Map standaloneModels ++ Map standaloneModels, ++ @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager ) { this.entityModelSet = p_388903_; this.unbakedBlockStateModels = p_251087_; @@ -35,18 +38,23 @@ this.unbakedPlainModels = p_388404_; this.missingModel = p_360944_; + this.standaloneModels = standaloneModels; ++ this.pluginManager = pluginManager; } public ModelBakery.BakingResult bakeModels(ModelBakery.TextureGetter p_352431_) { -@@ -68,7 +_,10 @@ +@@ -68,7 +_,14 @@ Map map = new HashMap<>(this.unbakedBlockStateModels.size()); this.unbakedBlockStateModels.forEach((p_386257_, p_386258_) -> { try { - BakedModel bakedmodel1 = p_386258_.bake(new ModelBakery.ModelBakerImpl(p_352431_, p_386257_::toString)); + ModelBakerImpl baker = new ModelBakerImpl(p_352431_, p_386257_::toString); -+ p_386258_ = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyBlockModelBeforeBake(p_386257_, p_386258_, baker); ++ if (this.pluginManager != null) { ++ p_386258_ = this.pluginManager.modifyBlockModelBeforeBake(p_386257_, p_386258_, baker); ++ } + BakedModel bakedmodel1 = p_386258_.bake(baker); -+ bakedmodel1 = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyBlockModelAfterBake(p_386257_, bakedmodel1, p_386258_, baker); ++ if (this.pluginManager != null) { ++ bakedmodel1 = this.pluginManager.modifyBlockModelAfterBake(p_386257_, bakedmodel1, p_386258_, baker); ++ } map.put(p_386257_, bakedmodel1); } catch (Exception exception) { LOGGER.warn("Unable to bake model: '{}': {}", p_386257_, exception); @@ -94,13 +102,17 @@ } @OnlyIn(Dist.CLIENT) -@@ -142,7 +_,9 @@ +@@ -142,7 +_,13 @@ return bakedmodel; } else { UnbakedModel unbakedmodel = this.getModel(p_252176_); -+ unbakedmodel = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelBeforeBake(p_252176_, unbakedmodel, p_249765_, this); ++ if (ModelBakery.this.pluginManager != null) { ++ unbakedmodel = ModelBakery.this.pluginManager.modifyModelBeforeBake(p_252176_, unbakedmodel, p_249765_, this); ++ } BakedModel bakedmodel1 = UnbakedModel.bakeWithTopModelValues(unbakedmodel, this, p_249765_); -+ bakedmodel1 = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelAfterBake(p_252176_, bakedmodel1, unbakedmodel, p_249765_, this); ++ if (ModelBakery.this.pluginManager != null) { ++ bakedmodel1 = ModelBakery.this.pluginManager.modifyModelAfterBake(p_252176_, bakedmodel1, unbakedmodel, p_249765_, this); ++ } ModelBakery.this.bakedCache.put(modelbakery$bakedcachekey, bakedmodel1); return bakedmodel1; } diff --git a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch index 04d22e85b1..4608b29e8f 100644 --- a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch @@ -1,14 +1,27 @@ --- a/net/minecraft/client/resources/model/ModelDiscovery.java +++ b/net/minecraft/client/resources/model/ModelDiscovery.java -@@ -22,6 +_,7 @@ +@@ -22,15 +_,33 @@ final UnbakedModel missingModel; private final List topModels = new ArrayList<>(); private final Map referencedModels = new HashMap<>(); + final Map standaloneModels = new HashMap<>(); ++ @org.jetbrains.annotations.Nullable ++ private final net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager; ++ /** ++ * @deprecated Use {@link ModelDiscovery(Map, UnbakedModel, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager) instead} ++ */ ++ @Deprecated public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_) { ++ this(p_360750_, p_365355_, null); ++ } ++ ++ public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_, @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager) { this.inputModels = p_360750_; -@@ -31,6 +_,12 @@ + this.missingModel = p_365355_; + this.referencedModels.put(MissingBlockModel.LOCATION, p_365355_); ++ this.pluginManager = pluginManager; + } public void registerSpecialModels() { this.referencedModels.put(ItemModelGenerator.GENERATED_ITEM_MODEL_ID, new ItemModelGenerator()); @@ -21,11 +34,13 @@ } public void addRoot(ResolvableModel p_388596_) { -@@ -55,6 +_,7 @@ +@@ -55,6 +_,9 @@ private UnbakedModel loadBlockModel(ResourceLocation p_361274_) { UnbakedModel unbakedmodel = this.inputModels.get(p_361274_); -+ unbakedmodel = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.modifyModelOnLoad(p_361274_, unbakedmodel); ++ if (this.pluginManager != null) { ++ unbakedmodel = this.pluginManager.modifyModelOnLoad(p_361274_, unbakedmodel); ++ } if (unbakedmodel == null) { LOGGER.warn("Missing block model: '{}'", p_361274_); return this.missingModel; diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index 6ca8f519e3..e9d9870b5c 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -1,12 +1,13 @@ --- a/net/minecraft/client/resources/model/ModelManager.java +++ b/net/minecraft/client/resources/model/ModelManager.java -@@ -83,11 +_,15 @@ +@@ -83,11 +_,16 @@ private BakedModel missingModel; private ItemModel missingItemModel; private Object2IntMap modelGroups = Object2IntMaps.emptyMap(); + private final java.util.concurrent.atomic.AtomicReference modelBakery = new java.util.concurrent.atomic.AtomicReference<>(null); + private Map bakedStandaloneModels = Map.of(); + private Set reportedMissingItemModels = new java.util.HashSet<>(); ++ private volatile CompletableFuture pluginFuture = CompletableFuture.completedFuture(null); public ModelManager(TextureManager p_119406_, BlockColors p_119407_, int p_119408_) { this.blockColors = p_119407_; @@ -32,7 +33,7 @@ } public ClientItem.Properties getItemProperties(ResourceLocation p_390438_) { -@@ -115,11 +_,13 @@ +@@ -115,15 +_,18 @@ public final CompletableFuture reload( PreparableReloadListener.PreparationBarrier p_249079_, ResourceManager p_251134_, Executor p_250550_, Executor p_249221_ ) { @@ -42,23 +43,39 @@ CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); - CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(unbakedmodel, p_251134_, p_250550_); ++ this.pluginFuture = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.prepare(p_251134_, p_250550_); + CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(unbakedmodel, p_251134_, p_250550_) -+ .thenApplyAsync(net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager::modifyBlockModelsOnLoad, p_250550_); ++ .thenCombine(this.pluginFuture, (models, pluginManager) -> pluginManager.modifyBlockModelsOnLoad(models)); CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) .thenApplyAsync( -@@ -154,8 +_,10 @@ +- p_390107_ -> discoverModelDependencies(unbakedmodel, completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), ++ p_390107_ -> discoverModelDependencies(unbakedmodel, completablefuture2.join(), completablefuture3.join(), completablefuture4.join(), this.pluginFuture.join()), + p_250550_ + ); + CompletableFuture> completablefuture6 = completablefuture3.thenApplyAsync( +@@ -154,13 +_,20 @@ completablefuture3.join().plainModels(), completablefuture4.join().contents(), modeldiscovery.getReferencedModels(), - unbakedmodel + unbakedmodel, -+ modeldiscovery.standaloneModels ++ modeldiscovery.standaloneModels, ++ this.pluginFuture.join() ); + this.modelBakery.set(modelbakery); return loadModels(Profiler.get(), map1, modelbakery, object2intmap, completablefuture.join(), completablefuture1.join()); }, p_250550_ + ) + .thenCompose(p_252255_ -> p_252255_.readyForUpload.thenApply(p_251581_ -> (ModelManager.ReloadState)p_252255_)) ++ .thenApplyAsync(state -> { ++ this.pluginFuture = CompletableFuture.completedFuture(null); ++ return state; ++ }) + .thenCompose(p_249079_::wait) + .thenAcceptAsync(p_372566_ -> this.apply(p_372566_, Profiler.get()), p_249221_); + } @@ -169,7 +_,7 @@ return CompletableFuture.>supplyAsync(() -> MODEL_LISTER.listMatchingResources(p_251361_), p_252189_) .thenCompose( @@ -77,6 +94,35 @@ } return pair; +@@ -197,13 +_,27 @@ + ); + } + ++ /** ++ * @deprecated Use {@link #discoverModelDependencies(UnbakedModel, Map, BlockStateModelLoader.LoadedModels, ClientItemInfoLoader.LoadedClientInfos, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager)} instead ++ */ ++ @Deprecated + private static ModelDiscovery discoverModelDependencies( + UnbakedModel p_360651_, + Map p_363228_, + BlockStateModelLoader.LoadedModels p_361624_, + ClientItemInfoLoader.LoadedClientInfos p_390496_ + ) { +- ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, p_360651_); ++ return discoverModelDependencies(p_360651_, p_363228_, p_361624_, p_390496_, null); ++ } ++ ++ private static ModelDiscovery discoverModelDependencies( ++ UnbakedModel p_360651_, ++ Map p_363228_, ++ BlockStateModelLoader.LoadedModels p_361624_, ++ ClientItemInfoLoader.LoadedClientInfos p_390496_, ++ @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager ++ ) { ++ ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, p_360651_, pluginManager); + p_361624_.forResolving().forEach(modeldiscovery::addRoot); + p_390496_.contents().values().forEach(p_390109_ -> modeldiscovery.addRoot(p_390109_.model())); + modeldiscovery.registerSpecialModels(); @@ -261,6 +_,8 @@ p_386267_.stream().sorted().map(p_386265_ -> " " + p_386265_).collect(Collectors.joining("\n")) ) diff --git a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java index eb5b27ab79..aaed5d42ce 100644 --- a/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/main/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -8,7 +8,6 @@ import com.google.common.base.Preconditions; import java.util.Collections; import java.util.Map; -import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -16,16 +15,16 @@ import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelManager; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Block; import net.neoforged.bus.api.Event; import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.fml.LogicalSide; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.client.model.UnbakedModelLoader; import net.neoforged.neoforge.client.model.loadingplugin.BlockStateResolver; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; +import net.neoforged.neoforge.client.model.loadingplugin.PreparableModelLoadingPlugin; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; /** * Houses events related to models. @@ -192,24 +191,24 @@ public void register(ResourceLocation key, UnbakedModelLoader loader) { } public static class RegisterLoadingPlugins extends ModelEvent implements IModBusEvent { - private final Map resolvers; - private final BiFunction modifierRegistrar; + private final Map plugins; + private final Map> preparablePlugins; @ApiStatus.Internal - public RegisterLoadingPlugins(Map resolvers, BiFunction modifierRegistrar) { - this.resolvers = resolvers; - this.modifierRegistrar = modifierRegistrar; + public RegisterLoadingPlugins(Map plugins, Map> preparablePlugins) { + this.plugins = plugins; + this.preparablePlugins = preparablePlugins; } - public void registerResolver(Block block, BlockStateResolver resolver) { - if (resolvers.putIfAbsent(block, resolver) != null) { - throw new IllegalStateException("Duplicate BlockStateResolver registration for block " + block); + public void registerPlugin(ResourceLocation id, ModelLoadingPlugin plugin) { + if (plugins.putIfAbsent(id, plugin) != null) { + throw new IllegalStateException("Duplicate ModelLoadingPlugin registration with ID " + id); } } - public void registerModifier(ResourceLocation id, ModelModifier modifier) { - if (modifierRegistrar.apply(id, modifier) != null) { - throw new IllegalStateException("Duplicate ModelLoadingPlugin registration with ID " + id); + public void registerPlugin(ResourceLocation id, PreparableModelLoadingPlugin plugin) { + if (preparablePlugins.putIfAbsent(id, plugin) != null) { + throw new IllegalStateException("Duplicate PreparableModelLoadingPlugin registration with ID " + id); } } } diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java new file mode 100644 index 0000000000..e5f93f35b0 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.world.level.block.Block; + +public interface ModelLoadingPlugin { + void initialize(Context context); + + interface Context { + void registerResolver(Block block, BlockStateResolver resolver); + + default void registerModifier(ModelModifier modifier) { + registerModifier(ModelModifier.Phase.DEFAULT, modifier); + } + + void registerModifier(ModelModifier.Phase phase, ModelModifier modifier); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index b6d94b6d4d..51659ac0c5 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -10,10 +10,16 @@ import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.function.BiConsumer; +import net.minecraft.Util; import net.minecraft.client.renderer.block.BlockModelShaper; import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel; import net.minecraft.client.resources.model.BakedModel; @@ -24,6 +30,7 @@ import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.fml.ModLoader; @@ -32,75 +39,122 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -@ApiStatus.Internal public final class ModelLoadingPluginManager { private static final Logger LOGGER = LogUtils.getLogger(); - private static final Map RESOLVERS = new Reference2ReferenceLinkedOpenHashMap<>(); - private static final Map ON_LOAD_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map BEFORE_BAKE_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map AFTER_BAKE_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map ON_LOAD_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map BEFORE_BAKE_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); - private static final Map AFTER_BAKE_BLOCK_MODIFIERS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map> PREPARABLE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private ModelLoadingPluginManager() {} + private final Map resolvers = new Reference2ReferenceLinkedOpenHashMap<>(); + private final List> onLoadModifiers = new ArrayList<>(); + private final List> beforeBakeModifiers = new ArrayList<>(); + private final List> afterBakeModifiers = new ArrayList<>(); + private final List> onLoadBlockModifiers = new ArrayList<>(); + private final List> beforeBakeBlockModifiers = new ArrayList<>(); + private final List> afterBakeBlockModifiers = new ArrayList<>(); + private ModelLoadingPluginManager(List plugins) { + var context = new ModelLoadingPlugin.Context() { + private ResourceLocation pluginId; + + @Override + public void registerResolver(Block block, BlockStateResolver resolver) { + if (resolvers.putIfAbsent(block, resolver) != null) { + throw new IllegalStateException("Duplicate BlockStateResolver registration for block " + block); + } + } + + @Override + public void registerModifier(ModelModifier.Phase phase, ModelModifier modifier) { + switch (modifier) { + case ModelModifier.ModifyOnLoad onLoad -> onLoadModifiers.add(new ModifierEntry<>(pluginId, phase, onLoad)); + case ModelModifier.ModifyBeforeBake beforeBake -> beforeBakeModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBake)); + case ModelModifier.ModifyAfterBake afterBake -> afterBakeModifiers.add(new ModifierEntry<>(pluginId, phase, afterBake)); + case ModelModifier.ModifyBlockOnLoad onLoadBlock -> onLoadBlockModifiers.add(new ModifierEntry<>(pluginId, phase, onLoadBlock)); + case ModelModifier.ModifyBlockBeforeBake beforeBakeBlock -> beforeBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBakeBlock)); + case ModelModifier.ModifyBlockAfterBake afterBakeBlock -> afterBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, afterBakeBlock)); + } + } + }; + for (PluginEntry plugin : plugins) { + context.pluginId = plugin.id; + plugin.plugin.initialize(context); + } + + Comparator> comp = Comparator.comparing(ModifierEntry::phase); + this.onLoadModifiers.sort(comp); + this.beforeBakeModifiers.sort(comp); + this.afterBakeModifiers.sort(comp); + this.onLoadBlockModifiers.sort(comp); + this.beforeBakeBlockModifiers.sort(comp); + this.afterBakeBlockModifiers.sort(comp); + } + + @ApiStatus.Internal public static void init() { - ModLoader.postEvent(new ModelEvent.RegisterLoadingPlugins(RESOLVERS, (id, plugin) -> switch (plugin) { - case ModelModifier.ModifyOnLoad onLoad -> ON_LOAD_MODIFIERS.putIfAbsent(id, onLoad); - case ModelModifier.ModifyBeforeBake beforeBake -> BEFORE_BAKE_MODIFIERS.putIfAbsent(id, beforeBake); - case ModelModifier.ModifyAfterBake afterBake -> AFTER_BAKE_MODIFIERS.putIfAbsent(id, afterBake); - case ModelModifier.ModifyBlockOnLoad onLoadBlock -> ON_LOAD_BLOCK_MODIFIERS.putIfAbsent(id, onLoadBlock); - case ModelModifier.ModifyBlockBeforeBake beforeBakeBlock -> BEFORE_BAKE_BLOCK_MODIFIERS.putIfAbsent(id, beforeBakeBlock); - case ModelModifier.ModifyBlockAfterBake afterBakeBlock -> AFTER_BAKE_BLOCK_MODIFIERS.putIfAbsent(id, afterBakeBlock); - })); + ModLoader.postEvent(new ModelEvent.RegisterLoadingPlugins(PLUGINS, PREPARABLE_PLUGINS)); + } + + public static CompletableFuture prepare(ResourceManager resourceManager, Executor executor) { + List> pluginFutures = new ArrayList<>(); + for (Map.Entry entry : PLUGINS.entrySet()) { + pluginFutures.add(CompletableFuture.completedFuture(new PluginEntry(entry.getKey(), entry.getValue()))); + } + for (Map.Entry> entry : PREPARABLE_PLUGINS.entrySet()) { + pluginFutures.add(preparePlugin(entry.getKey(), entry.getValue(), resourceManager, executor)); + } + return Util.sequence(pluginFutures).thenApplyAsync(ModelLoadingPluginManager::new, executor); + } + + private static CompletableFuture preparePlugin(ResourceLocation pluginId, PreparableModelLoadingPlugin plugin, ResourceManager resourceManager, Executor executor) { + CompletableFuture dataFuture = plugin.load(resourceManager, executor); + return dataFuture.thenApply(data -> new PluginEntry(pluginId, ctx -> plugin.initialize(data, ctx))); } @Nullable - public static UnbakedModel modifyModelOnLoad(ResourceLocation id, @Nullable UnbakedModel model) { - if (ON_LOAD_MODIFIERS.isEmpty()) return model; + public UnbakedModel modifyModelOnLoad(ResourceLocation id, @Nullable UnbakedModel model) { + if (onLoadModifiers.isEmpty()) return model; ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); - for (Map.Entry entry : ON_LOAD_MODIFIERS.entrySet()) { + for (ModifierEntry entry : onLoadModifiers) { try { - model = entry.getValue().modifyModelOnLoad(model, context); + model = entry.modifier.modifyModelOnLoad(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in on-load modifier", entry.getKey()); + LOGGER.error("On-load modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } return model; } - public static UnbakedModel modifyModelBeforeBake(ResourceLocation id, UnbakedModel model, ModelState modelState, ModelBaker baker) { - if (BEFORE_BAKE_MODIFIERS.isEmpty()) return model; + public UnbakedModel modifyModelBeforeBake(ResourceLocation id, UnbakedModel model, ModelState modelState, ModelBaker baker) { + if (beforeBakeModifiers.isEmpty()) return model; ModelModifier.ModifyBeforeBake.Context context = new ModelModifier.ModifyBeforeBake.Context(id, modelState, baker); - for (Map.Entry entry : BEFORE_BAKE_MODIFIERS.entrySet()) { + for (ModifierEntry entry : beforeBakeModifiers) { try { - model = entry.getValue().modifyModelBeforeBake(model, context); + model = entry.modifier.modifyModelBeforeBake(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in before-bake modifier", entry.getKey()); + LOGGER.error("Before-bake modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } return model; } - public static BakedModel modifyModelAfterBake(ResourceLocation id, BakedModel model, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) { - if (AFTER_BAKE_MODIFIERS.isEmpty()) return model; + public BakedModel modifyModelAfterBake(ResourceLocation id, BakedModel model, UnbakedModel sourceModel, ModelState modelState, ModelBaker baker) { + if (afterBakeModifiers.isEmpty()) return model; ModelModifier.ModifyAfterBake.Context context = new ModelModifier.ModifyAfterBake.Context(id, sourceModel, modelState, baker); - for (Map.Entry entry : AFTER_BAKE_MODIFIERS.entrySet()) { + for (ModifierEntry entry : afterBakeModifiers) { try { - model = entry.getValue().modifyModelAfterBake(model, context); + model = entry.modifier.modifyModelAfterBake(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in after-bake modifier", entry.getKey()); + LOGGER.error("After-bake modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } return model; } - public static BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { - if (RESOLVERS.isEmpty() && ON_LOAD_BLOCK_MODIFIERS.isEmpty()) return models; + public BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { + if (resolvers.isEmpty() && onLoadBlockModifiers.isEmpty()) return models; Map map = models.models(); if (!(map instanceof HashMap)) { @@ -108,17 +162,17 @@ public static BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockSt models = new BlockStateModelLoader.LoadedModels(map); } - if (!RESOLVERS.isEmpty()) { + if (!resolvers.isEmpty()) { resolveBlockStates(map); } - if (!ON_LOAD_BLOCK_MODIFIERS.isEmpty()) { + if (!onLoadBlockModifiers.isEmpty()) { modifyBlockModelsOnLoad(map); } return models; } - private static void resolveBlockStates(Map map) { + private void resolveBlockStates(Map map) { var context = new BlockStateResolver.Context() { private final Map models = new Reference2ReferenceOpenHashMap<>(); private Block block; @@ -150,7 +204,7 @@ public void setModel(BlockState state, UnbakedBlockStateModel model) { } } }; - for (Map.Entry entry : RESOLVERS.entrySet()) { + for (Map.Entry entry : resolvers.entrySet()) { Block block = entry.getKey(); context.block = block; @@ -188,7 +242,7 @@ public void setModel(BlockState state, UnbakedBlockStateModel model) { } } - private static void modifyBlockModelsOnLoad(Map map) { + private void modifyBlockModelsOnLoad(Map map) { var context = new ModelModifier.ModifyBlockOnLoad.Context() { private ModelResourceLocation id; private BlockState state; @@ -208,11 +262,11 @@ public BlockState state() { context.state = loadedModel.state(); UnbakedBlockStateModel model = loadedModel.model(); - for (Map.Entry entry : ON_LOAD_BLOCK_MODIFIERS.entrySet()) { + for (ModifierEntry entry : onLoadBlockModifiers) { try { - model = entry.getValue().modifyBlockModelOnLoad(model, context); + model = entry.modifier.modifyBlockModelOnLoad(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in on-load-block modifier", entry.getKey()); + LOGGER.error("On-load-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } if (loadedModel.model() != model) { @@ -222,31 +276,35 @@ public BlockState state() { }); } - public static UnbakedBlockStateModel modifyBlockModelBeforeBake(ModelResourceLocation id, UnbakedBlockStateModel model, ModelBaker baker) { - if (BEFORE_BAKE_BLOCK_MODIFIERS.isEmpty()) return model; + public UnbakedBlockStateModel modifyBlockModelBeforeBake(ModelResourceLocation id, UnbakedBlockStateModel model, ModelBaker baker) { + if (beforeBakeBlockModifiers.isEmpty()) return model; ModelModifier.ModifyBlockBeforeBake.Context context = new ModelModifier.ModifyBlockBeforeBake.Context(id, baker); - for (Map.Entry entry : BEFORE_BAKE_BLOCK_MODIFIERS.entrySet()) { + for (ModifierEntry entry : beforeBakeBlockModifiers) { try { - model = entry.getValue().modifyBlockModelBeforeBake(model, context); + model = entry.modifier.modifyBlockModelBeforeBake(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in before-bake-block modifier", entry.getKey()); + LOGGER.error("Before-bake-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } return model; } - public static BakedModel modifyBlockModelAfterBake(ModelResourceLocation id, BakedModel model, UnbakedBlockStateModel sourceModel, ModelBaker baker) { - if (AFTER_BAKE_BLOCK_MODIFIERS.isEmpty()) return model; + public BakedModel modifyBlockModelAfterBake(ModelResourceLocation id, BakedModel model, UnbakedBlockStateModel sourceModel, ModelBaker baker) { + if (afterBakeBlockModifiers.isEmpty()) return model; ModelModifier.ModifyBlockAfterBake.Context context = new ModelModifier.ModifyBlockAfterBake.Context(id, sourceModel, baker); - for (Map.Entry entry : AFTER_BAKE_BLOCK_MODIFIERS.entrySet()) { + for (ModifierEntry entry : afterBakeBlockModifiers) { try { - model = entry.getValue().modifyBlockModelAfterBake(model, context); + model = entry.modifier.modifyBlockModelAfterBake(model, context); } catch (Throwable t) { - LOGGER.error("Model loading plugin {} threw an exception in after-bake-block modifier", entry.getKey()); + LOGGER.error("After-bake-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); } } return model; } + + private record PluginEntry(ResourceLocation id, ModelLoadingPlugin plugin) {} + + private record ModifierEntry(ResourceLocation owningPlugin, ModelModifier.Phase phase, T modifier) {} } diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java index 2e30a7c45e..5361096ab9 100644 --- a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java @@ -68,4 +68,11 @@ default BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) record Context(ModelResourceLocation id, UnbakedBlockStateModel sourceModel, ModelBaker baker) {} } + + enum Phase { + OVERRIDE, + DEFAULT, + WRAP, + WRAP_LAST, + } } diff --git a/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java new file mode 100644 index 0000000000..23cbaeeda1 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import net.minecraft.server.packs.resources.ResourceManager; + +public interface PreparableModelLoadingPlugin { + CompletableFuture load(ResourceManager resourceManager, Executor executor); + + void initialize(T data, ModelLoadingPlugin.Context context); +} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java index 29c7c0402c..0254042b3f 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java @@ -100,7 +100,7 @@ public static class ClientEvents { @SubscribeEvent public static void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { - event.registerModifier(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), new ModelModifier.ModifyBlockAfterBake() { + event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), ctx -> ctx.registerModifier(new ModelModifier.ModifyBlockAfterBake() { @Override public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { if (context.id().equals(BLOCK_MODEL_LOC)) { @@ -108,7 +108,7 @@ public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { } return model; } - }); + })); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java index d4cb006947..e1208f20e4 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java @@ -62,8 +62,8 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { event.accept(TEST_ITEM); } - private void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins e) { - e.registerModifier(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), new ModelModifier.ModifyBlockAfterBake() { + private void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { + event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), ctx -> ctx.registerModifier(new ModelModifier.ModifyBlockAfterBake() { private static final ModelResourceLocation BLOCK_MODEL_LOC = new ModelResourceLocation(TEST_BLOCK.getId(), ""); @Override @@ -73,7 +73,7 @@ public BakedModel modifyBlockModelAfterBake(BakedModel model, Context context) { } return model; } - }); + })); } private static class MyBakedModel implements IDynamicBakedModel {