Skip to content

Commit b822f5c

Browse files
committed
Dynamic model loading on Fabric
1 parent 145896c commit b822f5c

File tree

5 files changed

+354
-51
lines changed

5 files changed

+354
-51
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
2+
3+
import net.minecraft.client.renderer.block.BlockModelShaper;
4+
import net.minecraft.client.resources.model.BakedModel;
5+
import net.minecraft.client.resources.model.ModelManager;
6+
import net.minecraft.client.resources.model.ModelResourceLocation;
7+
import net.minecraft.core.registries.BuiltInRegistries;
8+
import net.minecraft.world.level.block.Block;
9+
import net.minecraft.world.level.block.state.BlockState;
10+
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
11+
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
12+
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
13+
import org.embeddedt.modernfix.util.DynamicOverridableMap;
14+
import org.spongepowered.asm.mixin.*;
15+
import org.spongepowered.asm.mixin.injection.At;
16+
import org.spongepowered.asm.mixin.injection.Inject;
17+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
18+
19+
import java.util.Map;
20+
21+
@Mixin(BlockModelShaper.class)
22+
@ClientOnlyMixin
23+
public class BlockModelShaperMixin {
24+
@Shadow @Final private ModelManager modelManager;
25+
26+
@Shadow
27+
private Map<BlockState, BakedModel> modelByStateCache;
28+
29+
@Inject(method = { "<init>", "replaceCache" }, at = @At("RETURN"))
30+
private void replaceModelMap(CallbackInfo ci) {
31+
// replace the backing map for mods which will access it
32+
this.modelByStateCache = new DynamicOverridableMap<>(BlockState.class, state -> modelManager.getModel(ModelLocationCache.get(state)));
33+
// Clear the cached models on blockstate objects
34+
for(Block block : BuiltInRegistries.BLOCK) {
35+
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
36+
if(state instanceof IModelHoldingBlockState modelHolder) {
37+
modelHolder.mfix$setModel(null);
38+
}
39+
}
40+
}
41+
}
42+
43+
private BakedModel cacheBlockModel(BlockState state) {
44+
// Do all model system accesses in the unlocked path
45+
ModelResourceLocation mrl = ModelLocationCache.get(state);
46+
BakedModel model = mrl == null ? null : modelManager.getModel(mrl);
47+
if (model == null) {
48+
model = modelManager.getMissingModel();
49+
}
50+
51+
return model;
52+
}
53+
54+
/**
55+
* @author embeddedt
56+
* @reason get the model from the dynamic model provider
57+
*/
58+
@Overwrite
59+
public BakedModel getBlockModel(BlockState state) {
60+
if(state instanceof IModelHoldingBlockState modelHolder) {
61+
BakedModel model = modelHolder.mfix$getModel();
62+
63+
if(model != null) {
64+
return model;
65+
}
66+
67+
model = this.cacheBlockModel(state);
68+
modelHolder.mfix$setModel(model);
69+
return model;
70+
} else {
71+
return this.cacheBlockModel(state);
72+
}
73+
}
74+
}
75+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
2+
3+
import net.minecraft.client.resources.model.BakedModel;
4+
import net.minecraft.world.level.block.state.BlockBehaviour;
5+
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
6+
import org.embeddedt.modernfix.duck.IModelHoldingBlockState;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
9+
import java.lang.ref.SoftReference;
10+
11+
@Mixin(BlockBehaviour.BlockStateBase.class)
12+
@ClientOnlyMixin
13+
public class BlockStateBaseMixin implements IModelHoldingBlockState {
14+
private volatile SoftReference<BakedModel> mfix$model;
15+
16+
@Override
17+
public BakedModel mfix$getModel() {
18+
var ref = mfix$model;
19+
return ref != null ? ref.get() : null;
20+
}
21+
22+
@Override
23+
public void mfix$setModel(BakedModel model) {
24+
mfix$model = model != null ? new SoftReference<>(model) : null;
25+
}
26+
}
27+
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
2+
3+
import net.minecraft.client.Minecraft;
4+
import net.minecraft.client.model.geom.EntityModelSet;
5+
import net.minecraft.client.renderer.block.model.BlockModel;
6+
import net.minecraft.client.renderer.item.ClientItem;
7+
import net.minecraft.client.renderer.item.ItemModel;
8+
import net.minecraft.client.resources.model.BakedModel;
9+
import net.minecraft.client.resources.model.BlockStateModelLoader;
10+
import net.minecraft.client.resources.model.ModelManager;
11+
import net.minecraft.client.resources.model.ModelResourceLocation;
12+
import net.minecraft.client.resources.model.UnbakedModel;
13+
import net.minecraft.resources.ResourceLocation;
14+
import net.minecraft.server.packs.resources.ResourceManager;
15+
import net.minecraft.util.profiling.ProfilerFiller;
16+
import net.minecraft.world.level.block.state.BlockState;
17+
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
18+
import org.embeddedt.modernfix.dynamicresources.DynamicModelProvider;
19+
import org.spongepowered.asm.mixin.Mixin;
20+
import org.spongepowered.asm.mixin.Overwrite;
21+
import org.spongepowered.asm.mixin.Shadow;
22+
import org.spongepowered.asm.mixin.Unique;
23+
import org.spongepowered.asm.mixin.injection.At;
24+
import org.spongepowered.asm.mixin.injection.Inject;
25+
import org.spongepowered.asm.mixin.injection.Redirect;
26+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
27+
28+
import java.util.Map;
29+
import java.util.concurrent.CompletableFuture;
30+
import java.util.concurrent.Executor;
31+
32+
@Mixin(ModelManager.class)
33+
@ClientOnlyMixin
34+
public class ModelManagerMixin {
35+
@Shadow private BakedModel missingModel;
36+
@Shadow private ItemModel missingItemModel;
37+
@Shadow private EntityModelSet entityModelSet;
38+
@Unique
39+
private DynamicModelProvider mfix$modelProvider;
40+
41+
@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;"))
42+
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {
43+
return CompletableFuture.completedFuture(Map.of());
44+
}
45+
46+
@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;"))
47+
private CompletableFuture<BlockStateModelLoader.LoadedModels> deferBlockStateLoad(UnbakedModel unbakedModel, ResourceManager resourceManager, Executor executor) {
48+
return CompletableFuture.completedFuture(new BlockStateModelLoader.LoadedModels(Map.of()));
49+
}
50+
51+
/**
52+
* @author embeddedt
53+
* @reason disable map creation
54+
*/
55+
@Overwrite
56+
private static Map<BlockState, BakedModel> createBlockStateToModelDispatch(Map<ModelResourceLocation, BakedModel> map, BakedModel bakedModel) {
57+
return Map.of();
58+
}
59+
60+
@Inject(method = "apply", at = @At("RETURN"))
61+
private void createModelProvider(ModelManager.ReloadState reloadState, ProfilerFiller profiler, CallbackInfo ci) {
62+
this.mfix$modelProvider = new DynamicModelProvider(
63+
null, // TODO
64+
this.missingModel,
65+
this.missingItemModel,
66+
Minecraft.getInstance().getResourceManager(),
67+
this.entityModelSet,
68+
reloadState.atlasPreparations()
69+
);
70+
}
71+
72+
/**
73+
* @author embeddedt
74+
* @reason use dynamic model system
75+
*/
76+
@Overwrite
77+
public BakedModel getModel(ModelResourceLocation modelLocation) {
78+
if(this.mfix$modelProvider != null) {
79+
return this.mfix$modelProvider.getModel(modelLocation);
80+
} else {
81+
return this.missingModel;
82+
}
83+
}
84+
85+
/**
86+
* @author embeddedt
87+
* @reason use dynamic model system
88+
*/
89+
@Overwrite
90+
public ItemModel getItemModel(ResourceLocation resourceLocation) {
91+
return this.mfix$modelProvider.getItemModel(resourceLocation);
92+
}
93+
94+
/**
95+
* @author embeddedt
96+
* @reason use dynamic model system
97+
*/
98+
@Overwrite
99+
public ClientItem.Properties getItemProperties(ResourceLocation resourceLocation) {
100+
return this.mfix$modelProvider.getClientItemProperties(resourceLocation);
101+
}
102+
}

0 commit comments

Comments
 (0)