diff --git a/src/main/java/ru/bclib/client/render/CustomModelBakery.java b/src/main/java/ru/bclib/client/render/CustomModelBakery.java index b861e6bc..fb116d71 100644 --- a/src/main/java/ru/bclib/client/render/CustomModelBakery.java +++ b/src/main/java/ru/bclib/client/render/CustomModelBakery.java @@ -26,78 +26,90 @@ import java.util.Map; import java.util.Set; public class CustomModelBakery { + private static final Map UNBAKED_CACHE = Maps.newConcurrentMap(); + private static final Set LOADING_STACK = Sets.newConcurrentHashSet(); private static boolean modelsLoaded; - public static boolean areModelsLoaded() { + @Deprecated // Not working with Fabric model API + public static boolean modelsLoaded() { return modelsLoaded; } + @Deprecated // Not working with Fabric model API public static void setModelsLoaded(boolean modelsLoaded) { CustomModelBakery.modelsLoaded = modelsLoaded; } - public static void loadCustomModels(ResourceManager resourceManager, Map unbakedCache, Map topLevelModels, Set loadingStack) { - Map cache = Maps.newConcurrentMap(); - Map topLevel = Maps.newConcurrentMap(); - - Registry.BLOCK.stream().filter(block -> block instanceof BlockModelProvider).parallel().forEach(block -> { + @Deprecated // Not working with Fabric model API + public static void loadCustomModels(ResourceManager resourceManager, Map unbakedCache, Set loadingStack) { + Registry.BLOCK.stream().parallel().filter(block -> block instanceof BlockModelProvider).forEach(block -> { ResourceLocation blockID = Registry.BLOCK.getKey(block); ResourceLocation storageID = new ResourceLocation(blockID.getNamespace(), "blockstates/" + blockID.getPath() + ".json"); - BlockModelProvider provider = (BlockModelProvider) block; if (!resourceManager.hasResource(storageID)) { - ImmutableList states = block.getStateDefinition().getPossibleStates(); - BlockState defaultState = block.defaultBlockState(); - - ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState); - UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, cache); - - if (defaultModel instanceof MultiPart) { - states.forEach(blockState -> { - ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); - topLevel.put(stateID, defaultModel); - cache.put(stateID, defaultModel); - }); - } - else { - states.forEach(blockState -> { - ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); - UnbakedModel model = stateID.equals(defaultStateID) ? defaultModel : provider.getModelVariant(stateID, blockState, cache); - topLevel.put(stateID, model); - cache.put(stateID, model); - }); - } + addBlockModel(blockID, block); } if (Registry.ITEM.get(blockID) != Items.AIR) { - storageID = new ResourceLocation(blockID.getNamespace(), "models/item/" + blockID.getPath() + ".json"); - if (!resourceManager.hasResource(storageID)) { - ResourceLocation itemID = new ModelResourceLocation(blockID.getNamespace(), blockID.getPath(), "inventory"); - BlockModel model = provider.getItemModel(itemID); - topLevel.put(itemID, model); - cache.put(itemID, model); - } + addItemModel(blockID, (ItemModelProvider) block); } }); - Registry.ITEM.stream().filter(item -> item instanceof ItemModelProvider).parallel().forEach(item -> { + Registry.ITEM.stream().parallel().filter(item -> item instanceof ItemModelProvider).forEach(item -> { ResourceLocation registryID = Registry.ITEM.getKey(item); ResourceLocation storageID = new ResourceLocation(registryID.getNamespace(), "models/item/" + registryID.getPath() + ".json"); if (!resourceManager.hasResource(storageID)) { - ResourceLocation itemID = new ModelResourceLocation(registryID.getNamespace(), registryID.getPath(), "inventory"); - ItemModelProvider provider = (ItemModelProvider) item; - BlockModel model = provider.getItemModel(registryID); - topLevel.put(itemID, model); - cache.put(itemID, model); + addItemModel(registryID, (ItemModelProvider) item); } }); - cache.values().forEach(model -> { - loadingStack.addAll(model.getDependencies()); - }); + unbakedCache.putAll(UNBAKED_CACHE); + loadingStack.addAll(LOADING_STACK); + UNBAKED_CACHE.clear(); + LOADING_STACK.clear(); - topLevelModels.putAll(topLevel); - unbakedCache.putAll(cache); + modelsLoaded = true; + } + + @Deprecated // Not working with Fabric model API + private static void addBlockModel(ResourceLocation blockID, Block block) { + BlockModelProvider provider = (BlockModelProvider) block; + ImmutableList states = block.getStateDefinition().getPossibleStates(); + BlockState defaultState = block.defaultBlockState(); + + ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState); + UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, UNBAKED_CACHE); + + if (defaultModel instanceof MultiPart) { + states.forEach(blockState -> { + ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); + cacheAndQueueDependencies(stateID, defaultModel); + }); + } + else { + states.forEach(blockState -> { + ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState); + UnbakedModel model = stateID.equals(defaultStateID) ? defaultModel : provider.getModelVariant(stateID, blockState, UNBAKED_CACHE); + cacheAndQueueDependencies(stateID, model); + }); + } + } + + @Deprecated // Not working with Fabric model API + private static void addItemModel(ResourceLocation itemID, ItemModelProvider provider) { + ModelResourceLocation modelLocation = new ModelResourceLocation(itemID.getNamespace(), itemID.getPath(), "inventory"); + if (UNBAKED_CACHE.containsKey(modelLocation)) { + return; + } + BlockModel model = provider.getItemModel(modelLocation); + cacheAndQueueDependencies(modelLocation, model); + UNBAKED_CACHE.put(modelLocation, model); + } + + @Deprecated // Not working with Fabric model API + private static void cacheAndQueueDependencies(ResourceLocation resourceLocation, UnbakedModel unbakedModel) { + UNBAKED_CACHE.put(resourceLocation, unbakedModel); + LOADING_STACK.addAll(unbakedModel.getDependencies()); } public static void loadEmissiveModels(Map unbakedCache) { diff --git a/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java b/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java index f0fdd8df..8591e7f9 100644 --- a/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java +++ b/src/main/java/ru/bclib/mixin/client/ModelBakeryMixin.java @@ -2,11 +2,20 @@ package ru.bclib.mixin.client; import net.minecraft.client.Minecraft; import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.multipart.MultiPart; import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -15,55 +24,111 @@ import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import ru.bclib.BCLib; import ru.bclib.api.ModIntegrationAPI; import ru.bclib.client.render.CustomModelBakery; +import ru.bclib.interfaces.BlockModelProvider; +import ru.bclib.interfaces.ItemModelProvider; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; @Mixin(ModelBakery.class) public abstract class ModelBakeryMixin { + @Final + @Shadow + private ResourceManager resourceManager; @Final @Shadow private Map unbakedCache; - @Final - @Shadow - private Map topLevelModels; - @Final - @Shadow - private Set loadingStack; - @Inject( - method = "*", - require = 0, - expect = 0, - at = @At( - value = "INVOKE_STRING", - target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/lang/String;)V", - args = "ldc=missing_model", - shift = Shift.BEFORE - ) - ) - private void bclib_initCustomModels(ResourceManager resourceManager, BlockColors blockColors, ProfilerFiller profiler, int mipmap, CallbackInfo info) { - CustomModelBakery.loadCustomModels(resourceManager, unbakedCache, topLevelModels, loadingStack); - CustomModelBakery.setModelsLoaded(true); - } + @Shadow + protected abstract void cacheAndQueueDependencies(ResourceLocation resourceLocation, UnbakedModel unbakedModel); - // If Injection above failed - with Optifine, for example - @Inject(method = "loadModel(Lnet/minecraft/resources/ResourceLocation;)V", at = @At("HEAD")) - private void bclib_loadModelsIfNecessary(ResourceLocation resourceLocation, CallbackInfo model) { - if (!CustomModelBakery.areModelsLoaded()) { - ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); - CustomModelBakery.loadCustomModels(resourceManager, unbakedCache, topLevelModels, loadingStack); - CustomModelBakery.setModelsLoaded(true); + @Inject(method = "loadModel", at = @At("HEAD"), cancellable = true) + private void bclib_loadCustomModels(ResourceLocation resourceLocation, CallbackInfo info) { + if (resourceLocation instanceof ModelResourceLocation) { + String modId = resourceLocation.getNamespace(); + String path = resourceLocation.getPath(); + ResourceLocation clearLoc = new ResourceLocation(modId, path); + ModelResourceLocation modelId = (ModelResourceLocation) resourceLocation; + if ("inventory".equals(modelId.getVariant())) { + ResourceLocation itemLoc = new ResourceLocation(modId, "item/" + path); + ResourceLocation itemModelLoc = new ResourceLocation(modId, "models/" + itemLoc.getPath() + ".json"); + if (!resourceManager.hasResource(itemModelLoc)) { + Item item = Registry.ITEM.get(clearLoc); + ItemModelProvider modelProvider = null; + if (item instanceof ItemModelProvider) { + modelProvider = (ItemModelProvider) item; + } + else if (item instanceof BlockItem) { + Block block = Registry.BLOCK.get(clearLoc); + if (block instanceof ItemModelProvider) { + modelProvider = (ItemModelProvider) block; + } + } + if (modelProvider != null) { + BlockModel model = modelProvider.getItemModel(clearLoc); + if (model != null) { + model.name = itemLoc.toString(); + cacheAndQueueDependencies(modelId, model); + unbakedCache.put(itemLoc, model); + } + else { + BCLib.LOGGER.warning("Error loading model: {}", itemLoc); + } + info.cancel(); + } + } + } + else { + ResourceLocation stateLoc = new ResourceLocation(modId, "blockstates/" + path + ".json"); + if (!resourceManager.hasResource(stateLoc)) { + Block block = Registry.BLOCK.get(clearLoc); + if (block instanceof BlockModelProvider) { + List possibleStates = block.getStateDefinition().getPossibleStates(); + Optional possibleState = possibleStates + .stream() + .filter(state -> modelId.equals(BlockModelShaper.stateToModelLocation(clearLoc, state))) + .findFirst(); + if (possibleState.isPresent()) { + UnbakedModel modelVariant = ((BlockModelProvider) block).getModelVariant( + modelId, + possibleState.get(), + unbakedCache + ); + if (modelVariant != null) { + if (modelVariant instanceof MultiPart) { + possibleStates.forEach(state -> { + ResourceLocation stateId = BlockModelShaper.stateToModelLocation( + clearLoc, + state + ); + cacheAndQueueDependencies(stateId, modelVariant); + }); + } + else { + cacheAndQueueDependencies(modelId, modelVariant); + } + } + else { + BCLib.LOGGER.warning("Error loading variant: {}", modelId); + } + info.cancel(); + } + } + } + } } } @Inject(method = "*", at = @At("TAIL")) private void bclib_findEmissiveModels(ResourceManager resourceManager, BlockColors blockColors, ProfilerFiller profiler, int mipmap, CallbackInfo info) { + //CustomModelBakery.setModelsLoaded(false); if (ModIntegrationAPI.hasCanvas()) { CustomModelBakery.loadEmissiveModels(unbakedCache); } - CustomModelBakery.setModelsLoaded(false); } }