AtlasManager.java

net.minecraft.client.resources.model.sprite.AtlasManager

信息

  • 全限定名:net.minecraft.client.resources.model.sprite.AtlasManager
  • 类型:public class
  • 包:net.minecraft.client.resources.model.sprite
  • 源码路径:src/main/java/net/minecraft/client/resources/model/sprite/AtlasManager.java
  • 起始行号:L30
  • 实现:AutoCloseable, PreparableReloadListener, SpriteGetter
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L31
    • 说明:

      TODO

  • KNOWN_ATLASES

    • 类型: List<AtlasManager.AtlasConfig>
    • 修饰符: private static final
    • 源码定位: L32
    • 说明:

      TODO

  • PENDING_STITCH

    • 类型: PreparableReloadListener.StateKey<AtlasManager.PendingStitchResults>
    • 修饰符: public static final
    • 源码定位: L49
    • 说明:

      TODO

  • atlasByTexture

    • 类型: Map<Identifier,AtlasManager.AtlasEntry>
    • 修饰符: private final
    • 源码定位: L50
    • 说明:

      TODO

  • atlasById

    • 类型: Map<Identifier,AtlasManager.AtlasEntry>
    • 修饰符: private final
    • 源码定位: L51
    • 说明:

      TODO

  • spriteLookup

    • 类型: Map<SpriteId,TextureAtlasSprite>
    • 修饰符: private
    • 源码定位: L52
    • 说明:

      TODO

  • maxMipmapLevels

    • 类型: int
    • 修饰符: private
    • 源码定位: L53
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.resources.model.sprite.AtlasManager.AtlasConfig

    • 类型: record
    • 修饰符: public
    • 源码定位: L168
    • 说明:

      TODO

  • net.minecraft.client.resources.model.sprite.AtlasManager.AtlasEntry

    • 类型: record
    • 修饰符: private
    • 源码定位: L175
    • 说明:

      TODO

  • net.minecraft.client.resources.model.sprite.AtlasManager.PendingStitch

    • 类型: record
    • 修饰符: private
    • 源码定位: L190
    • 说明:

      TODO

  • net.minecraft.client.resources.model.sprite.AtlasManager.PendingStitchResults

    • 类型: class
    • 修饰符: public static
    • 源码定位: L199
    • 说明:

      TODO

构造器

public AtlasManager(TextureManager textureManager, int maxMipmapLevels) @ L55

  • 构造器名:AtlasManager
  • 源码定位:L55
  • 修饰符:public

参数:

  • textureManager: TextureManager
  • maxMipmapLevels: int

说明:

TODO

方法

下面的方法块按源码顺序生成。

public TextureAtlas getAtlasOrThrow(Identifier atlasId) @ L67

  • 方法名:getAtlasOrThrow
  • 源码定位:L67
  • 返回类型:TextureAtlas
  • 修饰符:public

参数:

  • atlasId: Identifier

说明:

TODO

public void forEach(BiConsumer<Identifier,TextureAtlas> output) @ L76

  • 方法名:forEach
  • 源码定位:L76
  • 返回类型:void
  • 修饰符:public

参数:

  • output: BiConsumer<Identifier,TextureAtlas>

说明:

TODO

public void updateMaxMipLevel(int maxMipmapLevels) @ L80

  • 方法名:updateMaxMipLevel
  • 源码定位:L80
  • 返回类型:void
  • 修饰符:public

参数:

  • maxMipmapLevels: int

说明:

TODO

public void close() @ L84

  • 方法名:close
  • 源码定位:L84
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public TextureAtlasSprite get(SpriteId sprite) @ L92

  • 方法名:get
  • 源码定位:L92
  • 返回类型:TextureAtlasSprite
  • 修饰符:public

参数:

  • sprite: SpriteId

说明:

TODO

public void prepareSharedState(PreparableReloadListener.SharedState currentReload) @ L108

  • 方法名:prepareSharedState
  • 源码定位:L108
  • 返回类型:void
  • 修饰符:public

参数:

  • currentReload: PreparableReloadListener.SharedState

说明:

TODO

public CompletableFuture<Void> reload(PreparableReloadListener.SharedState currentReload, Executor taskExecutor, PreparableReloadListener.PreparationBarrier preparationBarrier, Executor reloadExecutor) @ L124

  • 方法名:reload
  • 源码定位:L124
  • 返回类型:CompletableFuture
  • 修饰符:public

参数:

  • currentReload: PreparableReloadListener.SharedState
  • taskExecutor: Executor
  • preparationBarrier: PreparableReloadListener.PreparationBarrier
  • reloadExecutor: Executor

说明:

TODO

private void updateSpriteMaps(AtlasManager.PendingStitchResults pendingStitches) @ L146

  • 方法名:updateSpriteMaps
  • 源码定位:L146
  • 返回类型:void
  • 修饰符:private

参数:

  • pendingStitches: AtlasManager.PendingStitchResults

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class AtlasManager implements AutoCloseable, PreparableReloadListener, SpriteGetter {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final List<AtlasManager.AtlasConfig> KNOWN_ATLASES = List.of(
        new AtlasManager.AtlasConfig(Sheets.ARMOR_TRIMS_SHEET, AtlasIds.ARMOR_TRIMS, false),
        new AtlasManager.AtlasConfig(Sheets.BANNER_SHEET, AtlasIds.BANNER_PATTERNS, false),
        new AtlasManager.AtlasConfig(Sheets.BED_SHEET, AtlasIds.BEDS, false),
        new AtlasManager.AtlasConfig(TextureAtlas.LOCATION_BLOCKS, AtlasIds.BLOCKS, true),
        new AtlasManager.AtlasConfig(TextureAtlas.LOCATION_ITEMS, AtlasIds.ITEMS, false),
        new AtlasManager.AtlasConfig(Sheets.CHEST_SHEET, AtlasIds.CHESTS, false),
        new AtlasManager.AtlasConfig(Sheets.DECORATED_POT_SHEET, AtlasIds.DECORATED_POT, false),
        new AtlasManager.AtlasConfig(Sheets.GUI_SHEET, AtlasIds.GUI, false, Set.of(GuiMetadataSection.TYPE)),
        new AtlasManager.AtlasConfig(Sheets.MAP_DECORATIONS_SHEET, AtlasIds.MAP_DECORATIONS, false),
        new AtlasManager.AtlasConfig(Sheets.PAINTINGS_SHEET, AtlasIds.PAINTINGS, false),
        new AtlasManager.AtlasConfig(TextureAtlas.LOCATION_PARTICLES, AtlasIds.PARTICLES, false),
        new AtlasManager.AtlasConfig(Sheets.SHIELD_SHEET, AtlasIds.SHIELD_PATTERNS, false),
        new AtlasManager.AtlasConfig(Sheets.SHULKER_SHEET, AtlasIds.SHULKER_BOXES, false),
        new AtlasManager.AtlasConfig(Sheets.SIGN_SHEET, AtlasIds.SIGNS, false),
        new AtlasManager.AtlasConfig(Sheets.CELESTIAL_SHEET, AtlasIds.CELESTIALS, false)
    );
    public static final PreparableReloadListener.StateKey<AtlasManager.PendingStitchResults> PENDING_STITCH = new PreparableReloadListener.StateKey<>();
    private final Map<Identifier, AtlasManager.AtlasEntry> atlasByTexture = new HashMap<>();
    private final Map<Identifier, AtlasManager.AtlasEntry> atlasById = new HashMap<>();
    private Map<SpriteId, TextureAtlasSprite> spriteLookup = Map.of();
    private int maxMipmapLevels;
 
    public AtlasManager(TextureManager textureManager, int maxMipmapLevels) {
        for (AtlasManager.AtlasConfig info : KNOWN_ATLASES) {
            TextureAtlas atlasTexture = new TextureAtlas(info.textureId);
            textureManager.register(info.textureId, atlasTexture);
            AtlasManager.AtlasEntry atlasEntry = new AtlasManager.AtlasEntry(atlasTexture, info);
            this.atlasByTexture.put(info.textureId, atlasEntry);
            this.atlasById.put(info.definitionLocation, atlasEntry);
        }
 
        this.maxMipmapLevels = maxMipmapLevels;
    }
 
    public TextureAtlas getAtlasOrThrow(Identifier atlasId) {
        AtlasManager.AtlasEntry atlasEntry = this.atlasById.get(atlasId);
        if (atlasEntry == null) {
            throw new IllegalArgumentException("Invalid atlas id: " + atlasId);
        } else {
            return atlasEntry.atlas();
        }
    }
 
    public void forEach(BiConsumer<Identifier, TextureAtlas> output) {
        this.atlasById.forEach((atlasId, entry) -> output.accept(atlasId, entry.atlas));
    }
 
    public void updateMaxMipLevel(int maxMipmapLevels) {
        this.maxMipmapLevels = maxMipmapLevels;
    }
 
    @Override
    public void close() {
        this.spriteLookup = Map.of();
        this.atlasById.values().forEach(AtlasManager.AtlasEntry::close);
        this.atlasById.clear();
        this.atlasByTexture.clear();
    }
 
    @Override
    public TextureAtlasSprite get(SpriteId sprite) {
        TextureAtlasSprite result = this.spriteLookup.get(sprite);
        if (result != null) {
            return result;
        } else {
            Identifier atlasTextureId = sprite.atlasLocation();
            AtlasManager.AtlasEntry atlasEntry = this.atlasByTexture.get(atlasTextureId);
            if (atlasEntry == null) {
                throw new IllegalArgumentException("Invalid atlas texture id: " + atlasTextureId);
            } else {
                return atlasEntry.atlas().missingSprite();
            }
        }
    }
 
    @Override
    public void prepareSharedState(PreparableReloadListener.SharedState currentReload) {
        int atlasCount = this.atlasById.size();
        List<AtlasManager.PendingStitch> pendingStitches = new ArrayList<>(atlasCount);
        Map<Identifier, CompletableFuture<SpriteLoader.Preparations>> pendingStitchById = new HashMap<>(atlasCount);
        List<CompletableFuture<?>> readyForUploads = new ArrayList<>(atlasCount);
        this.atlasById.forEach((atlasId, atlasEntry) -> {
            CompletableFuture<SpriteLoader.Preparations> stitchingDone = new CompletableFuture<>();
            pendingStitchById.put(atlasId, stitchingDone);
            pendingStitches.add(new AtlasManager.PendingStitch(atlasEntry, stitchingDone));
            readyForUploads.add(stitchingDone.thenCompose(SpriteLoader.Preparations::readyForUpload));
        });
        CompletableFuture<?> allReadyForUploads = CompletableFuture.allOf(readyForUploads.toArray(CompletableFuture[]::new));
        currentReload.set(PENDING_STITCH, new AtlasManager.PendingStitchResults(pendingStitches, pendingStitchById, allReadyForUploads));
    }
 
    @Override
    public CompletableFuture<Void> reload(
        PreparableReloadListener.SharedState currentReload,
        Executor taskExecutor,
        PreparableReloadListener.PreparationBarrier preparationBarrier,
        Executor reloadExecutor
    ) {
        AtlasManager.PendingStitchResults pendingStitches = currentReload.get(PENDING_STITCH);
        ResourceManager resourceManager = currentReload.resourceManager();
        pendingStitches.pendingStitches
            .forEach(pending -> pending.entry.scheduleLoad(resourceManager, taskExecutor, this.maxMipmapLevels).whenComplete((value, throwable) -> {
                if (value != null) {
                    pending.preparations.complete(value);
                } else {
                    pending.preparations.completeExceptionally(throwable);
                }
            }));
        return pendingStitches.allReadyToUpload
            .thenCompose(preparationBarrier::wait)
            .thenAcceptAsync(unused -> this.updateSpriteMaps(pendingStitches), reloadExecutor);
    }
 
    private void updateSpriteMaps(AtlasManager.PendingStitchResults pendingStitches) {
        this.spriteLookup = pendingStitches.joinAndUpload();
        Map<Identifier, TextureAtlasSprite> globalSpriteLookup = new HashMap<>();
        this.spriteLookup
            .forEach(
                (id, sprite) -> {
                    if (!id.texture().equals(MissingTextureAtlasSprite.getLocation())) {
                        TextureAtlasSprite previous = globalSpriteLookup.putIfAbsent(id.texture(), sprite);
                        if (previous != null) {
                            LOGGER.warn(
                                "Duplicate sprite {} from atlas {}, already defined in atlas {}. This will be rejected in a future version",
                                id.texture(),
                                id.atlasLocation(),
                                previous.atlasLocation()
                            );
                        }
                    }
                }
            );
    }
 
    @OnlyIn(Dist.CLIENT)
    public record AtlasConfig(Identifier textureId, Identifier definitionLocation, boolean createMipmaps, Set<MetadataSectionType<?>> additionalMetadata) {
        public AtlasConfig(Identifier textureId, Identifier definitionLocation, boolean createMipmaps) {
            this(textureId, definitionLocation, createMipmaps, Set.of());
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record AtlasEntry(TextureAtlas atlas, AtlasManager.AtlasConfig config) implements AutoCloseable {
        @Override
        public void close() {
            this.atlas.clearTextureData();
        }
 
        private CompletableFuture<SpriteLoader.Preparations> scheduleLoad(ResourceManager resourceManager, Executor executor, int maxMipmapLevels) {
            return SpriteLoader.create(this.atlas)
                .loadAndStitch(
                    resourceManager, this.config.definitionLocation, this.config.createMipmaps ? maxMipmapLevels : 0, executor, this.config.additionalMetadata
                );
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record PendingStitch(AtlasManager.AtlasEntry entry, CompletableFuture<SpriteLoader.Preparations> preparations) {
        public void joinAndUpload(Map<SpriteId, TextureAtlasSprite> result) {
            SpriteLoader.Preparations preparations = this.preparations.join();
            this.entry.atlas.upload(preparations);
            preparations.regions().forEach((spriteId, spriteContents) -> result.put(new SpriteId(this.entry.config.textureId, spriteId), spriteContents));
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    public static class PendingStitchResults {
        private final List<AtlasManager.PendingStitch> pendingStitches;
        private final Map<Identifier, CompletableFuture<SpriteLoader.Preparations>> stitchFuturesById;
        private final CompletableFuture<?> allReadyToUpload;
 
        private PendingStitchResults(
            List<AtlasManager.PendingStitch> pendingStitches,
            Map<Identifier, CompletableFuture<SpriteLoader.Preparations>> stitchFuturesById,
            CompletableFuture<?> allReadyToUpload
        ) {
            this.pendingStitches = pendingStitches;
            this.stitchFuturesById = stitchFuturesById;
            this.allReadyToUpload = allReadyToUpload;
        }
 
        public Map<SpriteId, TextureAtlasSprite> joinAndUpload() {
            Map<SpriteId, TextureAtlasSprite> result = new HashMap<>();
            this.pendingStitches.forEach(pendingStitch -> pendingStitch.joinAndUpload(result));
            return result;
        }
 
        public CompletableFuture<SpriteLoader.Preparations> get(Identifier atlasId) {
            return Objects.requireNonNull(this.stitchFuturesById.get(atlasId));
        }
    }
}

引用的其他类