From 2290500b81d2c4bd6af49159abc526b1cad357b0 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Thu, 24 Oct 2024 00:10:33 +0200 Subject: [PATCH] Add event for registering atlases for use with Material --- .../resources/model/ModelManager.java.patch | 8 ++- .../neoforge/client/ClientHooks.java | 7 +++ .../event/RegisterMaterialAtlasesEvent.java | 58 ++++++++++++++++++ .../debug/client/TextureAtlasTests.java | 60 +++++++++++++++++++ .../atlases/material_test.json | 8 +++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java create mode 100644 tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java create mode 100644 tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index 2795dad254..8896f83c2e 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/resources/model/ModelManager.java +++ b/net/minecraft/client/resources/model/ModelManager.java -@@ -71,13 +_,14 @@ +@@ -71,18 +_,20 @@ TextureAtlas.LOCATION_BLOCKS, ResourceLocation.withDefaultNamespace("blocks") ); @@ -16,6 +16,12 @@ public ModelManager(TextureManager p_119406_, BlockColors p_119407_, int p_119408_) { this.blockColors = p_119407_; + this.maxMipmapLevels = p_119408_; + this.blockModelShaper = new BlockModelShaper(this); ++ Map VANILLA_ATLASES = net.neoforged.neoforge.client.ClientHooks.gatherMaterialAtlases(ModelManager.VANILLA_ATLASES); + this.atlases = new AtlasSet(VANILLA_ATLASES, p_119406_); + } + @@ -102,6 +_,7 @@ public final CompletableFuture reload( PreparableReloadListener.PreparationBarrier p_249079_, ResourceManager p_251134_, Executor p_250550_, Executor p_249221_ diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 0132b8a863..844ac14261 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -154,6 +154,7 @@ import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import net.neoforged.neoforge.client.event.RegisterMaterialAtlasesEvent; import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; import net.neoforged.neoforge.client.event.RegisterShadersEvent; import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent; @@ -1094,4 +1095,10 @@ public static boolean isInTranslucentBlockOutlinePass(Level level, BlockPos pos, ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, OUTLINE_PASS_RANDOM, level.getModelData(pos)); return renderTypes.contains(RenderType.TRANSLUCENT) || renderTypes.contains(RenderType.TRIPWIRE); } + + public static Map gatherMaterialAtlases(Map vanillaAtlases) { + vanillaAtlases = new HashMap<>(vanillaAtlases); + ModLoader.postEvent(new RegisterMaterialAtlasesEvent(vanillaAtlases)); + return Map.copyOf(vanillaAtlases); + } } diff --git a/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java b/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java new file mode 100644 index 0000000000..9d6f6c68ce --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/RegisterMaterialAtlasesEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import java.util.Map; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.resources.TextureAtlasHolder; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.ModelManager; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.event.IModBusEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired for registering {@linkplain TextureAtlas texture atlases} that will be used with {@link Material} or + * other systems which retrieve the atlas via {@link Minecraft#getTextureAtlas(ResourceLocation)} or + * {@link ModelManager#getAtlas(ResourceLocation)}. + *

+ * If an atlas is registered via this event, then it must NOT be used through a {@link TextureAtlasHolder}. + *

+ * This event fires during startup when the {@link ModelManager} is constructed. + *

+ * This event is not {@linkplain ICancellableEvent cancellable}. + *

+ * This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}. + */ +public class RegisterMaterialAtlasesEvent extends Event implements IModBusEvent { + private final Map atlases; + + @ApiStatus.Internal + public RegisterMaterialAtlasesEvent(Map atlases) { + this.atlases = atlases; + } + + /** + * Register a texture atlas with the given name and info location + * + * @param atlasLocation The name of the texture atlas + * @param atlasInfoLocation The location of the atlas info JSON relative to the {@code atlases} directory + */ + public void register(ResourceLocation atlasLocation, ResourceLocation atlasInfoLocation) { + ResourceLocation oldAtlasInfoLoc = this.atlases.putIfAbsent(atlasLocation, atlasInfoLocation); + if (oldAtlasInfoLoc != null) { + throw new IllegalStateException(String.format( + "Duplicate registration of atlas: %s (old info: %s, new info: %s)", + atlasLocation, + oldAtlasInfoLoc, + atlasInfoLocation)); + } + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java new file mode 100644 index 0000000000..11caca9757 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/TextureAtlasTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.Material; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent; +import net.neoforged.neoforge.client.event.RegisterMaterialAtlasesEvent; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; + +@ForEachTest(side = Dist.CLIENT, groups = { "client.texture_atlas", "texture_atlas" }) +public class TextureAtlasTests { + @TestHolder(description = { "Tests that texture atlases intended for use with Material are correctly registered and loaded" }, enabledByDefault = true) + static void testMaterialAtlas(final DynamicTest test) { + String modId = test.createModId(); + ResourceLocation atlasLoc = ResourceLocation.fromNamespaceAndPath(modId, "textures/atlas/material_test.png"); + + test.framework().modEventBus().addListener(RegisterMaterialAtlasesEvent.class, event -> { + ResourceLocation infoLoc = ResourceLocation.fromNamespaceAndPath(modId, "material_test"); + event.register(atlasLoc, infoLoc); + }); + + test.framework().modEventBus().addListener(RegisterClientReloadListenersEvent.class, event -> { + event.registerReloadListener((ResourceManagerReloadListener) manager -> { + try { + Minecraft.getInstance().getModelManager().getAtlas(atlasLoc); + } catch (NullPointerException npe) { + test.fail("Atlas was not registered"); + return; + } catch (Throwable t) { + test.fail("Atlas lookup failed: " + t.getMessage()); + return; + } + + try { + Material material = new Material(atlasLoc, ResourceLocation.withDefaultNamespace("block/stone")); + TextureAtlasSprite sprite = material.sprite(); + if (sprite.contents().name().equals(MissingTextureAtlasSprite.getLocation())) { + test.fail("Expected sprite was not stitched"); + return; + } + } catch (Throwable t) { + test.fail("Sprite lookup via material failed: " + t.getMessage()); + } + + test.pass(); + }); + }); + } +} diff --git a/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json b/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json new file mode 100644 index 0000000000..fcad3a1669 --- /dev/null +++ b/tests/src/main/resources/assets/neotests_test_material_atlas/atlases/material_test.json @@ -0,0 +1,8 @@ +{ + "sources": [ + { + "type": "minecraft:single", + "resource": "minecraft:block/stone" + } + ] +}