160 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.client.renderer.texture;
 | |
| 
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.util.Collection;
 | |
| import java.util.HashMap;
 | |
| import java.util.List;
 | |
| import java.util.Locale;
 | |
| import java.util.Map;
 | |
| import java.util.Objects;
 | |
| import java.util.Set;
 | |
| import java.util.concurrent.CompletableFuture;
 | |
| import java.util.concurrent.Executor;
 | |
| import java.util.function.Function;
 | |
| import java.util.function.Supplier;
 | |
| import java.util.stream.Collectors;
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.CrashReport;
 | |
| import net.minecraft.CrashReportCategory;
 | |
| import net.minecraft.ReportedException;
 | |
| import net.minecraft.Util;
 | |
| import net.minecraft.client.renderer.texture.atlas.SpriteResourceLoader;
 | |
| import net.minecraft.client.renderer.texture.atlas.SpriteSourceList;
 | |
| import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection;
 | |
| import net.minecraft.resources.ResourceLocation;
 | |
| import net.minecraft.server.packs.metadata.MetadataSectionType;
 | |
| import net.minecraft.server.packs.resources.ResourceManager;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.util.profiling.Profiler;
 | |
| import net.minecraft.util.profiling.Zone;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class SpriteLoader {
 | |
| 	public static final Set<MetadataSectionType<?>> DEFAULT_METADATA_SECTIONS = Set.of(AnimationMetadataSection.TYPE);
 | |
| 	private static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private final ResourceLocation location;
 | |
| 	private final int maxSupportedTextureSize;
 | |
| 	private final int minWidth;
 | |
| 	private final int minHeight;
 | |
| 
 | |
| 	public SpriteLoader(ResourceLocation location, int maxSupportedTextureSize, int minWidth, int minHeight) {
 | |
| 		this.location = location;
 | |
| 		this.maxSupportedTextureSize = maxSupportedTextureSize;
 | |
| 		this.minWidth = minWidth;
 | |
| 		this.minHeight = minHeight;
 | |
| 	}
 | |
| 
 | |
| 	public static SpriteLoader create(TextureAtlas atlas) {
 | |
| 		return new SpriteLoader(atlas.location(), atlas.maxSupportedTextureSize(), atlas.getWidth(), atlas.getHeight());
 | |
| 	}
 | |
| 
 | |
| 	public SpriteLoader.Preparations stitch(List<SpriteContents> contents, int mipLevel, Executor executor) {
 | |
| 		SpriteLoader.Preparations var17;
 | |
| 		try (Zone zone = Profiler.get().zone((Supplier<String>)(() -> "stitch " + this.location))) {
 | |
| 			int i = this.maxSupportedTextureSize;
 | |
| 			Stitcher<SpriteContents> stitcher = new Stitcher<>(i, i, mipLevel);
 | |
| 			int j = Integer.MAX_VALUE;
 | |
| 			int k = 1 << mipLevel;
 | |
| 
 | |
| 			for (SpriteContents spriteContents : contents) {
 | |
| 				j = Math.min(j, Math.min(spriteContents.width(), spriteContents.height()));
 | |
| 				int l = Math.min(Integer.lowestOneBit(spriteContents.width()), Integer.lowestOneBit(spriteContents.height()));
 | |
| 				if (l < k) {
 | |
| 					LOGGER.warn(
 | |
| 						"Texture {} with size {}x{} limits mip level from {} to {}",
 | |
| 						spriteContents.name(),
 | |
| 						spriteContents.width(),
 | |
| 						spriteContents.height(),
 | |
| 						Mth.log2(k),
 | |
| 						Mth.log2(l)
 | |
| 					);
 | |
| 					k = l;
 | |
| 				}
 | |
| 
 | |
| 				stitcher.registerSprite(spriteContents);
 | |
| 			}
 | |
| 
 | |
| 			int m = Math.min(j, k);
 | |
| 			int n = Mth.log2(m);
 | |
| 			int l;
 | |
| 			if (n < mipLevel) {
 | |
| 				LOGGER.warn("{}: dropping miplevel from {} to {}, because of minimum power of two: {}", this.location, mipLevel, n, m);
 | |
| 				l = n;
 | |
| 			} else {
 | |
| 				l = mipLevel;
 | |
| 			}
 | |
| 
 | |
| 			try {
 | |
| 				stitcher.stitch();
 | |
| 			} catch (StitcherException var19) {
 | |
| 				CrashReport crashReport = CrashReport.forThrowable(var19, "Stitching");
 | |
| 				CrashReportCategory crashReportCategory = crashReport.addCategory("Stitcher");
 | |
| 				crashReportCategory.setDetail(
 | |
| 					"Sprites",
 | |
| 					var19.getAllSprites()
 | |
| 						.stream()
 | |
| 						.map(entry -> String.format(Locale.ROOT, "%s[%dx%d]", entry.name(), entry.width(), entry.height()))
 | |
| 						.collect(Collectors.joining(","))
 | |
| 				);
 | |
| 				crashReportCategory.setDetail("Max Texture Size", i);
 | |
| 				throw new ReportedException(crashReport);
 | |
| 			}
 | |
| 
 | |
| 			int o = Math.max(stitcher.getWidth(), this.minWidth);
 | |
| 			int p = Math.max(stitcher.getHeight(), this.minHeight);
 | |
| 			Map<ResourceLocation, TextureAtlasSprite> map = this.getStitchedSprites(stitcher, o, p);
 | |
| 			TextureAtlasSprite textureAtlasSprite = (TextureAtlasSprite)map.get(MissingTextureAtlasSprite.getLocation());
 | |
| 			CompletableFuture<Void> completableFuture;
 | |
| 			if (l > 0) {
 | |
| 				completableFuture = CompletableFuture.runAsync(
 | |
| 					() -> map.values().forEach(textureAtlasSpritex -> textureAtlasSpritex.contents().increaseMipLevel(l)), executor
 | |
| 				);
 | |
| 			} else {
 | |
| 				completableFuture = CompletableFuture.completedFuture(null);
 | |
| 			}
 | |
| 
 | |
| 			var17 = new SpriteLoader.Preparations(o, p, l, textureAtlasSprite, map, completableFuture);
 | |
| 		}
 | |
| 
 | |
| 		return var17;
 | |
| 	}
 | |
| 
 | |
| 	public static CompletableFuture<List<SpriteContents>> runSpriteSuppliers(
 | |
| 		SpriteResourceLoader spriteResourceLoader, List<Function<SpriteResourceLoader, SpriteContents>> factories, Executor executor
 | |
| 	) {
 | |
| 		List<CompletableFuture<SpriteContents>> list = factories.stream()
 | |
| 			.map(function -> CompletableFuture.supplyAsync(() -> (SpriteContents)function.apply(spriteResourceLoader), executor))
 | |
| 			.toList();
 | |
| 		return Util.sequence(list).thenApply(listx -> listx.stream().filter(Objects::nonNull).toList());
 | |
| 	}
 | |
| 
 | |
| 	public CompletableFuture<SpriteLoader.Preparations> loadAndStitch(ResourceManager resourceManager, ResourceLocation location, int mipLevel, Executor executor) {
 | |
| 		return this.loadAndStitch(resourceManager, location, mipLevel, executor, DEFAULT_METADATA_SECTIONS);
 | |
| 	}
 | |
| 
 | |
| 	public CompletableFuture<SpriteLoader.Preparations> loadAndStitch(
 | |
| 		ResourceManager resourceManager, ResourceLocation location, int mipLevel, Executor executor, Collection<MetadataSectionType<?>> sectionSerializers
 | |
| 	) {
 | |
| 		SpriteResourceLoader spriteResourceLoader = SpriteResourceLoader.create(sectionSerializers);
 | |
| 		return CompletableFuture.supplyAsync(() -> SpriteSourceList.load(resourceManager, location).list(resourceManager), executor)
 | |
| 			.thenCompose(list -> runSpriteSuppliers(spriteResourceLoader, list, executor))
 | |
| 			.thenApply(list -> this.stitch(list, mipLevel, executor));
 | |
| 	}
 | |
| 
 | |
| 	private Map<ResourceLocation, TextureAtlasSprite> getStitchedSprites(Stitcher<SpriteContents> stitcher, int x, int y) {
 | |
| 		Map<ResourceLocation, TextureAtlasSprite> map = new HashMap();
 | |
| 		stitcher.gatherSprites((spriteContents, k, l) -> map.put(spriteContents.name(), new TextureAtlasSprite(this.location, spriteContents, x, y, k, l)));
 | |
| 		return map;
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public record Preparations(
 | |
| 		int width, int height, int mipLevel, TextureAtlasSprite missing, Map<ResourceLocation, TextureAtlasSprite> regions, CompletableFuture<Void> readyForUpload
 | |
| 	) {
 | |
| 		public CompletableFuture<SpriteLoader.Preparations> waitForUpload() {
 | |
| 			return this.readyForUpload.thenApply(void_ -> this);
 | |
| 		}
 | |
| 	}
 | |
| }
 |