Temporal model loading fix (#31)
This commit is contained in:
parent
49e833e147
commit
83d5a4d2bf
2 changed files with 152 additions and 75 deletions
|
@ -26,78 +26,90 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
public class CustomModelBakery {
|
||||
private static final Map<ResourceLocation, UnbakedModel> UNBAKED_CACHE = Maps.newConcurrentMap();
|
||||
private static final Set<ResourceLocation> 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<ResourceLocation, UnbakedModel> unbakedCache, Map<ResourceLocation, UnbakedModel> topLevelModels, Set<ResourceLocation> loadingStack) {
|
||||
Map<ResourceLocation, UnbakedModel> cache = Maps.newConcurrentMap();
|
||||
Map<ResourceLocation, UnbakedModel> 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<ResourceLocation, UnbakedModel> unbakedCache, Set<ResourceLocation> 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)) {
|
||||
addBlockModel(blockID, block);
|
||||
}
|
||||
|
||||
if (Registry.ITEM.get(blockID) != Items.AIR) {
|
||||
addItemModel(blockID, (ItemModelProvider) block);
|
||||
}
|
||||
});
|
||||
|
||||
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)) {
|
||||
addItemModel(registryID, (ItemModelProvider) item);
|
||||
}
|
||||
});
|
||||
|
||||
unbakedCache.putAll(UNBAKED_CACHE);
|
||||
loadingStack.addAll(LOADING_STACK);
|
||||
UNBAKED_CACHE.clear();
|
||||
LOADING_STACK.clear();
|
||||
|
||||
modelsLoaded = true;
|
||||
}
|
||||
|
||||
@Deprecated // Not working with Fabric model API
|
||||
private static void addBlockModel(ResourceLocation blockID, Block block) {
|
||||
BlockModelProvider provider = (BlockModelProvider) block;
|
||||
ImmutableList<BlockState> states = block.getStateDefinition().getPossibleStates();
|
||||
BlockState defaultState = block.defaultBlockState();
|
||||
|
||||
ResourceLocation defaultStateID = BlockModelShaper.stateToModelLocation(blockID, defaultState);
|
||||
UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, cache);
|
||||
UnbakedModel defaultModel = provider.getModelVariant(defaultStateID, defaultState, UNBAKED_CACHE);
|
||||
|
||||
if (defaultModel instanceof MultiPart) {
|
||||
states.forEach(blockState -> {
|
||||
ResourceLocation stateID = BlockModelShaper.stateToModelLocation(blockID, blockState);
|
||||
topLevel.put(stateID, defaultModel);
|
||||
cache.put(stateID, defaultModel);
|
||||
cacheAndQueueDependencies(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);
|
||||
UnbakedModel model = stateID.equals(defaultStateID) ? defaultModel : provider.getModelVariant(stateID, blockState, UNBAKED_CACHE);
|
||||
cacheAndQueueDependencies(stateID, model);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@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);
|
||||
}
|
||||
});
|
||||
|
||||
Registry.ITEM.stream().filter(item -> item instanceof ItemModelProvider).parallel().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);
|
||||
}
|
||||
});
|
||||
|
||||
cache.values().forEach(model -> {
|
||||
loadingStack.addAll(model.getDependencies());
|
||||
});
|
||||
|
||||
topLevelModels.putAll(topLevel);
|
||||
unbakedCache.putAll(cache);
|
||||
@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<ResourceLocation, UnbakedModel> unbakedCache) {
|
||||
|
|
|
@ -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<ResourceLocation, UnbakedModel> unbakedCache;
|
||||
@Final
|
||||
@Shadow
|
||||
private Map<ResourceLocation, UnbakedModel> topLevelModels;
|
||||
@Final
|
||||
@Shadow
|
||||
private Set<ResourceLocation> loadingStack;
|
||||
|
||||
@Inject(
|
||||
method = "<init>*",
|
||||
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);
|
||||
|
||||
@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<BlockState> possibleStates = block.getStateDefinition().getPossibleStates();
|
||||
Optional<BlockState> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = "<init>*", 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue