mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-05 19:47:00 -04:00
Finish porting Model Loading, FRAPI, and Indigo (#4043)
This commit is contained in:
parent
1bb677a646
commit
236902d814
37 changed files with 379 additions and 652 deletions
fabric-model-loading-api-v1/src
client
java/net/fabricmc/fabric
api/client/model/loading/v1
BlockStateResolver.javaDelegatingUnbakedModel.javaModelLoadingPlugin.javaModelModifier.javaModelResolver.javaPreparableModelLoadingPlugin.java
impl/client/model/loading
ModelLoaderHooks.javaModelLoadingEventDispatcher.javaModelLoadingPluginContextImpl.javaModelLoadingPluginManager.java
mixin/client/model/loading
resources
testmodClient
java/net/fabricmc/fabric/test/model/loading
resources
fabric-renderer-api-v1/src
client/java/net/fabricmc/fabric
api/renderer/v1/mesh
impl/renderer
testmod/java/net/fabricmc/fabric/test/renderer
testmodClient/java/net/fabricmc/fabric/test/renderer/client
fabric-renderer-indigo/src/client/java/net/fabricmc/fabric
settings.gradle
|
@ -20,9 +20,7 @@ import org.jetbrains.annotations.ApiStatus;
|
|||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Block state resolvers are responsible for mapping each {@link BlockState} of a block to an {@link UnbakedModel}.
|
||||
|
@ -69,18 +67,5 @@ public interface BlockStateResolver {
|
|||
* @param model the unbaked model for this block state
|
||||
*/
|
||||
void setModel(BlockState state, UnbakedModel model);
|
||||
|
||||
/**
|
||||
* Loads a model using an {@link Identifier}, or gets it if it was already loaded.
|
||||
*
|
||||
* @param id the model identifier
|
||||
* @return the unbaked model, or a missing model if it is not present
|
||||
*/
|
||||
UnbakedModel getOrLoadModel(Identifier id);
|
||||
|
||||
/**
|
||||
* The current model loader instance, which changes between resource reloads.
|
||||
*/
|
||||
ModelLoader loader();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.api.client.model.loading.v1;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -37,7 +35,6 @@ import net.minecraft.util.Identifier;
|
|||
*/
|
||||
public final class DelegatingUnbakedModel implements UnbakedModel {
|
||||
private final Identifier delegate;
|
||||
private final List<Identifier> dependencies;
|
||||
|
||||
/**
|
||||
* Constructs a new delegating model.
|
||||
|
@ -46,23 +43,11 @@ public final class DelegatingUnbakedModel implements UnbakedModel {
|
|||
*/
|
||||
public DelegatingUnbakedModel(Identifier delegate) {
|
||||
this.delegate = delegate;
|
||||
this.dependencies = List.of(delegate);
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
|
||||
modelLoader.apply(delegate).setParents(modelLoader);
|
||||
}*/
|
||||
|
||||
// TODO 24w44a
|
||||
@Override
|
||||
public void method_62326(class_10103 arg, class_10102 arg2) {
|
||||
|
||||
arg.method_62642(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,7 +47,7 @@ public interface ModelLoadingPlugin {
|
|||
* Called towards the beginning of the model loading process, every time resource are (re)loaded.
|
||||
* Use the context object to extend model loading as desired.
|
||||
*/
|
||||
void onInitializeModelLoader(Context pluginContext);
|
||||
void initialize(Context pluginContext);
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface Context {
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.jetbrains.annotations.UnknownNullability;
|
|||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
|
@ -109,19 +108,6 @@ public final class ModelModifier {
|
|||
*/
|
||||
@UnknownNullability("#resourceId() != null")
|
||||
ModelIdentifier topLevelId();
|
||||
|
||||
/**
|
||||
* Loads a model using an {@link Identifier}, or gets it if it was already loaded.
|
||||
*
|
||||
* @param id the model identifier
|
||||
* @return the unbaked model, or a missing model if it is not present
|
||||
*/
|
||||
UnbakedModel getOrLoadModel(Identifier id);
|
||||
|
||||
/**
|
||||
* The current model loader instance, which changes between resource reloads.
|
||||
*/
|
||||
ModelLoader loader();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,11 +163,6 @@ public final class ModelModifier {
|
|||
* {@linkplain Baker#bake load baked models}.
|
||||
*/
|
||||
Baker baker();
|
||||
|
||||
/**
|
||||
* The current model loader instance, which changes between resource reloads.
|
||||
*/
|
||||
ModelLoader loader();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,11 +231,6 @@ public final class ModelModifier {
|
|||
* {@linkplain Baker#bake load baked models}.
|
||||
*/
|
||||
Baker baker();
|
||||
|
||||
/**
|
||||
* The current model loader instance, which changes between resource reloads.
|
||||
*/
|
||||
ModelLoader loader();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ package net.fabricmc.fabric.api.client.model.loading.v1;
|
|||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
|
@ -61,18 +60,5 @@ public interface ModelResolver {
|
|||
* The identifier of the model to be loaded.
|
||||
*/
|
||||
Identifier id();
|
||||
|
||||
/**
|
||||
* Loads a model using an {@link Identifier}, or gets it if it was already loaded.
|
||||
*
|
||||
* @param id the model identifier
|
||||
* @return the unbaked model, or a missing model if it is not present
|
||||
*/
|
||||
UnbakedModel getOrLoadModel(Identifier id);
|
||||
|
||||
/**
|
||||
* The current model loader instance, which changes between resource reloads.
|
||||
*/
|
||||
ModelLoader loader();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public interface PreparableModelLoadingPlugin<T> {
|
|||
* @param data The data loaded by the {@link DataLoader}.
|
||||
* @param pluginContext The context that can be used to extend model loading.
|
||||
*/
|
||||
void onInitializeModelLoader(T data, ModelLoadingPlugin.Context pluginContext);
|
||||
void initialize(T data, ModelLoadingPlugin.Context pluginContext);
|
||||
|
||||
@FunctionalInterface
|
||||
interface DataLoader<T> {
|
||||
|
|
|
@ -16,16 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.client.model.loading;
|
||||
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface ModelLoaderHooks {
|
||||
ModelLoadingEventDispatcher fabric_getDispatcher();
|
||||
|
||||
UnbakedModel fabric_getMissingModel();
|
||||
|
||||
UnbakedModel fabric_getOrLoadModel(Identifier id);
|
||||
|
||||
void fabric_add(ModelIdentifier id, UnbakedModel model);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.client.model.loading;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -35,13 +39,14 @@ import net.minecraft.block.BlockState;
|
|||
import net.minecraft.client.render.block.BlockModels;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.BlockStateResolver;
|
||||
|
@ -51,24 +56,23 @@ import net.fabricmc.fabric.api.client.model.loading.v1.ModelResolver;
|
|||
|
||||
public class ModelLoadingEventDispatcher {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModelLoadingEventDispatcher.class);
|
||||
public static final ThreadLocal<ModelLoadingEventDispatcher> CURRENT = new ThreadLocal<>();
|
||||
|
||||
private final ModelLoader loader;
|
||||
private final ModelLoadingPluginContextImpl pluginContext;
|
||||
|
||||
private final ObjectArrayList<ModelResolverContext> modelResolverContextStack = new ObjectArrayList<>();
|
||||
private final ModelResolverContext modelResolverContext = new ModelResolverContext();
|
||||
private final BlockStateResolverContext blockStateResolverContext = new BlockStateResolverContext();
|
||||
|
||||
private final ObjectArrayList<OnLoadModifierContext> onLoadModifierContextStack = new ObjectArrayList<>();
|
||||
private final OnLoadModifierContext onLoadModifierContext = new OnLoadModifierContext();
|
||||
private final ObjectArrayList<BeforeBakeModifierContext> beforeBakeModifierContextStack = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<AfterBakeModifierContext> afterBakeModifierContextStack = new ObjectArrayList<>();
|
||||
|
||||
public ModelLoadingEventDispatcher(ModelLoader loader, List<ModelLoadingPlugin> plugins) {
|
||||
this.loader = loader;
|
||||
public ModelLoadingEventDispatcher(List<ModelLoadingPlugin> plugins) {
|
||||
this.pluginContext = new ModelLoadingPluginContextImpl();
|
||||
|
||||
for (ModelLoadingPlugin plugin : plugins) {
|
||||
try {
|
||||
plugin.onInitializeModelLoader(pluginContext);
|
||||
plugin.initialize(pluginContext);
|
||||
} catch (Exception exception) {
|
||||
LOGGER.error("Failed to initialize model loading plugin", exception);
|
||||
}
|
||||
|
@ -81,18 +85,30 @@ public class ModelLoadingEventDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean loadBlockStateModels(Identifier id, StateManager<Block, BlockState> stateManager) {
|
||||
BlockStateResolver resolver = pluginContext.blockStateResolvers.get(id);
|
||||
public BlockStatesLoader.class_10095 loadBlockStateModels() {
|
||||
Map<ModelIdentifier, BlockStatesLoader.BlockModel> map = new HashMap<>();
|
||||
|
||||
if (resolver != null) {
|
||||
resolveBlockStates(resolver, stateManager.getOwner(), id);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
pluginContext.blockStateResolvers.forEach((block, resolver) -> {
|
||||
Optional<RegistryKey<Block>> optionalKey = Registries.BLOCK.getKey(block);
|
||||
|
||||
if (optionalKey.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Identifier blockId = optionalKey.get().getValue();
|
||||
|
||||
BiConsumer<BlockState, UnbakedModel> output = (state, model) -> {
|
||||
ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
|
||||
map.put(modelId, new BlockStatesLoader.BlockModel(state, model));
|
||||
};
|
||||
|
||||
resolveBlockStates(resolver, block, output);
|
||||
});
|
||||
|
||||
return new BlockStatesLoader.class_10095(map);
|
||||
}
|
||||
|
||||
private void resolveBlockStates(BlockStateResolver resolver, Block block, Identifier blockId) {
|
||||
private void resolveBlockStates(BlockStateResolver resolver, Block block, BiConsumer<BlockState, UnbakedModel> output) {
|
||||
BlockStateResolverContext context = blockStateResolverContext;
|
||||
context.prepare(block);
|
||||
|
||||
|
@ -100,7 +116,6 @@ public class ModelLoadingEventDispatcher {
|
|||
ImmutableList<BlockState> allStates = block.getStateManager().getStates();
|
||||
boolean thrown = false;
|
||||
|
||||
// Call resolver
|
||||
try {
|
||||
resolver.resolveBlockStates(context);
|
||||
} catch (Exception e) {
|
||||
|
@ -108,34 +123,21 @@ public class ModelLoadingEventDispatcher {
|
|||
thrown = true;
|
||||
}
|
||||
|
||||
// Copy models over to the loader
|
||||
if (thrown) {
|
||||
UnbakedModel missingModel = ((ModelLoaderHooks) loader).fabric_getMissingModel();
|
||||
if (!thrown) {
|
||||
if (resolvedModels.size() == allStates.size()) {
|
||||
// If there are as many resolved models as total states, all states have
|
||||
// been resolved and models do not need to be null-checked.
|
||||
resolvedModels.forEach(output);
|
||||
} else {
|
||||
for (BlockState state : allStates) {
|
||||
@Nullable
|
||||
UnbakedModel model = resolvedModels.get(state);
|
||||
|
||||
for (BlockState state : allStates) {
|
||||
ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
|
||||
((ModelLoaderHooks) loader).fabric_add(modelId, missingModel);
|
||||
}
|
||||
} else if (resolvedModels.size() == allStates.size()) {
|
||||
// If there are as many resolved models as total states, all states have
|
||||
// been resolved and models do not need to be null-checked.
|
||||
resolvedModels.forEach((state, model) -> {
|
||||
ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
|
||||
((ModelLoaderHooks) loader).fabric_add(modelId, model);
|
||||
});
|
||||
} else {
|
||||
UnbakedModel missingModel = ((ModelLoaderHooks) loader).fabric_getMissingModel();
|
||||
|
||||
for (BlockState state : allStates) {
|
||||
ModelIdentifier modelId = BlockModels.getModelId(blockId, state);
|
||||
@Nullable
|
||||
UnbakedModel model = resolvedModels.get(state);
|
||||
|
||||
if (model == null) {
|
||||
LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block);
|
||||
((ModelLoaderHooks) loader).fabric_add(modelId, missingModel);
|
||||
} else {
|
||||
((ModelLoaderHooks) loader).fabric_add(modelId, model);
|
||||
if (model == null) {
|
||||
LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block);
|
||||
} else {
|
||||
output.accept(state, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,31 +147,13 @@ public class ModelLoadingEventDispatcher {
|
|||
|
||||
@Nullable
|
||||
public UnbakedModel resolveModel(Identifier id) {
|
||||
if (modelResolverContextStack.isEmpty()) {
|
||||
modelResolverContextStack.add(new ModelResolverContext());
|
||||
}
|
||||
|
||||
ModelResolverContext context = modelResolverContextStack.pop();
|
||||
context.prepare(id);
|
||||
|
||||
UnbakedModel model = pluginContext.resolveModel().invoker().resolveModel(context);
|
||||
|
||||
modelResolverContextStack.push(context);
|
||||
return model;
|
||||
modelResolverContext.prepare(id);
|
||||
return pluginContext.resolveModel().invoker().resolveModel(modelResolverContext);
|
||||
}
|
||||
|
||||
public UnbakedModel modifyModelOnLoad(UnbakedModel model, @UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId) {
|
||||
if (onLoadModifierContextStack.isEmpty()) {
|
||||
onLoadModifierContextStack.add(new OnLoadModifierContext());
|
||||
}
|
||||
|
||||
OnLoadModifierContext context = onLoadModifierContextStack.pop();
|
||||
context.prepare(resourceId, topLevelId);
|
||||
|
||||
model = pluginContext.modifyModelOnLoad().invoker().modifyModelOnLoad(model, context);
|
||||
|
||||
onLoadModifierContextStack.push(context);
|
||||
return model;
|
||||
onLoadModifierContext.prepare(resourceId, topLevelId);
|
||||
return pluginContext.modifyModelOnLoad().invoker().modifyModelOnLoad(model, onLoadModifierContext);
|
||||
}
|
||||
|
||||
public UnbakedModel modifyModelBeforeBake(UnbakedModel model, @UnknownNullability Identifier resourceId, @UnknownNullability ModelIdentifier topLevelId, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings settings, Baker baker) {
|
||||
|
@ -201,7 +185,7 @@ public class ModelLoadingEventDispatcher {
|
|||
return model;
|
||||
}
|
||||
|
||||
private class ModelResolverContext implements ModelResolver.Context {
|
||||
private static class ModelResolverContext implements ModelResolver.Context {
|
||||
private Identifier id;
|
||||
|
||||
private void prepare(Identifier id) {
|
||||
|
@ -212,19 +196,9 @@ public class ModelLoadingEventDispatcher {
|
|||
public Identifier id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel getOrLoadModel(Identifier id) {
|
||||
return ((ModelLoaderHooks) loader).fabric_getOrLoadModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader loader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockStateResolverContext implements BlockStateResolver.Context {
|
||||
private static class BlockStateResolverContext implements BlockStateResolver.Context {
|
||||
private Block block;
|
||||
private final Reference2ReferenceMap<BlockState, UnbakedModel> models = new Reference2ReferenceOpenHashMap<>();
|
||||
|
||||
|
@ -251,19 +225,9 @@ public class ModelLoadingEventDispatcher {
|
|||
throw new IllegalStateException("Duplicate model for state " + state + " on block " + block);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel getOrLoadModel(Identifier id) {
|
||||
return ((ModelLoaderHooks) loader).fabric_getOrLoadModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader loader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
private class OnLoadModifierContext implements ModelModifier.OnLoad.Context {
|
||||
private static class OnLoadModifierContext implements ModelModifier.OnLoad.Context {
|
||||
@UnknownNullability
|
||||
private Identifier resourceId;
|
||||
@UnknownNullability
|
||||
|
@ -285,19 +249,9 @@ public class ModelLoadingEventDispatcher {
|
|||
public ModelIdentifier topLevelId() {
|
||||
return topLevelId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel getOrLoadModel(Identifier id) {
|
||||
return ((ModelLoaderHooks) loader).fabric_getOrLoadModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader loader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
private class BeforeBakeModifierContext implements ModelModifier.BeforeBake.Context {
|
||||
private static class BeforeBakeModifierContext implements ModelModifier.BeforeBake.Context {
|
||||
@UnknownNullability
|
||||
private Identifier resourceId;
|
||||
@UnknownNullability
|
||||
|
@ -340,14 +294,9 @@ public class ModelLoadingEventDispatcher {
|
|||
public Baker baker() {
|
||||
return baker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader loader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
private class AfterBakeModifierContext implements ModelModifier.AfterBake.Context {
|
||||
private static class AfterBakeModifierContext implements ModelModifier.AfterBake.Context {
|
||||
@UnknownNullability
|
||||
private Identifier resourceId;
|
||||
@UnknownNullability
|
||||
|
@ -397,10 +346,5 @@ public class ModelLoadingEventDispatcher {
|
|||
public Baker baker() {
|
||||
return baker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader loader() {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package net.fabricmc.fabric.impl.client.model.loading;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -44,7 +44,7 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
|
|||
private static final Logger LOGGER = LoggerFactory.getLogger(ModelLoadingPluginContextImpl.class);
|
||||
|
||||
final Set<Identifier> extraModels = new LinkedHashSet<>();
|
||||
final Map<Identifier, BlockStateResolver> blockStateResolvers = new HashMap<>();
|
||||
final Map<Block, BlockStateResolver> blockStateResolvers = new IdentityHashMap<>();
|
||||
|
||||
private final Event<ModelResolver> modelResolvers = EventFactory.createArrayBacked(ModelResolver.class, resolvers -> context -> {
|
||||
for (ModelResolver resolver : resolvers) {
|
||||
|
@ -124,9 +124,7 @@ public class ModelLoadingPluginContextImpl implements ModelLoadingPlugin.Context
|
|||
throw new IllegalArgumentException("Received unregistered block");
|
||||
}
|
||||
|
||||
Identifier blockId = optionalKey.get().getValue();
|
||||
|
||||
if (blockStateResolvers.put(blockId, resolver) != null) {
|
||||
if (blockStateResolvers.put(block, resolver) != null) {
|
||||
throw new IllegalArgumentException("Duplicate block state resolver for " + block);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,11 +47,6 @@ public final class ModelLoadingPluginManager {
|
|||
PREPARABLE_PLUGINS.add(new PreparablePluginHolder<>(loader, plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* The current exception behavior as of 1.20 is as follows.
|
||||
* If getting a {@link CompletableFuture}s throws then the whole client will crash.
|
||||
* If a {@link CompletableFuture} completes exceptionally then the resource reload will fail.
|
||||
*/
|
||||
public static CompletableFuture<List<ModelLoadingPlugin>> preparePlugins(ResourceManager resourceManager, Executor executor) {
|
||||
List<CompletableFuture<ModelLoadingPlugin>> futures = new ArrayList<>();
|
||||
|
||||
|
@ -63,12 +58,12 @@ public final class ModelLoadingPluginManager {
|
|||
futures.add(preparePlugin(holder, resourceManager, executor));
|
||||
}
|
||||
|
||||
return Util.combine(futures);
|
||||
return Util.combineSafe(futures);
|
||||
}
|
||||
|
||||
private static <T> CompletableFuture<ModelLoadingPlugin> preparePlugin(PreparablePluginHolder<T> holder, ResourceManager resourceManager, Executor executor) {
|
||||
CompletableFuture<T> dataFuture = holder.loader.load(resourceManager, executor);
|
||||
return dataFuture.thenApply(data -> pluginContext -> holder.plugin.onInitializeModelLoader(data, pluginContext));
|
||||
return dataFuture.thenApply(data -> pluginContext -> holder.plugin.initialize(data, pluginContext));
|
||||
}
|
||||
|
||||
private ModelLoadingPluginManager() { }
|
||||
|
|
|
@ -16,17 +16,24 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.client.model.loading;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.class_10097;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
|
@ -41,12 +48,15 @@ import net.minecraft.util.Pair;
|
|||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.FabricBakedModelManager;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingConstants;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingPluginManager;
|
||||
|
||||
@Mixin(BakedModelManager.class)
|
||||
abstract class BakedModelManagerMixin implements FabricBakedModelManager {
|
||||
@Unique
|
||||
private volatile CompletableFuture<ModelLoadingEventDispatcher> eventDispatcherFuture;
|
||||
|
||||
@Shadow
|
||||
private Map<ModelIdentifier, BakedModel> models;
|
||||
|
||||
|
@ -55,33 +65,64 @@ abstract class BakedModelManagerMixin implements FabricBakedModelManager {
|
|||
return models.get(ModelLoadingConstants.toResourceModelId(id));
|
||||
}
|
||||
|
||||
@Inject(method = "reload", at = @At("HEAD"))
|
||||
private void onHeadReload(ResourceReloader.Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor, CallbackInfoReturnable<CompletableFuture<Void>> cir) {
|
||||
eventDispatcherFuture = ModelLoadingPluginManager.preparePlugins(manager, prepareExecutor).thenApplyAsync(ModelLoadingEventDispatcher::new);
|
||||
}
|
||||
|
||||
@ModifyReturnValue(method = "reload", at = @At("RETURN"))
|
||||
private CompletableFuture<Void> resetEventDispatcherFuture(CompletableFuture<Void> future) {
|
||||
return future.thenApplyAsync(v -> {
|
||||
eventDispatcherFuture = null;
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "reload", at = @At(value = "INVOKE", target = "net/minecraft/client/render/model/BakedModelManager.reloadBlockStates(Lnet/minecraft/client/render/model/BlockStatesLoader;Lnet/minecraft/resource/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture<BlockStatesLoader.class_10095> hookBlockStateModelLoading(CompletableFuture<BlockStatesLoader.class_10095> modelsFuture) {
|
||||
CompletableFuture<BlockStatesLoader.class_10095> resolvedModelsFuture = eventDispatcherFuture.thenApplyAsync(ModelLoadingEventDispatcher::loadBlockStateModels);
|
||||
return modelsFuture.thenCombine(resolvedModelsFuture, (models, resolvedModels) -> {
|
||||
Map<ModelIdentifier, BlockStatesLoader.BlockModel> map = models.models();
|
||||
|
||||
if (!(map instanceof HashMap)) {
|
||||
map = new HashMap<>(map);
|
||||
models = new BlockStatesLoader.class_10095(map);
|
||||
}
|
||||
|
||||
map.putAll(resolvedModels.models());
|
||||
return models;
|
||||
});
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "reload",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "java/util/concurrent/CompletableFuture.thenCombineAsync(Ljava/util/concurrent/CompletionStage;Ljava/util/function/BiFunction;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;",
|
||||
ordinal = 0,
|
||||
remap = false
|
||||
),
|
||||
allow = 1)
|
||||
private CompletableFuture<class_10097> loadModelPluginData(
|
||||
))
|
||||
private CompletableFuture<class_10097> hookModelDiscovery(
|
||||
CompletableFuture<BlockStatesLoader.class_10095> self,
|
||||
CompletionStage<Map<Identifier, UnbakedModel>> otherFuture,
|
||||
BiFunction<BlockStatesLoader.class_10095, Map<Identifier, UnbakedModel>, class_10097> function,
|
||||
Executor executor,
|
||||
// reload args
|
||||
ResourceReloader.Synchronizer synchronizer,
|
||||
ResourceManager manager,
|
||||
Profiler prepareProfiler,
|
||||
Profiler applyProfiler,
|
||||
Executor prepareExecutor,
|
||||
Executor applyExecutor) {
|
||||
CompletableFuture<List<ModelLoadingPlugin>> pluginsFuture = ModelLoadingPluginManager.preparePlugins(manager, prepareExecutor);
|
||||
Executor executor) {
|
||||
CompletableFuture<Pair<BlockStatesLoader.class_10095, Map<Identifier, UnbakedModel>>> pairFuture = self.thenCombine(otherFuture, Pair::new);
|
||||
return pairFuture.thenCombineAsync(pluginsFuture, (pair, plugins) -> {
|
||||
ModelLoadingPluginManager.CURRENT_PLUGINS.set(plugins);
|
||||
class_10097 modelLoader = function.apply(pair.getLeft(), pair.getRight());
|
||||
ModelLoadingPluginManager.CURRENT_PLUGINS.remove();
|
||||
return modelLoader;
|
||||
return pairFuture.thenCombineAsync(eventDispatcherFuture, (pair, eventDispatcher) -> {
|
||||
ModelLoadingEventDispatcher.CURRENT.set(eventDispatcher);
|
||||
class_10097 class_10097 = function.apply(pair.getLeft(), pair.getRight());
|
||||
ModelLoadingEventDispatcher.CURRENT.remove();
|
||||
return class_10097;
|
||||
}, executor);
|
||||
}
|
||||
|
||||
@ModifyArg(method = "reload", at = @At(value = "INVOKE", target = "java/util/concurrent/CompletableFuture.thenApplyAsync (Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;", ordinal = 1), index = 0)
|
||||
private Function<Void, Object> hookModelBaking(Function<Void, Object> function) {
|
||||
return v -> {
|
||||
ModelLoadingEventDispatcher.CURRENT.set(eventDispatcherFuture.join());
|
||||
Object bakingResult = function.apply(v);
|
||||
ModelLoadingEventDispatcher.CURRENT.remove();
|
||||
return bakingResult;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.model.loading;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.model.loading.BlockStatesLoaderHooks;
|
||||
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BlockStatesLoader.class)
|
||||
abstract class BlockStatesLoaderMixin implements BlockStatesLoaderHooks {
|
||||
@Unique
|
||||
@Nullable
|
||||
private LoadingOverride loadingOverride;
|
||||
|
||||
// TODO 24w33a - Mappings
|
||||
@Inject(method = "method_62627", at = @At("HEAD"), cancellable = true)
|
||||
private void onHeadLoadBlockStates(Identifier id, StateManager<Block, BlockState> stateManager, List<BlockStatesLoader.class_10094> list, CallbackInfoReturnable<BlockStatesLoader.class_10095> cir) {
|
||||
if (loadingOverride != null && loadingOverride.loadBlockStates(id, stateManager)) {
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fabric_setLoadingOverride(LoadingOverride override) {
|
||||
loadingOverride = override;
|
||||
}
|
||||
}
|
|
@ -16,162 +16,54 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.client.model.loading;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.Coerce;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.class_10096;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.render.model.json.JsonUnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.model.loading.BakerImplHooks;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.BlockStatesLoaderHooks;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoaderHooks;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingConstants;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingPluginManager;
|
||||
|
||||
@Mixin(ModelLoader.class)
|
||||
abstract class ModelLoaderMixin implements ModelLoaderHooks {
|
||||
@Final
|
||||
@Shadow
|
||||
private Set<Identifier> modelsToLoad;
|
||||
@Final
|
||||
@Shadow
|
||||
private Map<Identifier, UnbakedModel> unbakedModels;
|
||||
@Shadow
|
||||
@Final
|
||||
private Map<ModelIdentifier, UnbakedModel> modelsToBake;
|
||||
@Shadow
|
||||
@Final
|
||||
private UnbakedModel missingModel;
|
||||
|
||||
@Unique
|
||||
@Nullable
|
||||
private ModelLoadingEventDispatcher fabric_eventDispatcher;
|
||||
@Unique
|
||||
private final ObjectLinkedOpenHashSet<Identifier> modelLoadingStack = new ObjectLinkedOpenHashSet<>();
|
||||
|
||||
@Shadow
|
||||
abstract UnbakedModel getOrLoadModel(Identifier id);
|
||||
|
||||
@Shadow
|
||||
abstract void add(ModelIdentifier id, UnbakedModel unbakedModel);
|
||||
|
||||
@Shadow
|
||||
abstract JsonUnbakedModel loadModelFromJson(Identifier id);
|
||||
|
||||
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BlockStatesLoader;load()V"))
|
||||
private void afterMissingModelInit(BlockColors blockColors, Profiler profiler, Map<Identifier, JsonUnbakedModel> jsonUnbakedModels, Map<Identifier, List<BlockStatesLoader.SourceTrackedData>> blockStates, CallbackInfo info, @Local BlockStatesLoader blockStatesLoader) {
|
||||
// Sanity check
|
||||
if (missingModel == null || !modelsToBake.containsKey(ModelLoader.MISSING_MODEL_ID)) {
|
||||
throw new AssertionError("Missing model not initialized. This is likely a Fabric API porting bug.");
|
||||
}
|
||||
|
||||
// Add the missing model to the cache since vanilla doesn't. Mods may load/bake the missing model directly.
|
||||
unbakedModels.put(ModelLoader.MISSING_ID, missingModel);
|
||||
profiler.swap("fabric_plugins_init");
|
||||
|
||||
fabric_eventDispatcher = new ModelLoadingEventDispatcher((ModelLoader) (Object) this, ModelLoadingPluginManager.CURRENT_PLUGINS.get());
|
||||
fabric_eventDispatcher.addExtraModels(this::addExtraModel);
|
||||
((BlockStatesLoaderHooks) blockStatesLoader).fabric_setLoadingOverride(fabric_eventDispatcher::loadBlockStateModels);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void addExtraModel(Identifier id) {
|
||||
ModelIdentifier modelId = ModelLoadingConstants.toResourceModelId(id);
|
||||
UnbakedModel unbakedModel = getOrLoadModel(id);
|
||||
add(modelId, unbakedModel);
|
||||
}
|
||||
|
||||
@Inject(method = "getOrLoadModel", at = @At("HEAD"), cancellable = true)
|
||||
private void allowRecursiveLoading(Identifier id, CallbackInfoReturnable<UnbakedModel> cir) {
|
||||
// If the stack is empty, this is the top-level call, so proceed as normal.
|
||||
if (!modelLoadingStack.isEmpty()) {
|
||||
if (unbakedModels.containsKey(id)) {
|
||||
cir.setReturnValue(unbakedModels.get(id));
|
||||
} else if (modelLoadingStack.contains(id)) {
|
||||
throw new IllegalStateException("Circular reference while loading model '" + id + "' (" + modelLoadingStack.stream().map(i -> i + "->").collect(Collectors.joining()) + id + ")");
|
||||
} else {
|
||||
UnbakedModel model = loadModel(id);
|
||||
unbakedModels.put(id, model);
|
||||
// These will be loaded at the top-level call.
|
||||
modelsToLoad.addAll(model.getModelDependencies());
|
||||
cir.setReturnValue(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the call that needs to be redirected to support ModelResolvers, but it returns a JsonUnbakedModel.
|
||||
// Redirect it to always return null and handle the logic in a ModifyVariable right after the call.
|
||||
@Redirect(method = "getOrLoadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/ModelLoader;loadModelFromJson(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/json/JsonUnbakedModel;"))
|
||||
private JsonUnbakedModel cancelLoadModelFromJson(ModelLoader self, Identifier id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "getOrLoadModel", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/client/render/model/ModelLoader;loadModelFromJson(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/model/json/JsonUnbakedModel;"))
|
||||
private UnbakedModel doLoadModel(UnbakedModel model, @Local(ordinal = 1) Identifier id) {
|
||||
return loadModel(id);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private UnbakedModel loadModel(Identifier id) {
|
||||
modelLoadingStack.add(id);
|
||||
|
||||
try {
|
||||
UnbakedModel model = fabric_eventDispatcher.resolveModel(id);
|
||||
|
||||
if (model == null) {
|
||||
model = loadModelFromJson(id);
|
||||
}
|
||||
|
||||
return fabric_eventDispatcher.modifyModelOnLoad(model, id, null);
|
||||
} finally {
|
||||
modelLoadingStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "add", at = @At("HEAD"), argsOnly = true)
|
||||
private UnbakedModel onAdd(UnbakedModel model, ModelIdentifier id) {
|
||||
if (ModelLoadingConstants.isResourceModelId(id)) {
|
||||
return model;
|
||||
}
|
||||
|
||||
return fabric_eventDispatcher.modifyModelOnLoad(model, null, id);
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onReturnInit(CallbackInfo ci) {
|
||||
fabric_eventDispatcher = ModelLoadingEventDispatcher.CURRENT.get();
|
||||
}
|
||||
|
||||
@WrapOperation(method = "method_61072(Lnet/minecraft/client/render/model/ModelLoader$SpriteGetter;Lnet/minecraft/client/util/ModelIdentifier;Lnet/minecraft/client/render/model/UnbakedModel;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/ModelLoader$BakerImpl;bake(Lnet/minecraft/client/render/model/UnbakedModel;Lnet/minecraft/client/render/model/ModelBakeSettings;)Lnet/minecraft/client/render/model/BakedModel;"))
|
||||
private BakedModel wrapSingleOuterBake(@Coerce Baker baker, UnbakedModel unbakedModel, ModelBakeSettings settings, Operation<BakedModel> operation, ModelLoader.SpriteGetter spriteGetter, ModelIdentifier id) {
|
||||
if (ModelLoadingConstants.isResourceModelId(id) || id.equals(ModelLoader.MISSING_MODEL_ID)) {
|
||||
if (fabric_eventDispatcher == null) {
|
||||
return operation.call(baker, unbakedModel, settings);
|
||||
}
|
||||
|
||||
if (ModelLoadingConstants.isResourceModelId(id) || id.equals(class_10096.field_53661)) {
|
||||
// Call the baker instead of the operation to ensure the baked model is cached and doesn't end up going
|
||||
// through events twice.
|
||||
// This ignores the UnbakedModel in modelsToBake but it should be the same as the one in unbakedModels.
|
||||
// This ignores the UnbakedModel in field_53662 (top-level model map) but it should be the same as the one in field_53663 (resource model map).
|
||||
return baker.bake(id.id(), settings);
|
||||
}
|
||||
|
||||
|
@ -185,19 +77,4 @@ abstract class ModelLoaderMixin implements ModelLoaderHooks {
|
|||
public ModelLoadingEventDispatcher fabric_getDispatcher() {
|
||||
return fabric_eventDispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel fabric_getMissingModel() {
|
||||
return missingModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnbakedModel fabric_getOrLoadModel(Identifier id) {
|
||||
return getOrLoadModel(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fabric_add(ModelIdentifier id, UnbakedModel model) {
|
||||
add(id, model);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.model.loading;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.class_10097;
|
||||
import net.minecraft.client.render.model.BlockStatesLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingConstants;
|
||||
import net.fabricmc.fabric.impl.client.model.loading.ModelLoadingEventDispatcher;
|
||||
|
||||
@Mixin(class_10097.class)
|
||||
abstract class class_10097Mixin {
|
||||
@Unique
|
||||
@Nullable
|
||||
private ModelLoadingEventDispatcher fabric_eventDispatcher;
|
||||
|
||||
@Shadow
|
||||
abstract UnbakedModel method_62638(Identifier identifier);
|
||||
|
||||
@Shadow
|
||||
abstract void method_62635(ModelIdentifier modelIdentifier, UnbakedModel unbakedModel);
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void onReturnInit(CallbackInfo ci) {
|
||||
fabric_eventDispatcher = ModelLoadingEventDispatcher.CURRENT.get();
|
||||
}
|
||||
|
||||
@Inject(method = "method_62632", at = @At("RETURN"))
|
||||
private void onAddStandardModels(BlockStatesLoader.class_10095 blockStateModels, CallbackInfo ci) {
|
||||
if (fabric_eventDispatcher == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
fabric_eventDispatcher.addExtraModels(id -> {
|
||||
ModelIdentifier modelId = ModelLoadingConstants.toResourceModelId(id);
|
||||
UnbakedModel unbakedModel = method_62638(id);
|
||||
method_62635(modelId, unbakedModel);
|
||||
});
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "method_62640", at = @At(value = "STORE", ordinal = 0), ordinal = 0)
|
||||
@Nullable
|
||||
private UnbakedModel onLoadResourceModel(@Nullable UnbakedModel model, Identifier id) {
|
||||
if (fabric_eventDispatcher == null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
UnbakedModel resolvedModel = fabric_eventDispatcher.resolveModel(id);
|
||||
|
||||
if (resolvedModel != null) {
|
||||
model = resolvedModel;
|
||||
}
|
||||
|
||||
return fabric_eventDispatcher.modifyModelOnLoad(model, id, null);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "method_62635", at = @At("HEAD"), argsOnly = true)
|
||||
private UnbakedModel onAddTopLevelModel(UnbakedModel model, ModelIdentifier modelId) {
|
||||
if (fabric_eventDispatcher == null) {
|
||||
return model;
|
||||
}
|
||||
|
||||
if (ModelLoadingConstants.isResourceModelId(modelId)) {
|
||||
return model;
|
||||
}
|
||||
|
||||
return fabric_eventDispatcher.modifyModelOnLoad(model, null, modelId);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@
|
|||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"BakedModelManagerMixin",
|
||||
"BlockStatesLoaderMixin",
|
||||
"ModelLoaderMixin",
|
||||
"ModelLoaderBakerImplMixin"
|
||||
"class_10097Mixin",
|
||||
"ModelLoaderBakerImplMixin",
|
||||
"ModelLoaderMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -18,8 +18,6 @@ package net.fabricmc.fabric.test.model.loading;
|
|||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.client.render.entity.state.EntityRenderState;
|
||||
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Quaternionf;
|
||||
|
||||
|
@ -31,29 +29,27 @@ import net.minecraft.client.render.VertexConsumerProvider;
|
|||
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.render.entity.state.LivingEntityRenderState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class BakedModelFeatureRenderer<T extends EntityRenderState, M extends EntityModel<T>> extends FeatureRenderer<T, M> {
|
||||
public class BakedModelFeatureRenderer<S extends LivingEntityRenderState, M extends EntityModel<S>> extends FeatureRenderer<S, M> {
|
||||
private final Supplier<BakedModel> modelSupplier;
|
||||
|
||||
public BakedModelFeatureRenderer(FeatureRendererContext<T, M> context, Supplier<BakedModel> modelSupplier) {
|
||||
public BakedModelFeatureRenderer(FeatureRendererContext<S, M> context, Supplier<BakedModel> modelSupplier) {
|
||||
super(context);
|
||||
this.modelSupplier = modelSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T state, float limbAngle, float limbDistance) {
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, S state, float limbAngle, float limbDistance) {
|
||||
BakedModel model = modelSupplier.get();
|
||||
VertexConsumer vertices = vertexConsumers.getBuffer(TexturedRenderLayers.getEntityCutout());
|
||||
matrices.push();
|
||||
//matrices.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(headYaw));
|
||||
//matrices.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(headPitch));
|
||||
// TODO 24w33a Is limbAngle the same as animationProgress? Probably not
|
||||
matrices.multiply(new Quaternionf(new AxisAngle4f(limbAngle * 0.07F, 0, 1, 0)));
|
||||
matrices.multiply(new Quaternionf(new AxisAngle4f(state.age * 0.07F - state.bodyYaw * MathHelper.RADIANS_PER_DEGREE, 0, 1, 0)));
|
||||
matrices.scale(-0.75F, -0.75F, 0.75F);
|
||||
float aboveHead = (float) (Math.sin(limbAngle * 0.08F)) * 0.5F + 0.5F;
|
||||
float aboveHead = (float) (Math.sin(state.age * 0.08F)) * 0.5F + 0.5F;
|
||||
matrices.translate(-0.5F, 0.75F + aboveHead, -0.5F);
|
||||
MinecraftClient.getInstance().getBlockRenderManager().getModelRenderer().render(matrices.peek(), vertices, null, model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV);
|
||||
matrices.pop();
|
||||
|
|
|
@ -26,7 +26,6 @@ import net.minecraft.class_10096;
|
|||
import net.minecraft.client.render.block.BlockModels;
|
||||
import net.minecraft.client.render.entity.PlayerEntityRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
|
@ -69,6 +68,7 @@ public class ModelTestModClient implements ClientModInitializer {
|
|||
public void onInitializeClient() {
|
||||
ModelLoadingPlugin.register(pluginContext -> {
|
||||
pluginContext.addModels(HALF_RED_SAND_MODEL_ID);
|
||||
|
||||
// remove bottom face of gold blocks
|
||||
pluginContext.modifyModelAfterBake().register(ModelModifier.WRAP_PHASE, (model, context) -> {
|
||||
Identifier id = context.resourceId();
|
||||
|
@ -79,17 +79,19 @@ public class ModelTestModClient implements ClientModInitializer {
|
|||
|
||||
return model;
|
||||
});
|
||||
|
||||
// make fences with west: true and everything else false appear to be a missing model visually
|
||||
ModelIdentifier fenceId = BlockModels.getModelId(Blocks.OAK_FENCE.getDefaultState().with(HorizontalConnectingBlock.WEST, true));
|
||||
pluginContext.modifyModelOnLoad().register(ModelModifier.OVERRIDE_PHASE, (model, context) -> {
|
||||
ModelIdentifier id = context.topLevelId();
|
||||
|
||||
if (id != null && id.equals(fenceId)) {
|
||||
return context.getOrLoadModel(class_10096.field_53660);
|
||||
return new DelegatingUnbakedModel(class_10096.field_53660);
|
||||
}
|
||||
|
||||
return model;
|
||||
});
|
||||
|
||||
// make brown glazed terracotta appear to be a missing model visually, but without affecting the item, by using pre-bake
|
||||
// using load here would make the item also appear missing
|
||||
pluginContext.modifyModelBeforeBake().register(ModelModifier.OVERRIDE_PHASE, (model, context) -> {
|
||||
|
@ -109,14 +111,15 @@ public class ModelTestModClient implements ClientModInitializer {
|
|||
// All the block state models are top-level...
|
||||
// Use a delegating unbaked model to make sure the identical models only get baked a single time.
|
||||
Identifier wheatStage0Id = Identifier.ofVanilla("block/wheat_stage0");
|
||||
|
||||
UnbakedModel stage0Model = new DelegatingUnbakedModel(wheatStage0Id);
|
||||
Identifier wheatStage7Id = Identifier.ofVanilla("block/wheat_stage7");
|
||||
UnbakedModel wheatStage0Model = new DelegatingUnbakedModel(wheatStage0Id);
|
||||
UnbakedModel wheatStage7Model = new DelegatingUnbakedModel(wheatStage7Id);
|
||||
|
||||
for (int age = 0; age <= 6; age++) {
|
||||
context.setModel(state.with(CropBlock.AGE, age), stage0Model);
|
||||
context.setModel(state.with(CropBlock.AGE, age), wheatStage0Model);
|
||||
}
|
||||
|
||||
context.setModel(state.with(CropBlock.AGE, 7), context.getOrLoadModel(Identifier.ofVanilla("block/wheat_stage7")));
|
||||
context.setModel(state.with(CropBlock.AGE, 7), wheatStage7Model);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.model.loading;
|
||||
|
||||
import static net.fabricmc.fabric.test.model.loading.ModelTestModClient.id;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraft.class_10096;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
|
||||
/**
|
||||
* Tests that deep model resolution resolve each model a single time, depth-first.
|
||||
*/
|
||||
public class NestedModelLoadingTest implements ClientModInitializer {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private static final Identifier BASE_MODEL = id("nested_base");
|
||||
private static final Identifier NESTED_MODEL_1 = id("nested_1");
|
||||
private static final Identifier NESTED_MODEL_2 = id("nested_2");
|
||||
private static final Identifier NESTED_MODEL_3 = id("nested_3");
|
||||
private static final Identifier NESTED_MODEL_4 = id("nested_4");
|
||||
private static final Identifier NESTED_MODEL_5 = id("nested_5");
|
||||
private static final Identifier TARGET_MODEL = Identifier.ofVanilla("block/stone");
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ModelLoadingPlugin.register(pluginContext -> {
|
||||
pluginContext.addModels(BASE_MODEL);
|
||||
|
||||
pluginContext.resolveModel().register(context -> {
|
||||
Identifier id = context.id();
|
||||
UnbakedModel ret = null;
|
||||
|
||||
if (id.equals(BASE_MODEL)) {
|
||||
LOGGER.info("Nested model 1 started loading");
|
||||
ret = context.getOrLoadModel(NESTED_MODEL_1);
|
||||
LOGGER.info("Nested model 1 finished loading");
|
||||
} else if (id.equals(NESTED_MODEL_1)) {
|
||||
LOGGER.info(" Nested model 2 started loading");
|
||||
ret = context.getOrLoadModel(NESTED_MODEL_2);
|
||||
LOGGER.info(" Nested model 2 finished loading");
|
||||
} else if (id.equals(NESTED_MODEL_2)) {
|
||||
LOGGER.info(" Nested model 3 started loading");
|
||||
ret = context.getOrLoadModel(NESTED_MODEL_3);
|
||||
LOGGER.info(" Nested model 3 finished loading");
|
||||
} else if (id.equals(NESTED_MODEL_3)) {
|
||||
// Will be overridden by the model modifier below anyway.
|
||||
LOGGER.info(" Returning dummy model for nested model 3");
|
||||
ret = context.getOrLoadModel(class_10096.field_53660);
|
||||
} else if (id.equals(NESTED_MODEL_4)) {
|
||||
// Will be overridden by the model modifier below anyway.
|
||||
LOGGER.info(" Returning dummy model for nested model 4");
|
||||
ret = context.getOrLoadModel(class_10096.field_53660);
|
||||
} else if (id.equals(NESTED_MODEL_5)) {
|
||||
LOGGER.info(" Target model started loading");
|
||||
ret = context.getOrLoadModel(TARGET_MODEL);
|
||||
LOGGER.info(" Target model finished loading");
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
pluginContext.modifyModelOnLoad().register((model, context) -> {
|
||||
UnbakedModel ret = model;
|
||||
Identifier id = context.resourceId();
|
||||
|
||||
if (id != null) {
|
||||
if (id.equals(NESTED_MODEL_3)) {
|
||||
LOGGER.info(" Nested model 4 started loading");
|
||||
ret = context.getOrLoadModel(NESTED_MODEL_4);
|
||||
LOGGER.info(" Nested model 4 finished loading");
|
||||
|
||||
if (!id.equals(context.resourceId())) {
|
||||
throw new AssertionError("Context object should not have changed.");
|
||||
}
|
||||
} else if (id.equals(NESTED_MODEL_4)) {
|
||||
LOGGER.info(" Nested model 5 started loading");
|
||||
ret = context.getOrLoadModel(NESTED_MODEL_5);
|
||||
LOGGER.info(" Nested model 5 finished loading");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@
|
|||
"entrypoints": {
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.model.loading.ModelTestModClient",
|
||||
"net.fabricmc.fabric.test.model.loading.NestedModelLoadingTest",
|
||||
"net.fabricmc.fabric.test.model.loading.PreparablePluginTest"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1.mesh;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector2fc;
|
||||
|
@ -292,14 +293,39 @@ public interface MutableQuadView extends QuadView {
|
|||
/**
|
||||
* Enables bulk vertex data transfer using the standard Minecraft quad format.
|
||||
*
|
||||
* <p>The material applied to this quad view might be slightly different from the {@code material} parameter regarding diffuse shading.
|
||||
* If either the baked quad {@link BakedQuad#hasShade() does not have shade} or the material {@link MaterialFinder#disableDiffuse(boolean) does not have shade},
|
||||
* diffuse shading will be disabled for this quad view.
|
||||
* This is reflected in the quad view's {@link #material()}, but the {@code material} parameter is unchanged (it is immutable anyway).
|
||||
*
|
||||
* <p>The {@linkplain BakedQuad#getLightEmission() baked quad's light emission} will be applied to the lightmap values from the vertex data
|
||||
* after copying.
|
||||
*
|
||||
* <p>Calling this method does not emit the quad.
|
||||
*/
|
||||
default MutableQuadView fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace) {
|
||||
return fromVanilla(quad, material, cullFace, true);
|
||||
}
|
||||
|
||||
// TODO: If this is unmarked as experimental, update the javadoc of the other overloads.
|
||||
/**
|
||||
* Enables bulk vertex data transfer using the standard Minecraft quad format.
|
||||
*
|
||||
* <p>The material applied to this quad view might be slightly different from the {@code material} parameter regarding diffuse shading.
|
||||
* If either the baked quad {@link BakedQuad#hasShade() does not have shade} or the material {@link MaterialFinder#disableDiffuse(boolean) does not have shade},
|
||||
* diffuse shading will be disabled for this quad view.
|
||||
* This is reflected in the quad view's {@link #material()}, but the {@code material} parameter is unchanged (it is immutable anyway).
|
||||
*
|
||||
* <p>If {@code applyLightEmission} is {@code true}, the {@linkplain BakedQuad#getLightEmission() baked quad's light emission} will be applied
|
||||
* to the lightmap values from the vertex data after copying. Otherwise, the light emission will be ignored.
|
||||
*
|
||||
* <p>Calling this method does not emit the quad.
|
||||
*
|
||||
* @apiNote This method is marked as experimental because future snapshots may change the item renderer to also respect quad light emission,
|
||||
* in which case this method will be removed. See <a href="https://bugs.mojang.com/browse/MC-275296">MC-275296</a>.
|
||||
*/
|
||||
MutableQuadView fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace);
|
||||
@ApiStatus.Experimental
|
||||
MutableQuadView fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace, boolean applyLightEmission);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #color(int, int)} instead.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package net.fabricmc.fabric.api.renderer.v1.mesh;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector2fc;
|
||||
|
@ -138,7 +139,14 @@ public interface QuadEmitter extends MutableQuadView {
|
|||
QuadEmitter fromVanilla(int[] quadData, int startIndex);
|
||||
|
||||
@Override
|
||||
QuadEmitter fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace);
|
||||
default QuadEmitter fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace) {
|
||||
MutableQuadView.super.fromVanilla(quad, material, cullFace);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Override
|
||||
QuadEmitter fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace, boolean applyLightEmission);
|
||||
|
||||
/**
|
||||
* Tolerance for determining if the depth parameter to {@link #square(Direction, float, float, float, float, float)}
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
|
@ -200,7 +201,23 @@ public interface QuadView {
|
|||
// Mimic material properties to the largest possible extent
|
||||
int outputColorIndex = material().disableColorIndex() ? -1 : colorIndex();
|
||||
boolean outputShade = !material().disableDiffuse();
|
||||
return new BakedQuad(vertexData, outputColorIndex, lightFace(), sprite, outputShade, 0 /* TODO 24w33a */);
|
||||
// The output light emission is equal to the minimum of all four sky light values and all four block light values.
|
||||
int outputLightEmission = 15;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int lightmap = lightmap(i);
|
||||
|
||||
if (lightmap == 0) {
|
||||
outputLightEmission = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
int blockLight = LightmapTextureManager.getBlockLightCoordinates(lightmap);
|
||||
int skyLight = LightmapTextureManager.getSkyLightCoordinates(lightmap);
|
||||
outputLightEmission = Math.min(outputLightEmission, Math.min(blockLight, skyLight));
|
||||
}
|
||||
|
||||
return new BakedQuad(vertexData, outputColorIndex, lightFace(), sprite, outputShade, outputLightEmission);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ public class VanillaModelEncoder {
|
|||
|
||||
for (int j = 0; j < count; j++) {
|
||||
final BakedQuad q = quads.get(j);
|
||||
emitter.fromVanilla(q, STANDARD_MATERIAL, cullFace);
|
||||
emitter.fromVanilla(q, STANDARD_MATERIAL, cullFace, false);
|
||||
emitter.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.renderer;
|
||||
|
||||
import net.minecraft.util.ActionResult;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
|
@ -27,6 +25,7 @@ import net.minecraft.block.Blocks;
|
|||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
@ -46,8 +45,6 @@ public class FrameBlock extends Block implements BlockEntityProvider, FabricBloc
|
|||
@Override
|
||||
public ActionResult onUseWithItem(ItemStack stack, BlockState blockState, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) {
|
||||
if (world.getBlockEntity(pos) instanceof FrameBlockEntity frame) {
|
||||
Block handBlock = Block.getBlockFromItem(stack.getItem());
|
||||
|
||||
@Nullable
|
||||
Block currentBlock = frame.getBlock();
|
||||
|
||||
|
@ -59,26 +56,31 @@ public class FrameBlock extends Block implements BlockEntityProvider, FabricBloc
|
|||
frame.setBlock(null);
|
||||
}
|
||||
|
||||
if (world.isClient)
|
||||
return ActionResult.SUCCESS;
|
||||
return ActionResult.SUCCESS;
|
||||
} else {
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
return ActionResult.PASS_TO_DEFAULT_BLOCK_ACTION;
|
||||
}
|
||||
|
||||
Block handBlock = Block.getBlockFromItem(stack.getItem());
|
||||
|
||||
// getBlockFromItem will return air if we do not have a block item in hand
|
||||
if (handBlock == Blocks.AIR) {
|
||||
return ActionResult.FAIL;
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
// Do not allow blocks that may have a block entity
|
||||
if (handBlock instanceof BlockEntityProvider) {
|
||||
return ActionResult.FAIL;
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
stack.decrement(1);
|
||||
if (currentBlock == handBlock) {
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
if (!world.isClient()) {
|
||||
stack.decrementUnlessCreative(1, player);
|
||||
|
||||
if (currentBlock != null) {
|
||||
player.getInventory().offerOrDrop(new ItemStack(currentBlock));
|
||||
}
|
||||
|
@ -86,11 +88,10 @@ public class FrameBlock extends Block implements BlockEntityProvider, FabricBloc
|
|||
frame.setBlock(handBlock);
|
||||
}
|
||||
|
||||
if (world.isClient())
|
||||
return ActionResult.SUCCESS;
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
return ActionResult.FAIL;
|
||||
return ActionResult.PASS_TO_DEFAULT_BLOCK_ACTION;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.renderer.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -41,19 +39,8 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
|||
public class FrameUnbakedModel implements UnbakedModel {
|
||||
private static final SpriteIdentifier OBSIDIAN_SPRITE_ID = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, Identifier.ofVanilla("block/obsidian"));
|
||||
|
||||
// TODO 24w33a
|
||||
/*@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void method_62326(class_10103 arg, class_10102 arg2) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.renderer.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -56,19 +54,8 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
|
|||
this.shadeMode = shadeMode;
|
||||
}
|
||||
|
||||
// TODO 24w33a
|
||||
/*@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void method_62326(class_10103 arg, class_10102 arg2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.renderer.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -31,7 +29,6 @@ import net.minecraft.client.render.model.UnbakedModel;
|
|||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.test.renderer.RendererTest;
|
||||
|
||||
|
@ -40,19 +37,8 @@ public class PillarUnbakedModel implements UnbakedModel {
|
|||
.map(suffix -> new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, RendererTest.id("block/pillar_" + suffix)))
|
||||
.toList();
|
||||
|
||||
// TODO 24w33a
|
||||
/*@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void method_62326(class_10103 arg, class_10102 arg2) {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.renderer.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -34,21 +32,10 @@ public class RiverstoneUnbakedModel implements UnbakedModel {
|
|||
private static final Identifier STONE_MODEL_ID = Identifier.ofVanilla("block/stone");
|
||||
private static final Identifier GOLD_BLOCK_MODEL_ID = Identifier.ofVanilla("block/gold_block");
|
||||
|
||||
// TODO 24w33a
|
||||
/*@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
|
||||
modelLoader.apply(STONE_MODEL_ID).setParents(modelLoader);
|
||||
modelLoader.apply(GOLD_BLOCK_MODEL_ID).setParents(modelLoader);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void method_62326(class_10103 arg, class_10102 arg2) {
|
||||
|
||||
arg.method_62642(STONE_MODEL_ID);
|
||||
arg.method_62642(GOLD_BLOCK_MODEL_ID);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
@ -25,7 +25,6 @@ import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.material.MaterialFinderImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshBuilderImpl;
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,6 +36,7 @@ import net.minecraft.block.BlockState;
|
|||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.render.block.BlockModelRenderer;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
@ -147,17 +148,18 @@ public abstract class AoCalculator {
|
|||
// Because this instance is effectively thread-local, we preserve instances
|
||||
// to avoid making a new allocation each call.
|
||||
private final float[] vanillaAoData = new float[Direction.values().length * 2];
|
||||
private final BitSet vanillaAoControlBits = new BitSet(3);
|
||||
private final BitSet vanillaAoFlags = new BitSet(3);
|
||||
private final int[] vertexData = new int[EncodingFormat.QUAD_STRIDE];
|
||||
private final DummyBakedQuad dummyBakedQuad = new DummyBakedQuad();
|
||||
|
||||
private void calcVanilla(QuadViewImpl quad, float[] aoDest, int[] lightDest) {
|
||||
vanillaAoControlBits.clear();
|
||||
vanillaAoFlags.clear();
|
||||
final Direction lightFace = quad.lightFace();
|
||||
quad.toVanilla(vertexData, 0);
|
||||
dummyBakedQuad.prepare(quad.lightFace(), quad.hasShade());
|
||||
|
||||
VanillaAoHelper.updateShape(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vertexData, lightFace, vanillaAoData, vanillaAoControlBits);
|
||||
// TODO 24w33a
|
||||
//vanillaCalc.apply(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, lightFace, vanillaAoData, vanillaAoControlBits, quad.hasShade());
|
||||
VanillaAoHelper.getQuadDimensions(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vertexData, lightFace, vanillaAoData, vanillaAoFlags);
|
||||
vanillaCalc.apply(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vanillaAoData, vanillaAoFlags, dummyBakedQuad);
|
||||
|
||||
System.arraycopy(vanillaCalc.brightness, 0, aoDest, 0, 4);
|
||||
System.arraycopy(vanillaCalc.light, 0, lightDest, 0, 4);
|
||||
|
@ -582,4 +584,31 @@ public abstract class AoCalculator {
|
|||
if (b == 0) return a;
|
||||
return Math.min(a, b);
|
||||
}
|
||||
|
||||
// This quad is passed to the vanilla AO calc. It only calls getFace, isEmissive, getLightEmission, and hasShade.
|
||||
// Since Indigo already applies vanilla's light emission value in MutableQuadView#fromVanilla, this quad should not
|
||||
// provide its own light emission.
|
||||
private static class DummyBakedQuad extends BakedQuad {
|
||||
private Direction lightFace;
|
||||
private boolean shade;
|
||||
|
||||
DummyBakedQuad() {
|
||||
super(new int[32], -1, Direction.UP, null, true, 0);
|
||||
}
|
||||
|
||||
public void prepare(Direction lightFace, boolean shade) {
|
||||
this.lightFace = lightFace;
|
||||
this.shade = shade;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getFace() {
|
||||
return lightFace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasShade() {
|
||||
return shade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,21 +19,18 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.aocalc;
|
|||
import java.util.BitSet;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.block.BlockModelRenderer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
public class VanillaAoHelper {
|
||||
public final class VanillaAoHelper {
|
||||
// Renderer method we call isn't declared as static, but uses no
|
||||
// instance data and is called from multiple threads in vanilla also.
|
||||
private static BlockModelRenderer blockRenderer;
|
||||
private static BlockModelRenderer BLOCK_RENDERER = MinecraftClient.getInstance().getBlockRenderManager().getModelRenderer();
|
||||
|
||||
public static void initialize(BlockModelRenderer instance) {
|
||||
blockRenderer = instance;
|
||||
}
|
||||
|
||||
public static void updateShape(BlockRenderView blockRenderView, BlockState blockState, BlockPos pos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits) {
|
||||
blockRenderer.getQuadDimensions(blockRenderView, blockState, pos, vertexData, face, aoData, controlBits);
|
||||
public static void getQuadDimensions(BlockRenderView blockRenderView, BlockState blockState, BlockPos pos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits) {
|
||||
BLOCK_RENDERER.getQuadDimensions(blockRenderView, blockState, pos, vertexData, face, aoData, controlBits);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
@ -194,7 +195,7 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
}
|
||||
|
||||
@Override
|
||||
public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace) {
|
||||
public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace, boolean applyLightEmission) {
|
||||
fromVanilla(quad.getVertexData(), 0);
|
||||
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(0, cullFace);
|
||||
nominalFace(quad.getFace());
|
||||
|
@ -204,6 +205,14 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
material = RenderMaterialImpl.setDisableDiffuse((RenderMaterialImpl) material, true);
|
||||
}
|
||||
|
||||
if (applyLightEmission && quad.isEmissive()) {
|
||||
int lightEmission = quad.getLightEmission();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lightmap(i, LightmapTextureManager.applyEmission(lightmap(i), lightEmission));
|
||||
}
|
||||
}
|
||||
|
||||
material(material);
|
||||
tag(0);
|
||||
return this;
|
||||
|
|
|
@ -111,9 +111,7 @@ public class BlockRenderInfo {
|
|||
if ((cullCompletionFlags & mask) == 0) {
|
||||
cullCompletionFlags |= mask;
|
||||
|
||||
// TODO 24w33a - Double check this
|
||||
//if (Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face))) {
|
||||
if (Block.shouldDrawSide(blockState, blockState, face)) {
|
||||
if (Block.shouldDrawSide(blockState, blockView.getBlockState(searchPos.set(blockPos, face)), face)) {
|
||||
cullResultFlags |= mask;
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -120,7 +120,7 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
return vanillaModelConsumer;
|
||||
}
|
||||
|
||||
public void renderModel(ItemStack itemStack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model) {
|
||||
public void renderModel(ItemStack itemStack, ModelTransformationMode transformMode, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model) {
|
||||
this.itemStack = itemStack;
|
||||
this.transformMode = transformMode;
|
||||
this.matrixStack = matrixStack;
|
||||
|
|
|
@ -31,7 +31,6 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.VanillaAoHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext;
|
||||
|
||||
@Mixin(BlockModelRenderer.class)
|
||||
|
@ -47,9 +46,4 @@ public abstract class BlockModelRendererMixin {
|
|||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "<init>*")
|
||||
private void onInit(CallbackInfo ci) {
|
||||
VanillaAoHelper.initialize((BlockModelRenderer) (Object) this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,10 @@ public abstract class ItemRendererMixin {
|
|||
@Unique
|
||||
private final ThreadLocal<ItemRenderContext> fabric_contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(colors));
|
||||
|
||||
// FIXME: Method is unmapped in Yarn
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;isBuiltin()Z"), method = "method_62476", cancellable = true)
|
||||
public void hook_renderItem(ItemStack stack, ModelTransformationMode transformMode, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, boolean invert, CallbackInfo ci) {
|
||||
@Inject(method = "method_62476", at = @At(value = "HEAD"), cancellable = true)
|
||||
public void hook_renderItem(ItemStack stack, ModelTransformationMode transformMode, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, boolean notInHand, CallbackInfo ci) {
|
||||
if (!model.isVanillaAdapter()) {
|
||||
fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model);
|
||||
matrixStack.pop();
|
||||
fabric_contexts.get().renderModel(stack, transformMode, matrixStack, vertexConsumerProvider, light, overlay, model);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,14 +38,14 @@ include 'fabric-key-binding-api-v1'
|
|||
include 'fabric-lifecycle-events-v1'
|
||||
include 'fabric-loot-api-v3'
|
||||
include 'fabric-message-api-v1'
|
||||
//include 'fabric-model-loading-api-v1'
|
||||
include 'fabric-model-loading-api-v1'
|
||||
include 'fabric-networking-api-v1'
|
||||
include 'fabric-object-builder-api-v1'
|
||||
include 'fabric-particles-v1'
|
||||
include 'fabric-recipe-api-v1'
|
||||
include 'fabric-registry-sync-v0'
|
||||
//include 'fabric-renderer-api-v1'
|
||||
//include 'fabric-renderer-indigo'
|
||||
include 'fabric-renderer-api-v1'
|
||||
include 'fabric-renderer-indigo'
|
||||
include 'fabric-rendering-fluids-v1'
|
||||
include 'fabric-rendering-v1'
|
||||
include 'fabric-resource-conditions-api-v1'
|
||||
|
|
Loading…
Add table
Reference in a new issue