Rewrite dynamic resources once again
This commit is contained in:
parent
d8b86708e5
commit
5450a16aad
|
|
@ -1,22 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelManager;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(Minecraft.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class MinecraftMixin_ModelTicking {
|
|
||||||
@Shadow public abstract ModelManager getModelManager();
|
|
||||||
|
|
||||||
@Inject(method = "tick", at = @At(value = "RETURN"))
|
|
||||||
private void tickModels(CallbackInfo ci) {
|
|
||||||
((IExtendedModelManager)this.getModelManager()).mfix$tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +1,32 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
|
import net.minecraft.client.resources.model.AtlasSet;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelManager;
|
import org.embeddedt.modernfix.dynamicresources.DynamicModelProvider;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
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.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -26,9 +35,16 @@ import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@Mixin(ModelManager.class)
|
@Mixin(ModelManager.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class ModelManagerMixin implements IExtendedModelManager {
|
public class ModelManagerMixin {
|
||||||
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
||||||
|
|
||||||
|
@Shadow private BakedModel missingModel;
|
||||||
|
@Unique
|
||||||
|
private DynamicModelProvider mfix$modelProvider;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Map<ResourceLocation, AtlasSet.StitchResult> mfix$stitchResults;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
||||||
if(this.bakedRegistry == null) {
|
if(this.bakedRegistry == null) {
|
||||||
|
|
@ -51,8 +67,31 @@ public class ModelManagerMixin implements IExtendedModelManager {
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Inject(method = "loadModels", at = @At("HEAD"))
|
||||||
public void mfix$tick() {
|
private void saveStitchResults(ProfilerFiller profilerFiller, Map<ResourceLocation, AtlasSet.StitchResult> map, ModelBakery modelBakery, Object2IntMap<BlockState> object2IntMap, CallbackInfoReturnable<?> cir) {
|
||||||
|
this.mfix$stitchResults = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "apply", at = @At("RETURN"))
|
||||||
|
private void createModelProvider(CallbackInfo ci) {
|
||||||
|
this.mfix$modelProvider = new DynamicModelProvider(
|
||||||
|
null, // TODO
|
||||||
|
this.missingModel,
|
||||||
|
Minecraft.getInstance().getResourceManager(),
|
||||||
|
this.mfix$stitchResults
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
package org.embeddedt.modernfix.dynamicresources;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.Function;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Mojang Triple-based baked cache system is too slow to be hitting on every model retrieval, so
|
|
||||||
* we need a fast, concurrency-safe wrapper on top.
|
|
||||||
*/
|
|
||||||
public class DynamicModelCache<K> {
|
|
||||||
private final Reference2ReferenceLinkedOpenHashMap<K, BakedModel> cache = new Reference2ReferenceLinkedOpenHashMap<>();
|
|
||||||
private final StampedLock lock = new StampedLock();
|
|
||||||
private final Function<K, BakedModel> modelRetriever;
|
|
||||||
private final boolean allowNulls;
|
|
||||||
|
|
||||||
public DynamicModelCache(Function<K, BakedModel> modelRetriever, boolean allowNulls) {
|
|
||||||
this.modelRetriever = modelRetriever;
|
|
||||||
this.allowNulls = allowNulls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
long stamp = lock.writeLock();
|
|
||||||
try {
|
|
||||||
cache.clear();
|
|
||||||
} finally {
|
|
||||||
lock.unlock(stamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean needToPopulate(K state) {
|
|
||||||
long stamp = lock.readLock();
|
|
||||||
try {
|
|
||||||
return !cache.containsKey(state);
|
|
||||||
} finally {
|
|
||||||
lock.unlock(stamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BakedModel getModelFromCache(K state) {
|
|
||||||
long stamp = lock.readLock();
|
|
||||||
try {
|
|
||||||
return cache.get(state);
|
|
||||||
} finally {
|
|
||||||
lock.unlock(stamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BakedModel cacheModel(K state) {
|
|
||||||
BakedModel model = modelRetriever.apply(state);
|
|
||||||
|
|
||||||
// Lock and modify our local, faster cache
|
|
||||||
long stamp = lock.writeLock();
|
|
||||||
|
|
||||||
try {
|
|
||||||
cache.putAndMoveToFirst(state, model);
|
|
||||||
// TODO: choose less arbitrary number
|
|
||||||
if(cache.size() >= 1000) {
|
|
||||||
cache.removeLast();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock(stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BakedModel get(K key) {
|
|
||||||
BakedModel model = getModelFromCache(key);
|
|
||||||
|
|
||||||
if(model == null && (!allowNulls || needToPopulate(key))) {
|
|
||||||
model = cacheModel(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +1,236 @@
|
||||||
package org.embeddedt.modernfix.dynamicresources;
|
package org.embeddedt.modernfix.dynamicresources;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
|
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
|
||||||
|
import net.minecraft.client.resources.model.AtlasSet;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||||
|
import net.minecraft.client.resources.model.MissingBlockModel;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.client.resources.model.SpecialModels;
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
import net.minecraft.util.GsonHelper;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.util.DynamicMap;
|
||||||
|
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles loading models dynamically, rather than at startup time.
|
* Handles loading models dynamically, rather than at startup time.
|
||||||
*/
|
*/
|
||||||
public class DynamicModelProvider {
|
public class DynamicModelProvider {
|
||||||
private final Map<ResourceLocation, UnbakedModel> internalModels;
|
private final LoadingCache<ResourceLocation, Optional<BlockStateModelLoader.LoadedModels>> loadedStateDefinitions =
|
||||||
private final Cache<ResourceLocation, Optional<UnbakedModel>> loadedModels =
|
|
||||||
CacheBuilder.newBuilder()
|
CacheBuilder.newBuilder()
|
||||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||||
.maximumSize(1000)
|
.maximumSize(1000)
|
||||||
.concurrencyLevel(8)
|
.concurrencyLevel(8)
|
||||||
.softValues()
|
.softValues()
|
||||||
.build();
|
.build(new CacheLoader<>() {
|
||||||
|
@Override
|
||||||
|
public Optional<BlockStateModelLoader.LoadedModels> load(ResourceLocation key) {
|
||||||
|
return loadBlockStateDefinition(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
public DynamicModelProvider(Map<ResourceLocation, UnbakedModel> initialModels) {
|
private final LoadingCache<ResourceLocation, Optional<UnbakedModel>> loadedBlockModels =
|
||||||
this.internalModels = initialModels;
|
CacheBuilder.newBuilder()
|
||||||
|
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||||
|
.maximumSize(1000)
|
||||||
|
.concurrencyLevel(8)
|
||||||
|
.softValues()
|
||||||
|
.build(new CacheLoader<>() {
|
||||||
|
@Override
|
||||||
|
public Optional<UnbakedModel> load(ResourceLocation key) {
|
||||||
|
return loadBlockModel(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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)
|
||||||
|
.maximumSize(1000)
|
||||||
|
.concurrencyLevel(8)
|
||||||
|
.softValues()
|
||||||
|
.build(new CacheLoader<>() {
|
||||||
|
@Override
|
||||||
|
public Optional<BakedModel> load(ModelResourceLocation key) {
|
||||||
|
return loadBakedModel(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private final Map<ModelResourceLocation, BakedModel> initialBakedRegistry;
|
||||||
|
private final BakedModel missingModel;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public DynamicModelProvider(Map<ModelResourceLocation, BakedModel> initialBakedRegistry, BakedModel missingModel, ResourceManager resourceManager, Map<ResourceLocation, AtlasSet.StitchResult> atlasMap) {
|
||||||
|
this.initialBakedRegistry = initialBakedRegistry;
|
||||||
|
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.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnbakedModel getModel(ResourceLocation location) {
|
private Optional<BlockStateModelLoader.LoadedModels> loadBlockStateDefinition(ResourceLocation location) {
|
||||||
try {
|
StateDefinition<Block, BlockState> stateDefinition = this.stateMapper.apply(location);
|
||||||
return loadedModels.get(location, () -> Optional.ofNullable(loadModel(location))).orElse(null);
|
if(stateDefinition == null) {
|
||||||
} catch(ExecutionException e) {
|
return Optional.empty();
|
||||||
throw new RuntimeException(e.getCause());
|
}
|
||||||
|
List<Resource> resources = resourceManager.getResourceStack(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "blockstates/" + location.getPath() + ".json"));
|
||||||
|
List<BlockStateModelLoader.LoadedBlockModelDefinition> loadedDefinitions = new ArrayList<>(resources.size());
|
||||||
|
for(Resource resource : resources) {
|
||||||
|
try(Reader reader = resource.openAsReader()) {
|
||||||
|
JsonObject jsonObject = GsonHelper.parse(reader);
|
||||||
|
BlockModelDefinition blockModelDefinition = BlockModelDefinition.fromJsonElement(jsonObject);
|
||||||
|
loadedDefinitions.add(new BlockStateModelLoader.LoadedBlockModelDefinition(resource.sourcePackId(), blockModelDefinition));
|
||||||
|
} catch(Exception e) {
|
||||||
|
ModernFix.LOGGER.error("Failed to load blockstate definition {} from pack '{}'", location, resource.sourcePackId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.of(this.blockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UnbakedModel loadModel(ResourceLocation location) {
|
private Optional<BakedModel> loadBakedModel(ModelResourceLocation location) {
|
||||||
return null; /* TODO :) */
|
var unbakedModel = this.loadedUnbakedModels.getUnchecked(location);
|
||||||
|
return unbakedModel.map(model -> this.bakeModel(model, 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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ModernFix.LOGGER.warn("Model '{}' does not exist in any resource packs", location);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BakedModel getModel(ModelResourceLocation location) {
|
||||||
|
return this.loadedBakedModels.getUnchecked(location).orElse(this.missingModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnbakedModel resolve(ResourceLocation resourceLocation) {
|
||||||
|
if (this.stack.contains(resourceLocation)) {
|
||||||
|
ModernFix.LOGGER.warn("Detected model loading loop: {}->{}", this.stacktraceToString(), resourceLocation);
|
||||||
|
return DynamicModelProvider.this.unbakedMissingModel;
|
||||||
|
} else {
|
||||||
|
UnbakedModel unbakedModel = DynamicModelProvider.this.loadedBlockModels.getUnchecked(resourceLocation).orElse(DynamicModelProvider.this.unbakedMissingModel);
|
||||||
|
if (this.resolvedModels.add(resourceLocation)) {
|
||||||
|
this.stack.add(resourceLocation);
|
||||||
|
unbakedModel.resolveDependencies(this);
|
||||||
|
this.stack.remove(resourceLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return unbakedModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stacktraceToString() {
|
||||||
|
return this.stack.stream().map(ResourceLocation::toString).collect(Collectors.joining("->"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearResolver() {
|
||||||
|
this.stack.clear();
|
||||||
|
this.resolvedModels.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,45 +11,6 @@ import net.minecraft.world.level.block.state.properties.Property;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ModelBakeryHelpers {
|
public class ModelBakeryHelpers {
|
||||||
/**
|
|
||||||
* The maximum number of baked models kept in memory at once.
|
|
||||||
*/
|
|
||||||
public static final int MAX_BAKED_MODEL_COUNT = 10000;
|
|
||||||
/**
|
|
||||||
* The maximum number of unbaked models kept in memory at once.
|
|
||||||
*/
|
|
||||||
public static final int MAX_UNBAKED_MODEL_COUNT = 10000;
|
|
||||||
/**
|
|
||||||
* The time in seconds after which a model becomes eligible for eviction if not used.
|
|
||||||
*/
|
|
||||||
public static final int MAX_MODEL_LIFETIME_SECS = 300;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These folders will have all textures stitched onto the atlas when dynamic resources is enabled.
|
|
||||||
*/
|
|
||||||
public static String[] getExtraTextureFolders() {
|
|
||||||
return new String[] {
|
|
||||||
"attachment",
|
|
||||||
"bettergrass",
|
|
||||||
"block",
|
|
||||||
"blocks",
|
|
||||||
"cape",
|
|
||||||
"entity/bed",
|
|
||||||
"entity/chest",
|
|
||||||
"item",
|
|
||||||
"items",
|
|
||||||
"model",
|
|
||||||
"models",
|
|
||||||
"part",
|
|
||||||
"pipe",
|
|
||||||
"ropebridge",
|
|
||||||
"runes",
|
|
||||||
"solid_block",
|
|
||||||
"spell_effect",
|
|
||||||
"spell_projectile"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
|
private static <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
|
||||||
return state.setValue(prop, (V)o);
|
return state.setValue(prop, (V)o);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package org.embeddedt.modernfix.dynamicresources;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.block.model.BlockFaceUV;
|
|
||||||
|
|
||||||
public class UVController {
|
|
||||||
public static final ThreadLocal<Boolean> useDummyUv = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
|
||||||
public static final BlockFaceUV dummyUv = new BlockFaceUV(new float[4], 0);
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user