Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.20.2] Add an event to register custom sprite source types #77

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--- a/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java
+++ b/net/minecraft/client/renderer/texture/atlas/SpriteResourceLoader.java
@@ -24,7 +_,7 @@
Logger LOGGER = LogUtils.getLogger();

static SpriteResourceLoader create(Collection<MetadataSectionSerializer<?>> p_296204_) {
- return (p_293680_, p_293681_) -> {
+ return (p_293680_, p_293681_, constructor) -> {
ResourceMetadata resourcemetadata;
try {
resourcemetadata = p_293681_.metadata().copySections(p_296204_);
@@ -45,7 +_,7 @@
.orElse(AnimationMetadataSection.EMPTY);
FrameSize framesize = animationmetadatasection.calculateFrameSize(nativeimage.getWidth(), nativeimage.getHeight());
if (Mth.isMultipleOf(nativeimage.getWidth(), framesize.width()) && Mth.isMultipleOf(nativeimage.getHeight(), framesize.height())) {
- return new SpriteContents(p_293680_, framesize, nativeimage, resourcemetadata);
+ return constructor.create(p_293680_, framesize, nativeimage, resourcemetadata);
} else {
LOGGER.error(
"Image {} size {},{} is not multiple of frame size {},{}",
@@ -62,5 +_,10 @@
}

@Nullable
- SpriteContents loadSprite(ResourceLocation p_295581_, Resource p_294329_);
+ default SpriteContents loadSprite(ResourceLocation p_295581_, Resource p_294329_) {
+ return loadSprite(p_295581_, p_294329_, SpriteContents::new);
+ }
+
+ @Nullable
+ SpriteContents loadSprite(ResourceLocation id, Resource resource, net.neoforged.neoforge.client.textures.SpriteContentConstructor constructor);
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- a/net/minecraft/client/renderer/texture/atlas/SpriteSources.java
+++ b/net/minecraft/client/renderer/texture/atlas/SpriteSources.java
@@ -16,7 +_,7 @@

@OnlyIn(Dist.CLIENT)
public class SpriteSources {
- private static final BiMap<ResourceLocation, SpriteSourceType> TYPES = HashBiMap.create();
+ private static final BiMap<ResourceLocation, SpriteSourceType> TYPES = net.neoforged.neoforge.client.ClientHooks.makeSpriteSourceTypesMap();
public static final SpriteSourceType SINGLE_FILE = register("single", SingleFile.CODEC);
public static final SpriteSourceType DIRECTORY = register("directory", DirectoryLister.CODEC);
public static final SpriteSourceType FILTER = register("filter", SourceFilter.CODEC);
16 changes: 16 additions & 0 deletions src/main/java/net/neoforged/neoforge/client/ClientHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package net.neoforged.neoforge.client;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window;
Expand Down Expand Up @@ -74,6 +76,7 @@
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.atlas.SpriteSourceType;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.Material;
Expand Down Expand Up @@ -145,6 +148,7 @@
import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent;
import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent;
import net.neoforged.neoforge.client.event.RegisterShadersEvent;
import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent;
import net.neoforged.neoforge.client.event.RenderArmEvent;
import net.neoforged.neoforge.client.event.RenderBlockScreenEffectEvent;
import net.neoforged.neoforge.client.event.RenderHandEvent;
Expand Down Expand Up @@ -1033,6 +1037,17 @@ public static void onCreativeModeTabBuildContents(CreativeModeTab tab, ResourceK
output.accept(entry.getKey(), entry.getValue());
}

private static final BiMap<ResourceLocation, SpriteSourceType> SPRITE_SOURCE_TYPES_MAP = HashBiMap.create();

public static BiMap<ResourceLocation, SpriteSourceType> makeSpriteSourceTypesMap() {
return SPRITE_SOURCE_TYPES_MAP;
}

@ApiStatus.Internal
public static void registerSpriteSourceTypes() {
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
ModLoader.get().postEvent(new RegisterSpriteSourceTypesEvent(SPRITE_SOURCE_TYPES_MAP));
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
}

// Make sure the below method is only ever called once (by forge).
private static boolean initializedClientHooks = false;

Expand All @@ -1045,6 +1060,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou
initializedClientHooks = true;

GameTestHooks.registerGametests();
registerSpriteSourceTypes();
ModLoader.get().postEvent(new RegisterClientReloadListenersEvent(resourceManager));
ModLoader.get().postEvent(new EntityRenderersEvent.RegisterLayerDefinitions());
ModLoader.get().postEvent(new EntityRenderersEvent.RegisterRenderers());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client.event;

import com.google.common.collect.BiMap;
import com.mojang.serialization.Codec;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
import net.minecraft.client.renderer.texture.atlas.SpriteSourceType;
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 net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext;
import org.jetbrains.annotations.ApiStatus.Internal;

/**
* Fired to allow mods to register their own {@linkplain SpriteSourceType}. This event is fired once during the construction of the {@link Minecraft} instance or before datagen when client datagen is enabled.
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>This event is not {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.</p>
*
* <p>This event is fired on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod-specific event bus},
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
*/
public class RegisterSpriteSourceTypesEvent extends Event implements IModBusEvent {
private final BiMap<ResourceLocation, SpriteSourceType> types;

@Internal
public RegisterSpriteSourceTypesEvent(BiMap<ResourceLocation, SpriteSourceType> types) {
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
this.types = types;
}

/**
* Registers the given {@link Codec} as SpriteSourceType under the given id.
*
* @param id The id to register the {@link SpriteSourceType} under
* @param codec The codec for the {@link SpriteSourceType} to register
*/
public SpriteSourceType register(ResourceLocation id, Codec<? extends SpriteSource> codec) {
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
if (this.types.containsKey(id)) {
throw new IllegalStateException("Duplicate sprite source type registration " + id);
}
SpriteSourceType sourceType = new SpriteSourceType(codec);
this.types.put(id, sourceType);
return sourceType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client.textures;

import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.client.resources.metadata.animation.FrameSize;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceMetadata;
import org.jetbrains.annotations.Nullable;

/**
* Functional interface representing the signature of the SpriteContents constructor
* but nullable to support skipping based on metadata.
*/
@FunctionalInterface
public interface SpriteContentConstructor {
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
@Nullable
SpriteContents create(ResourceLocation id, FrameSize frameSize, NativeImage nativeImage, ResourceMetadata resourceMetadata);
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.minecraft.server.Bootstrap;
import net.neoforged.fml.ModLoader;
import net.neoforged.fml.ModWorkManager;
import net.neoforged.neoforge.client.ClientHooks;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -47,6 +48,9 @@ public static void begin(final Set<String> mods, final Path path, final Collecti
// If we aren't generating data for forge, automatically add forge as an existing so mods can access forge's data
existingMods.add("neoforge");
}
if (clientGenerators) {
ClientHooks.registerSpriteSourceTypes();
}
existingFileHelper = new ExistingFileHelper(existingPacks, existingMods, structureValidator, assetIndex, assetsDir);
ModLoader.get().runEventGenerator(mc -> new GatherDataEvent(mc, dataGeneratorConfig.makeGenerator(p -> dataGeneratorConfig.isFlat() ? p : p.resolve(mc.getModId()),
dataGeneratorConfig.getMods().contains(mc.getModId())), dataGeneratorConfig, existingFileHelper));
Expand Down
9 changes: 8 additions & 1 deletion src/main/resources/META-INF/accesstransformer.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ public net.minecraft.client.renderer.entity.LivingEntityRenderer addLayer(Lnet/m
public net.minecraft.client.renderer.texture.SpriteContents byMipLevel # byMipLevel
default net.minecraft.client.renderer.texture.SpriteContents animatedTexture # animatedTexture
default net.minecraft.client.renderer.texture.SpriteContents getFrameCount()I # getFrameCount
public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations <init>(Ljava/util/List;Lnet/minecraft/resources/ResourceLocation;Ljava/util/Map;)V # constructor
public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations$PalettedSpriteSupplier
public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations$PalettedSpriteSupplier <init>(Lnet/minecraft/client/renderer/texture/atlas/sources/LazyLoadedImage;Ljava/util/function/Supplier;Lnet/minecraft/resources/ResourceLocation;)V # constructor
public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$Region
public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$Region <init>(Lnet/minecraft/resources/ResourceLocation;DDDD)V # constructor
public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInstance
public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInstance <init>(Lnet/minecraft/client/renderer/texture/atlas/sources/LazyLoadedImage;Lnet/minecraft/client/renderer/texture/atlas/sources/Unstitcher$Region;DD)V # constructor
public net.minecraft.client.resources.ClientPackSource createVanillaPackSource(Ljava/nio/file/Path;)Lnet/minecraft/server/packs/VanillaPackResources; # createVanillaPackSource
protected net.minecraft.client.resources.TextureAtlasHolder textureAtlas # textureAtlas
protected net.minecraft.client.resources.model.ModelBakery loadBlockModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/block/model/BlockModel; # loadBlockModel
Expand Down Expand Up @@ -510,4 +517,4 @@ public net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType <
protected net.minecraft.world.level.portal.PortalForcer level # level
public net.minecraft.world.level.storage.LevelResource <init>(Ljava/lang/String;)V # constructor
private-f net.minecraft.world.level.storage.loot.LootPool rolls # rolls
private-f net.minecraft.world.level.storage.loot.LootPool bonusRolls # bonusRolls
private-f net.minecraft.world.level.storage.loot.LootPool bonusRolls # bonusRolls
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.debug.client;

import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Optional;
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.client.renderer.texture.SpriteTicker;
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
import net.minecraft.client.renderer.texture.atlas.SpriteSourceType;
import net.minecraft.client.resources.metadata.animation.FrameSize;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceMetadata;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.client.event.RegisterSpriteSourceTypesEvent;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;

@Mod(CustomSpriteSourceTest.MOD_ID)
public class CustomSpriteSourceTest {
Minecraftschurli marked this conversation as resolved.
Show resolved Hide resolved
private static final boolean ENABLED = true;
static final String MOD_ID = "custom_sprite_source_test";
private static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MOD_ID);
private static final Holder<Item> TEST_ITEM = ITEMS.registerSimpleItem("test_item");

public CustomSpriteSourceTest() {
if (!ENABLED) return;
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
if (FMLLoader.getDist().isClient()) {
modEventBus.addListener(this::registerTextureAtlasSpriteLoaders);
}
ITEMS.register(modEventBus);
}

private static SpriteSourceType tasLoader;

private void registerTextureAtlasSpriteLoaders(RegisterSpriteSourceTypesEvent event) {
tasLoader = event.register(new ResourceLocation(MOD_ID, "custom_sprite_source"), CustomSpriteSource.CODEC);
}

private record CustomSpriteSource(ResourceLocation id) implements SpriteSource {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Codec<CustomSpriteSource> CODEC = RecordCodecBuilder.create(inst -> inst.group(
ResourceLocation.CODEC.fieldOf("id").forGetter(CustomSpriteSource::id)).apply(inst, CustomSpriteSource::new));

@Override
public void run(ResourceManager manager, Output output) {
ResourceLocation id = this.id();
ResourceLocation resourcelocation = TEXTURE_ID_CONVERTER.idToFile(id);
Optional<Resource> optional = manager.getResource(resourcelocation);
if (optional.isPresent()) {
output.add(id, spriteResourceLoader -> spriteResourceLoader.loadSprite(id, optional.get(), CustomSpriteContents::new));
} else {
LOGGER.warn("Missing sprite: {}", resourcelocation);
}
}

@Override
public SpriteSourceType type() {
return tasLoader;
}

static final class CustomSpriteContents extends SpriteContents {

public CustomSpriteContents(ResourceLocation name, FrameSize size, NativeImage image, ResourceMetadata metadata) {
super(name, size, image, metadata);
}

@Override
public @NotNull SpriteTicker createTicker() {
return new Ticker();
}

class Ticker implements SpriteTicker {
final RandomSource random = RandomSource.create();

@Override
public void tickAndUpload(int x, int y) {
CustomSpriteContents.this.byMipLevel[0].fillRect(0, 0, 16, 16, 0xFF000000 | random.nextInt(0xFFFFFF));
CustomSpriteContents.this.uploadFirstFrame(x, y);
}

@Override
public void close() {}
}
}
}
}
Loading
Loading