Add Fabric Model Loading API support
This commit is contained in:
parent
e7e065f809
commit
a0cc8bfbd2
|
|
@ -20,6 +20,8 @@ dependencies {
|
|||
}
|
||||
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
|
||||
modCompileOnly fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)
|
||||
// Remove the next line if you don't want to depend on the API
|
||||
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
|
@ -50,6 +51,7 @@ import java.util.AbstractSet;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -97,6 +99,9 @@ public class DynamicModelProvider {
|
|||
private final Map<ModelResourceLocation, BakedModel> mrlModelOverrides = new ConcurrentHashMap<>();
|
||||
private final Map<ResourceLocation, ItemModel> itemStackModelOverrides = new ConcurrentHashMap<>();
|
||||
private final Map<ResourceLocation, BakedModel> standaloneModelOverrides = new ConcurrentHashMap<>();
|
||||
private final Map<ModelResourceLocation, UnbakedBlockStateModel> unbakedBlockStateModelOverrides = new ConcurrentHashMap<>();
|
||||
|
||||
private final List<DynamicModelProvider.DynamicModelPlugin> pluginList = new ArrayList<>();
|
||||
|
||||
private static final boolean DEBUG_DYNAMIC_MODEL_LOADING = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||
|
||||
|
|
@ -129,6 +134,12 @@ public class DynamicModelProvider {
|
|||
this.itemModelGenerator = new ItemModelGenerator();
|
||||
this.missingModel = this.bakeModel(this.unbakedMissingModel, () -> "missing");
|
||||
this.missingItemModel = new MissingItemModel(this.missingModel);
|
||||
try {
|
||||
Class.forName("net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin");
|
||||
pluginList.add(new FabricDynamicModelHandler(this));
|
||||
} catch(Exception ignored) {
|
||||
// Fabric API likely not present
|
||||
}
|
||||
}
|
||||
|
||||
public BakedModel getMissingBakedModel() {
|
||||
|
|
@ -329,7 +340,21 @@ public class DynamicModelProvider {
|
|||
ModernFix.LOGGER.error("Failed to load blockstate definition {} from pack '{}'", location, resource.sourcePackId(), e);
|
||||
}
|
||||
}
|
||||
return Optional.of(BlockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions, this.unbakedMissingModel));
|
||||
var loadedModels = new HashMap<>(BlockStateModelLoader.loadBlockStateDefinitionStack(location, stateDefinition, loadedDefinitions, this.unbakedMissingModel).models());
|
||||
if (!pluginList.isEmpty()) {
|
||||
loadedModels.replaceAll((mrl, oldModel) -> {
|
||||
UnbakedBlockStateModel ubm = oldModel.model();
|
||||
for (var plugin : pluginList) {
|
||||
ubm = plugin.modifyBlockModelOnLoad(ubm, mrl, oldModel.state());
|
||||
}
|
||||
if (ubm == oldModel.model()) {
|
||||
return oldModel;
|
||||
} else {
|
||||
return new BlockStateModelLoader.LoadedModel(oldModel.state(), ubm);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Optional.of(new BlockStateModelLoader.LoadedModels(loadedModels));
|
||||
}
|
||||
|
||||
private BakedModel bakeModel(UnbakedModel model, ModelDebugName name) {
|
||||
|
|
@ -364,15 +389,18 @@ public class DynamicModelProvider {
|
|||
if (location.variant().equals("standalone") || location.variant().equals("fabric_resource")) {
|
||||
return this.loadStandaloneModel(location.id());
|
||||
} 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;
|
||||
}
|
||||
});
|
||||
Optional<UnbakedBlockStateModel> unbakedModelOpt = Optional.ofNullable(this.unbakedBlockStateModelOverrides.get(location));
|
||||
if (unbakedModelOpt.isEmpty()) {
|
||||
var optLoadedModels = this.loadedStateDefinitions.getUnchecked(location.id());
|
||||
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::toString);
|
||||
});
|
||||
|
|
@ -389,12 +417,14 @@ public class DynamicModelProvider {
|
|||
});
|
||||
}
|
||||
|
||||
private Optional<UnbakedModel> loadBlockModel(ResourceLocation location) {
|
||||
private Optional<UnbakedModel> loadBlockModelDefault(ResourceLocation location) {
|
||||
if (DEBUG_DYNAMIC_MODEL_LOADING) {
|
||||
ModernFix.LOGGER.info("Loading block model '{}'", location);
|
||||
}
|
||||
if (location.equals(ItemModelGenerator.GENERATED_ITEM_MODEL_ID)) {
|
||||
return Optional.of(this.itemModelGenerator);
|
||||
} else if (location.equals(MissingBlockModel.LOCATION)) {
|
||||
return Optional.of(this.unbakedMissingModel);
|
||||
}
|
||||
var resource = this.resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(location.getNamespace(), "models/" + location.getPath() + ".json"));
|
||||
if(resource.isPresent()) {
|
||||
|
|
@ -411,6 +441,15 @@ public class DynamicModelProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private Optional<UnbakedModel> loadBlockModel(ResourceLocation location) {
|
||||
Optional<UnbakedModel> value = loadBlockModelDefault(location);
|
||||
for (var plugin : this.pluginList) {
|
||||
value = plugin.modifyModelOnLoad(value, location);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private Optional<ClientItem> loadClientItemProperties(ResourceLocation location) {
|
||||
if (DEBUG_DYNAMIC_MODEL_LOADING) {
|
||||
ModernFix.LOGGER.info("Loading client item '{}'", location);
|
||||
|
|
@ -460,6 +499,10 @@ public class DynamicModelProvider {
|
|||
return this.loadedStandaloneModels.getUnchecked(location).orElse(this.missingModel);
|
||||
}
|
||||
|
||||
public void addUnbakedBlockStateOverride(ModelResourceLocation location, UnbakedBlockStateModel model) {
|
||||
this.unbakedBlockStateModelOverrides.put(location, model);
|
||||
}
|
||||
|
||||
private class DynamicBaker implements ModelBaker {
|
||||
private final ModelDebugName modelDebugName;
|
||||
|
||||
|
|
@ -469,7 +512,7 @@ public class DynamicModelProvider {
|
|||
|
||||
@Override
|
||||
public BakedModel bake(ResourceLocation location, ModelState transform) {
|
||||
return DynamicModelProvider.this.loadBlockModel(location).map(unbakedModel -> {
|
||||
return DynamicModelProvider.this.loadedBlockModels.getUnchecked(location).map(unbakedModel -> {
|
||||
DynamicModelProvider.this.resolver.clearResolver();
|
||||
unbakedModel.resolveDependencies(DynamicModelProvider.this.resolver);
|
||||
return UnbakedModel.bakeWithTopModelValues(unbakedModel, this, transform);
|
||||
|
|
@ -526,4 +569,9 @@ public class DynamicModelProvider {
|
|||
public interface ModelManagerExtension {
|
||||
DynamicModelProvider mfix$getModelProvider();
|
||||
}
|
||||
|
||||
public interface DynamicModelPlugin {
|
||||
Optional<UnbakedModel> modifyModelOnLoad(Optional<UnbakedModel> model, ResourceLocation id);
|
||||
UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, ModelResourceLocation id, BlockState state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.renderer.block.model.UnbakedBlockStateModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FabricDynamicModelHandler implements DynamicModelProvider.DynamicModelPlugin {
|
||||
private final List<ModelLoadingPlugin> pluginList;
|
||||
|
||||
// Borrowed from Fabric API, this dispatching logic is extremely trivial
|
||||
|
||||
private static final ResourceLocation[] MODEL_MODIFIER_PHASES = new ResourceLocation[] { ModelModifier.OVERRIDE_PHASE, ModelModifier.DEFAULT_PHASE, ModelModifier.WRAP_PHASE, ModelModifier.WRAP_LAST_PHASE };
|
||||
|
||||
private final Event<ModelModifier.OnLoad> onLoadModifiers = EventFactory.createWithPhases(ModelModifier.OnLoad.class, modifiers -> (model, context) -> {
|
||||
for (ModelModifier.OnLoad modifier : modifiers) {
|
||||
try {
|
||||
model = modifier.modifyModelOnLoad(model, context);
|
||||
} catch (Exception exception) {
|
||||
ModernFix.LOGGER.error("Failed to modify unbaked model on load", exception);
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}, MODEL_MODIFIER_PHASES);
|
||||
|
||||
private final Event<ModelModifier.OnLoadBlock> onLoadBlockModifiers = EventFactory.createWithPhases(ModelModifier.OnLoadBlock.class, modifiers -> (model, context) -> {
|
||||
for (ModelModifier.OnLoadBlock modifier : modifiers) {
|
||||
try {
|
||||
model = modifier.modifyModelOnLoad(model, context);
|
||||
} catch (Exception exception) {
|
||||
ModernFix.LOGGER.error("Failed to modify unbaked block model on load", exception);
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}, MODEL_MODIFIER_PHASES);
|
||||
|
||||
public FabricDynamicModelHandler(DynamicModelProvider provider) {
|
||||
this.pluginList = ModelLoadingPlugin.getAll();
|
||||
var context = new PluginContext(provider);
|
||||
for (var plugin : this.pluginList) {
|
||||
plugin.initialize(context);
|
||||
}
|
||||
context.fireResolvers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UnbakedModel> modifyModelOnLoad(Optional<UnbakedModel> model, ResourceLocation id) {
|
||||
return Optional.ofNullable(this.onLoadModifiers.invoker().modifyModelOnLoad(model.orElse(null), () -> id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedBlockStateModel modifyBlockModelOnLoad(UnbakedBlockStateModel model, ModelResourceLocation id, BlockState state) {
|
||||
return this.onLoadBlockModifiers.invoker().modifyModelOnLoad(model, new ModelModifier.OnLoadBlock.Context() {
|
||||
@Override
|
||||
public ModelResourceLocation id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState state() {
|
||||
return state;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class PluginContext implements ModelLoadingPlugin.Context {
|
||||
private final DynamicModelProvider provider;
|
||||
private final Map<Block, BlockStateResolver> resolvers = new HashMap<>();
|
||||
|
||||
private PluginContext(DynamicModelProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModels(ResourceLocation... ids) {
|
||||
/* no-op on dynamic model loader */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModels(Collection<? extends ResourceLocation> ids) {
|
||||
/* no-op on dynamic model loader */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBlockStateResolver(Block block, BlockStateResolver resolver) {
|
||||
resolvers.put(block, resolver);
|
||||
}
|
||||
|
||||
public void fireResolvers() {
|
||||
resolvers.forEach((block, resolver) -> {
|
||||
resolver.resolveBlockStates(new BlockStateResolver.Context() {
|
||||
@Override
|
||||
public Block block() {
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModel(BlockState state, UnbakedBlockStateModel model) {
|
||||
provider.addUnbakedBlockStateOverride(BlockModelShaper.stateToModelLocation(state), model);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event<ModelModifier.OnLoad> modifyModelOnLoad() {
|
||||
return onLoadModifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event<ModelModifier.OnLoadBlock> modifyBlockModelOnLoad() {
|
||||
return onLoadBlockModifiers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,13 +36,10 @@ dependencies {
|
|||
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
if(project.use_fabric_api_at_runtime.toBoolean()) {
|
||||
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modImplementation "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||
} else {
|
||||
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
}
|
||||
|
||||
// Remove the next line if you don't want to depend on the API
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user