Dynamic model loading on Fabric
This commit is contained in:
parent
145896cc99
commit
b822f5ce87
|
|
@ -0,0 +1,75 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
|
||||
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||
import org.embeddedt.modernfix.util.DynamicOverridableMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(BlockModelShaper.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockModelShaperMixin {
|
||||
@Shadow @Final private ModelManager modelManager;
|
||||
|
||||
@Shadow
|
||||
private Map<BlockState, BakedModel> modelByStateCache;
|
||||
|
||||
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
|
||||
private void replaceModelMap(CallbackInfo ci) {
|
||||
// replace the backing map for mods which will access it
|
||||
this.modelByStateCache = new DynamicOverridableMap<>(BlockState.class, state -> modelManager.getModel(ModelLocationCache.get(state)));
|
||||
// Clear the cached models on blockstate objects
|
||||
for(Block block : BuiltInRegistries.BLOCK) {
|
||||
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
||||
modelHolder.mfix$setModel(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BakedModel cacheBlockModel(BlockState state) {
|
||||
// Do all model system accesses in the unlocked path
|
||||
ModelResourceLocation mrl = ModelLocationCache.get(state);
|
||||
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
|
||||
if (model == null) {
|
||||
model = modelManager.getMissingModel();
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason get the model from the dynamic model provider
|
||||
*/
|
||||
@Overwrite
|
||||
public BakedModel getBlockModel(BlockState state) {
|
||||
if(state instanceof IModelHoldingBlockState modelHolder) {
|
||||
BakedModel model = modelHolder.mfix$getModel();
|
||||
|
||||
if(model != null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
model = this.cacheBlockModel(state);
|
||||
modelHolder.mfix$setModel(model);
|
||||
return model;
|
||||
} else {
|
||||
return this.cacheBlockModel(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockStateBaseMixin implements IModelHoldingBlockState {
|
||||
private volatile SoftReference<BakedModel> mfix$model;
|
||||
|
||||
@Override
|
||||
public BakedModel mfix$getModel() {
|
||||
var ref = mfix$model;
|
||||
return ref != null ? ref.get() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$setModel(BakedModel model) {
|
||||
mfix$model = model != null ? new SoftReference<>(model) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.geom.EntityModelSet;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.item.ClientItem;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamicresources.DynamicModelProvider;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Mixin(ModelManager.class)
|
||||
@ClientOnlyMixin
|
||||
public class ModelManagerMixin {
|
||||
@Shadow private BakedModel missingModel;
|
||||
@Shadow private ItemModel missingItemModel;
|
||||
@Shadow private EntityModelSet entityModelSet;
|
||||
@Unique
|
||||
private DynamicModelProvider mfix$modelProvider;
|
||||
|
||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
}
|
||||
|
||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadBlockStates(Lnet/minecraft/client/resources/model/UnbakedModel;Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture<BlockStateModelLoader.LoadedModels> deferBlockStateLoad(UnbakedModel unbakedModel, ResourceManager resourceManager, Executor executor) {
|
||||
return CompletableFuture.completedFuture(new BlockStateModelLoader.LoadedModels(Map.of()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason disable map creation
|
||||
*/
|
||||
@Overwrite
|
||||
private static Map<BlockState, BakedModel> createBlockStateToModelDispatch(Map<ModelResourceLocation, BakedModel> map, BakedModel bakedModel) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Inject(method = "apply", at = @At("RETURN"))
|
||||
private void createModelProvider(ModelManager.ReloadState reloadState, ProfilerFiller profiler, CallbackInfo ci) {
|
||||
this.mfix$modelProvider = new DynamicModelProvider(
|
||||
null, // TODO
|
||||
this.missingModel,
|
||||
this.missingItemModel,
|
||||
Minecraft.getInstance().getResourceManager(),
|
||||
this.entityModelSet,
|
||||
reloadState.atlasPreparations()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use dynamic model system
|
||||
*/
|
||||
@Overwrite
|
||||
public BakedModel getModel(ModelResourceLocation modelLocation) {
|
||||
if(this.mfix$modelProvider != null) {
|
||||
return this.mfix$modelProvider.getModel(modelLocation);
|
||||
} else {
|
||||
return this.missingModel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use dynamic model system
|
||||
*/
|
||||
@Overwrite
|
||||
public ItemModel getItemModel(ResourceLocation resourceLocation) {
|
||||
return this.mfix$modelProvider.getItemModel(resourceLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use dynamic model system
|
||||
*/
|
||||
@Overwrite
|
||||
public ClientItem.Properties getItemProperties(ResourceLocation resourceLocation) {
|
||||
return this.mfix$modelProvider.getClientItemProperties(resourceLocation);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,15 +4,30 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.client.model.geom.EntityModelSet;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
|
||||
import net.minecraft.client.renderer.block.model.ItemModelGenerator;
|
||||
import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel;
|
||||
import net.minecraft.client.renderer.item.ClientItem;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.AtlasSet;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.MissingBlockModel;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.ModelDebugName;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.client.resources.model.SpriteGetter;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
|
|
@ -38,7 +53,6 @@ import java.util.stream.Collectors;
|
|||
* Handles loading models dynamically, rather than at startup time.
|
||||
*/
|
||||
public class DynamicModelProvider {
|
||||
/*
|
||||
private final LoadingCache<ResourceLocation, Optional<BlockStateModelLoader.LoadedModels>> loadedStateDefinitions =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
|
|
@ -65,19 +79,6 @@ public class DynamicModelProvider {
|
|||
}
|
||||
});
|
||||
|
||||
private final LoadingCache<ModelResourceLocation, Optional<UnbakedModel>> loadedUnbakedModels =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.softValues()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Optional<UnbakedModel> load(ModelResourceLocation key) {
|
||||
return loadModel(key);
|
||||
}
|
||||
});
|
||||
|
||||
private final LoadingCache<ModelResourceLocation, Optional<BakedModel>> loadedBakedModels =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
|
|
@ -91,35 +92,72 @@ public class DynamicModelProvider {
|
|||
}
|
||||
});
|
||||
|
||||
private final Map<ModelResourceLocation, BakedModel> initialBakedRegistry;
|
||||
private final LoadingCache<ResourceLocation, Optional<ClientItem>> loadedClientItemProperties =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.softValues()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Optional<ClientItem> load(ResourceLocation key) {
|
||||
return loadClientItemProperties(key);
|
||||
}
|
||||
});
|
||||
|
||||
private final LoadingCache<ResourceLocation, Optional<ItemModel>> loadedItemModels =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(8)
|
||||
.softValues()
|
||||
.build(new CacheLoader<>() {
|
||||
@Override
|
||||
public Optional<ItemModel> load(ResourceLocation key) {
|
||||
return loadItemModel(key);
|
||||
}
|
||||
});
|
||||
|
||||
private final BakedModel missingModel;
|
||||
private final ItemModel missingItemModel;
|
||||
private final UnbakedModel unbakedMissingModel;
|
||||
private final Function<ResourceLocation, StateDefinition<Block, BlockState>> stateMapper;
|
||||
private final ResourceManager resourceManager;
|
||||
private final BlockStateModelLoader blockStateModelLoader;
|
||||
private final ModelBakery.TextureGetter textureGetter;
|
||||
private final DynamicMap<ResourceLocation, UnbakedModel> fakeUnbakedModelMap;
|
||||
private final DynamicResolver resolver;
|
||||
private final EntityModelSet entityModelSet;
|
||||
private final ItemModelGenerator itemModelGenerator;
|
||||
|
||||
public DynamicModelProvider(Map<ModelResourceLocation, BakedModel> initialBakedRegistry, BakedModel missingModel, ResourceManager resourceManager, Map<ResourceLocation, AtlasSet.StitchResult> atlasMap) {
|
||||
this.initialBakedRegistry = initialBakedRegistry;
|
||||
public DynamicModelProvider(Map<ModelResourceLocation, BakedModel> initialBakedRegistry, BakedModel missingModel,
|
||||
ItemModel missingItemModel, ResourceManager resourceManager, EntityModelSet entityModelSet,
|
||||
Map<ResourceLocation, AtlasSet.StitchResult> atlasMap) {
|
||||
this.missingModel = missingModel;
|
||||
this.textureGetter = (mrl, material) -> {
|
||||
var atlas = atlasMap.get(material.atlasLocation());
|
||||
var sprite = atlas.getSprite(material.texture());
|
||||
if (sprite != null) {
|
||||
return sprite;
|
||||
} else {
|
||||
ModernFix.LOGGER.warn("Unable to find sprite '{}' referenced by model '{}'", material.texture(), mrl);
|
||||
return atlas.missing();
|
||||
this.missingItemModel = missingItemModel;
|
||||
this.entityModelSet = entityModelSet;
|
||||
var missing = atlasMap.get(TextureAtlas.LOCATION_BLOCKS).missing();
|
||||
this.textureGetter = new ModelBakery.TextureGetter() {
|
||||
@Override
|
||||
public TextureAtlasSprite get(ModelDebugName modelDebugName, Material material) {
|
||||
var atlas = atlasMap.get(material.atlasLocation());
|
||||
var sprite = atlas.getSprite(material.texture());
|
||||
if (sprite != null) {
|
||||
return sprite;
|
||||
} else {
|
||||
ModernFix.LOGGER.warn("Unable to find sprite '{}' referenced by model '{}'", material.texture(), modelDebugName.get());
|
||||
return missing;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite reportMissingReference(ModelDebugName modelDebugName, String string) {
|
||||
return missing;
|
||||
}
|
||||
};
|
||||
this.stateMapper = BlockStateModelLoader.definitionLocationToBlockMapper();
|
||||
this.resourceManager = resourceManager;
|
||||
this.unbakedMissingModel = MissingBlockModel.missingModel();
|
||||
this.blockStateModelLoader = new BlockStateModelLoader(this.unbakedMissingModel);
|
||||
this.fakeUnbakedModelMap = new DynamicMap<>(ResourceLocation.class, key -> this.loadedBlockModels.getUnchecked(key).orElse(null));
|
||||
this.resolver = new DynamicResolver();
|
||||
this.itemModelGenerator = new ItemModelGenerator();
|
||||
}
|
||||
|
||||
private Optional<BlockStateModelLoader.LoadedModels> loadBlockStateDefinition(ResourceLocation location) {
|
||||
|
|
@ -138,35 +176,56 @@ public class DynamicModelProvider {
|
|||
ModernFix.LOGGER.error("Failed to load blockstate definition {} from pack '{}'", location, resource.sourcePackId(), e);
|
||||
}
|
||||
}
|
||||
return Optional.of(this.blockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions));
|
||||
return Optional.of(BlockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions, this.unbakedMissingModel));
|
||||
}
|
||||
|
||||
private BakedModel bakeModel(UnbakedModel model, ModelResourceLocation location) {
|
||||
synchronized (this) {
|
||||
this.resolver.clearResolver();
|
||||
model.resolveDependencies(this.resolver);
|
||||
var modelBakery = new ModelBakery(Map.of(location, model), this.fakeUnbakedModelMap, this.unbakedMissingModel);
|
||||
modelBakery.bakeModels(this.textureGetter);
|
||||
return modelBakery.getBakedTopLevelModels().get(location);
|
||||
var modelBaker = new DynamicBaker(location::toString);
|
||||
return UnbakedModel.bakeWithTopModelValues(model, modelBaker, BlockModelRotation.X0_Y0);
|
||||
}
|
||||
}
|
||||
|
||||
private BakedModel bakeModel(UnbakedBlockStateModel model, ModelResourceLocation location) {
|
||||
synchronized (this) {
|
||||
this.resolver.clearResolver();
|
||||
model.resolveDependencies(this.resolver);
|
||||
var modelBaker = new DynamicBaker(location::toString);
|
||||
return model.bake(modelBaker);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<BakedModel> loadBakedModel(ModelResourceLocation location) {
|
||||
var unbakedModel = this.loadedUnbakedModels.getUnchecked(location);
|
||||
return unbakedModel.map(model -> this.bakeModel(model, location));
|
||||
if (location.variant().equals("standalone") || location.variant().equals("fabric_resource")) {
|
||||
return this.loadedBlockModels.getUnchecked(location.id()).map(unbakedModel -> {
|
||||
return this.bakeModel(unbakedModel, location);
|
||||
});
|
||||
} else {
|
||||
var optLoadedModels = this.loadedStateDefinitions.getUnchecked(location.id());
|
||||
Optional<UnbakedBlockStateModel> unbakedModelOpt = optLoadedModels.map(loadedModels -> {
|
||||
var loadedModel = loadedModels.models().get(location);
|
||||
if(loadedModel != null) {
|
||||
return loadedModel.model();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return unbakedModelOpt.map(unbakedModel -> {
|
||||
return this.bakeModel(unbakedModel, location);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<UnbakedModel> loadBlockModel(ResourceLocation location) {
|
||||
if(location.equals(SpecialModels.BUILTIN_GENERATED)) {
|
||||
return Optional.of(SpecialModels.GENERATED_MARKER);
|
||||
} else if(location.equals(SpecialModels.BUILTIN_BLOCK_ENTITY)) {
|
||||
return Optional.of(SpecialModels.BLOCK_ENTITY_MARKER);
|
||||
if (location.equals(ItemModelGenerator.GENERATED_ITEM_MODEL_ID)) {
|
||||
return Optional.of(this.itemModelGenerator);
|
||||
}
|
||||
var resource = this.resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "models/" + location.getPath() + ".json"));
|
||||
if(resource.isPresent()) {
|
||||
try(Reader reader = resource.get().openAsReader()) {
|
||||
BlockModel blockModel = BlockModel.fromStream(reader);
|
||||
blockModel.name = location.toString();
|
||||
return Optional.of(blockModel);
|
||||
} catch(Exception e) {
|
||||
ModernFix.LOGGER.error("Failed to load block model {} from '{}'", location, resource.get().sourcePackId(), e);
|
||||
|
|
@ -178,34 +237,71 @@ public class DynamicModelProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private Optional<UnbakedModel> loadModel(ModelResourceLocation location) {
|
||||
if (location.variant().equals(ModelResourceLocation.INVENTORY_VARIANT)) {
|
||||
return this.loadedBlockModels.getUnchecked(ResourceLocation.fromNamespaceAndPath(location.id().getNamespace(), "item/" + location.id().getPath()));
|
||||
} else if (location.variant().equals("standalone") || location.variant().equals("fabric_resource")) {
|
||||
return this.loadedBlockModels.getUnchecked(location.id());
|
||||
private Optional<ClientItem> loadClientItemProperties(ResourceLocation location) {
|
||||
var resource = this.resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "items/" + location.getPath() + ".json"));
|
||||
if(resource.isPresent()) {
|
||||
try(Reader reader = resource.get().openAsReader()) {
|
||||
ClientItem clientItem = ClientItem.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow();
|
||||
return Optional.of(clientItem);
|
||||
} catch(Exception e) {
|
||||
ModernFix.LOGGER.error("Failed to load block model {} from '{}'", location, resource.get().sourcePackId(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
var optLoadedModels = this.loadedStateDefinitions.getUnchecked(location.id());
|
||||
return optLoadedModels.map(loadedModels -> {
|
||||
var loadedModel = loadedModels.models().get(location);
|
||||
if(loadedModel != null) {
|
||||
return loadedModel.model();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
ModernFix.LOGGER.warn("Client item '{}' does not exist in any resource packs", location);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<ItemModel> loadItemModel(ResourceLocation location) {
|
||||
return this.loadedClientItemProperties.getUnchecked(location).map(clientItem -> {
|
||||
var bakingContext = new ItemModel.BakingContext(new DynamicBaker(location::toString), this.entityModelSet, this.missingItemModel);
|
||||
return clientItem.model().bake(bakingContext);
|
||||
});
|
||||
}
|
||||
|
||||
public BakedModel getModel(ModelResourceLocation location) {
|
||||
return this.loadedBakedModels.getUnchecked(location).orElse(this.missingModel);
|
||||
}
|
||||
|
||||
*/
|
||||
public ClientItem.Properties getClientItemProperties(ResourceLocation location) {
|
||||
return this.loadedClientItemProperties.getUnchecked(location).map(ClientItem::properties).orElse(ClientItem.Properties.DEFAULT);
|
||||
}
|
||||
|
||||
public ItemModel getItemModel(ResourceLocation location) {
|
||||
return this.loadedItemModels.getUnchecked(location).orElse(this.missingItemModel);
|
||||
}
|
||||
|
||||
private class DynamicBaker implements ModelBaker {
|
||||
private final ModelDebugName modelDebugName;
|
||||
|
||||
private DynamicBaker(ModelDebugName modelDebugName) {
|
||||
this.modelDebugName = modelDebugName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel bake(ResourceLocation location, ModelState transform) {
|
||||
return DynamicModelProvider.this.loadBlockModel(location).map(unbakedModel -> {
|
||||
DynamicModelProvider.this.resolver.clearResolver();
|
||||
unbakedModel.resolveDependencies(DynamicModelProvider.this.resolver);
|
||||
return UnbakedModel.bakeWithTopModelValues(unbakedModel, this, transform);
|
||||
}).orElse(DynamicModelProvider.this.missingModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpriteGetter sprites() {
|
||||
return DynamicModelProvider.this.textureGetter.bind(this.modelDebugName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelDebugName rootName() {
|
||||
return this.modelDebugName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the Mojang impl but with some changes to make it slightly more efficient.
|
||||
*/
|
||||
/*
|
||||
private class DynamicResolver implements UnbakedModel.Resolver {
|
||||
private final Set<ResourceLocation> stack = new ObjectOpenHashSet<>(4);
|
||||
private final Set<ResourceLocation> resolvedModels = new ObjectOpenHashSet<>();
|
||||
|
|
@ -236,6 +332,4 @@ public class DynamicModelProvider {
|
|||
this.resolvedModels.clear();
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
|||
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
||||
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
||||
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/ModelBakery$TextureGetter;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
||||
accessible class net/minecraft/client/resources/model/ModelManager$ReloadState
|
||||
accessible method net/minecraft/client/resources/model/BlockStateModelLoader definitionLocationToBlockMapper ()Ljava/util/function/Function;
|
||||
accessible method net/minecraft/client/resources/model/BlockStateModelLoader loadBlockStateDefinitionStack (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/world/level/block/state/StateDefinition;Ljava/util/List;Lnet/minecraft/client/resources/model/UnbakedModel;)Lnet/minecraft/client/resources/model/BlockStateModelLoader$LoadedModels;
|
||||
accessible class net/minecraft/client/resources/model/BlockStateModelLoader$LoadedBlockModelDefinition
|
||||
accessible method net/minecraft/client/resources/model/BlockStateModelLoader$LoadedBlockModelDefinition <init> (Ljava/lang/String;Lnet/minecraft/client/renderer/block/model/BlockModelDefinition;)V
|
||||
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
||||
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user