TextureAtlas.java

net.minecraft.client.renderer.texture.TextureAtlas

信息

  • 全限定名:net.minecraft.client.renderer.texture.TextureAtlas
  • 类型:public class
  • 包:net.minecraft.client.renderer.texture
  • 源码路径:src/main/java/net/minecraft/client/renderer/texture/TextureAtlas.java
  • 起始行号:L37
  • 继承:AbstractTexture
  • 实现:TickableTexture, Dumpable
  • 职责:

    TODO

字段/常量

  • LOGGER

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

      TODO

  • LOCATION_BLOCKS

    • 类型: Identifier
    • 修饰符: public static final
    • 源码定位: L39
    • 说明:

      TODO

  • LOCATION_ITEMS

    • 类型: Identifier
    • 修饰符: public static final
    • 源码定位: L41
    • 说明:

      TODO

  • LOCATION_PARTICLES

    • 类型: Identifier
    • 修饰符: public static final
    • 源码定位: L43
    • 说明:

      TODO

  • sprites

    • 类型: List<TextureAtlasSprite>
    • 修饰符: private
    • 源码定位: L45
    • 说明:

      TODO

  • animatedTexturesStates

    • 类型: List<SpriteContents.AnimationState>
    • 修饰符: private
    • 源码定位: L46
    • 说明:

      TODO

  • texturesByName

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

      TODO

  • missingSprite

    • 类型: TextureAtlasSprite
    • 修饰符: private
    • 源码定位: L48
    • 说明:

      TODO

  • location

    • 类型: Identifier
    • 修饰符: private final
    • 源码定位: L49
    • 说明:

      TODO

  • maxSupportedTextureSize

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

      TODO

  • width

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

      TODO

  • height

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

      TODO

  • maxMipLevel

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

      TODO

  • mipLevelCount

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

      TODO

  • mipViews

    • 类型: GpuTextureView[]
    • 修饰符: private
    • 源码定位: L55
    • 说明:

      TODO

  • spriteUbos

    • 类型: GpuBuffer
    • 修饰符: private
    • 源码定位: L56
    • 说明:

      TODO

内部类/嵌套类型

构造器

public TextureAtlas(Identifier location) @ L58

  • 构造器名:TextureAtlas
  • 源码定位:L58
  • 修饰符:public

参数:

  • location: Identifier

说明:

TODO

方法

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

private void createTexture(int newWidth, int newHeight, int newMipLevel) @ L63

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

参数:

  • newWidth: int
  • newHeight: int
  • newMipLevel: int

说明:

TODO

public void upload(SpriteLoader.Preparations preparations) @ L80

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

参数:

  • preparations: SpriteLoader.Preparations

说明:

TODO

private void uploadInitialContents() @ L139

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

参数:

说明:

TODO

public void dumpContents(Identifier selfId, Path dir) @ L191

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

参数:

  • selfId: Identifier
  • dir: Path

说明:

TODO

private static void dumpSpriteNames(Path dir, String outputId, Map<Identifier,TextureAtlasSprite> regions) @ L198

  • 方法名:dumpSpriteNames
  • 源码定位:L198
  • 返回类型:void
  • 修饰符:private static

参数:

  • dir: Path
  • outputId: String
  • regions: Map<Identifier,TextureAtlasSprite>

说明:

TODO

public void cycleAnimationFrames() @ L221

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

参数:

说明:

TODO

private void uploadAnimationFrames() @ L231

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

参数:

说明:

TODO

public void tick() @ L247

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

参数:

说明:

TODO

public TextureAtlasSprite getSprite(Identifier location) @ L252

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

参数:

  • location: Identifier

说明:

TODO

public TextureAtlasSprite missingSprite() @ L261

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

参数:

说明:

TODO

public void clearTextureData() @ L265

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

参数:

说明:

TODO

public void close() @ L273

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

参数:

说明:

TODO

public Identifier location() @ L291

  • 方法名:location
  • 源码定位:L291
  • 返回类型:Identifier
  • 修饰符:public

参数:

说明:

TODO

public int maxSupportedTextureSize() @ L295

  • 方法名:maxSupportedTextureSize
  • 源码定位:L295
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

int getWidth() @ L299

  • 方法名:getWidth
  • 源码定位:L299
  • 返回类型:int
  • 修饰符:package-private

参数:

说明:

TODO

int getHeight() @ L303

  • 方法名:getHeight
  • 源码定位:L303
  • 返回类型:int
  • 修饰符:package-private

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class TextureAtlas extends AbstractTexture implements TickableTexture, Dumpable {
    private static final Logger LOGGER = LogUtils.getLogger();
    @Deprecated
    public static final Identifier LOCATION_BLOCKS = Identifier.withDefaultNamespace("textures/atlas/blocks.png");
    @Deprecated
    public static final Identifier LOCATION_ITEMS = Identifier.withDefaultNamespace("textures/atlas/items.png");
    @Deprecated
    public static final Identifier LOCATION_PARTICLES = Identifier.withDefaultNamespace("textures/atlas/particles.png");
    private List<TextureAtlasSprite> sprites = List.of();
    private List<SpriteContents.AnimationState> animatedTexturesStates = List.of();
    private Map<Identifier, TextureAtlasSprite> texturesByName = Map.of();
    private @Nullable TextureAtlasSprite missingSprite;
    private final Identifier location;
    private final int maxSupportedTextureSize;
    private int width;
    private int height;
    private int maxMipLevel;
    private int mipLevelCount;
    private GpuTextureView[] mipViews = new GpuTextureView[0];
    private @Nullable GpuBuffer spriteUbos;
 
    public TextureAtlas(Identifier location) {
        this.location = location;
        this.maxSupportedTextureSize = RenderSystem.getDevice().getMaxTextureSize();
    }
 
    private void createTexture(int newWidth, int newHeight, int newMipLevel) {
        LOGGER.info("Created: {}x{}x{} {}-atlas", newWidth, newHeight, newMipLevel, this.location);
        GpuDevice device = RenderSystem.getDevice();
        this.close();
        this.texture = device.createTexture(this.location::toString, 15, TextureFormat.RGBA8, newWidth, newHeight, 1, newMipLevel + 1);
        this.textureView = device.createTextureView(this.texture);
        this.width = newWidth;
        this.height = newHeight;
        this.maxMipLevel = newMipLevel;
        this.mipLevelCount = newMipLevel + 1;
        this.mipViews = new GpuTextureView[this.mipLevelCount];
 
        for (int level = 0; level <= this.maxMipLevel; level++) {
            this.mipViews[level] = device.createTextureView(this.texture, level, 1);
        }
    }
 
    public void upload(SpriteLoader.Preparations preparations) {
        this.createTexture(preparations.width(), preparations.height(), preparations.mipLevel());
        this.clearTextureData();
        this.sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST);
        this.texturesByName = Map.copyOf(preparations.regions());
        this.missingSprite = this.texturesByName.get(MissingTextureAtlasSprite.getLocation());
        if (this.missingSprite == null) {
            throw new IllegalStateException("Atlas '" + this.location + "' (" + this.texturesByName.size() + " sprites) has no missing texture sprite");
        } else {
            List<TextureAtlasSprite> sprites = new ArrayList<>();
            List<SpriteContents.AnimationState> animationStates = new ArrayList<>();
            int animatedSpriteCount = (int)preparations.regions().values().stream().filter(TextureAtlasSprite::isAnimated).count();
            int spriteUboSize = Mth.roundToward(SpriteContents.UBO_SIZE, RenderSystem.getDevice().getUniformOffsetAlignment());
            int uboBlockSize = spriteUboSize * this.mipLevelCount;
            ByteBuffer spriteUboBuffer = MemoryUtil.memAlloc(animatedSpriteCount * uboBlockSize);
            int animationIndex = 0;
 
            for (TextureAtlasSprite sprite : preparations.regions().values()) {
                if (sprite.isAnimated()) {
                    sprite.uploadSpriteUbo(spriteUboBuffer, animationIndex * uboBlockSize, this.maxMipLevel, this.width, this.height, spriteUboSize);
                    animationIndex++;
                }
            }
 
            GpuBuffer spriteUbos = animationIndex > 0
                ? RenderSystem.getDevice().createBuffer(() -> this.location + " sprite UBOs", 128, spriteUboBuffer)
                : null;
            animationIndex = 0;
 
            for (TextureAtlasSprite spritex : preparations.regions().values()) {
                sprites.add(spritex);
                if (spritex.isAnimated() && spriteUbos != null) {
                    SpriteContents.AnimationState animationState = spritex.createAnimationState(
                        spriteUbos.slice(animationIndex * uboBlockSize, uboBlockSize), spriteUboSize
                    );
                    animationIndex++;
                    if (animationState != null) {
                        animationStates.add(animationState);
                    }
                }
            }
 
            this.spriteUbos = spriteUbos;
            this.sprites = sprites;
            this.animatedTexturesStates = List.copyOf(animationStates);
            this.uploadInitialContents();
            if (SharedConstants.DEBUG_DUMP_TEXTURE_ATLAS) {
                Path dumpDir = TextureUtil.getDebugTexturePath();
 
                try {
                    Files.createDirectories(dumpDir);
                    this.dumpContents(this.location, dumpDir);
                } catch (Exception var13) {
                    LOGGER.warn("Failed to dump atlas contents to {}", dumpDir);
                }
            }
        }
    }
 
    private void uploadInitialContents() {
        GpuDevice device = RenderSystem.getDevice();
        int spriteUboSize = Mth.roundToward(SpriteContents.UBO_SIZE, RenderSystem.getDevice().getUniformOffsetAlignment());
        int uboBlockSize = spriteUboSize * this.mipLevelCount;
        GpuSampler sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST, true);
        List<TextureAtlasSprite> staticSprites = this.sprites.stream().filter(s -> !s.isAnimated()).toList();
        List<GpuTextureView[]> scratchTextures = new ArrayList<>();
        ByteBuffer buffer = MemoryUtil.memAlloc(staticSprites.size() * uboBlockSize);
 
        for (int i = 0; i < staticSprites.size(); i++) {
            TextureAtlasSprite sprite = staticSprites.get(i);
            sprite.uploadSpriteUbo(buffer, i * uboBlockSize, this.maxMipLevel, this.width, this.height, spriteUboSize);
            GpuTexture scratchTexture = device.createTexture(
                () -> sprite.contents().name().toString(), 5, TextureFormat.RGBA8, sprite.contents().width(), sprite.contents().height(), 1, this.mipLevelCount
            );
            GpuTextureView[] views = new GpuTextureView[this.mipLevelCount];
 
            for (int level = 0; level <= this.maxMipLevel; level++) {
                sprite.uploadFirstFrame(scratchTexture, level);
                views[level] = device.createTextureView(scratchTexture);
            }
 
            scratchTextures.add(views);
        }
 
        try (GpuBuffer ubo = device.createBuffer(() -> "SpriteAnimationInfo", 128, buffer)) {
            for (int level = 0; level < this.mipLevelCount; level++) {
                try (RenderPass renderPass = RenderSystem.getDevice()
                        .createCommandEncoder()
                        .createRenderPass(() -> "Animate " + this.location, this.mipViews[level], OptionalInt.empty())) {
                    renderPass.setPipeline(RenderPipelines.ANIMATE_SPRITE_BLIT);
 
                    for (int i = 0; i < staticSprites.size(); i++) {
                        renderPass.bindTexture("Sprite", scratchTextures.get(i)[level], sampler);
                        renderPass.setUniform("SpriteAnimationInfo", ubo.slice(i * uboBlockSize + level * spriteUboSize, SpriteContents.UBO_SIZE));
                        renderPass.draw(0, 6);
                    }
                }
            }
        }
 
        for (GpuTextureView[] views : scratchTextures) {
            for (GpuTextureView view : views) {
                view.close();
                view.texture().close();
            }
        }
 
        MemoryUtil.memFree(buffer);
        this.uploadAnimationFrames();
    }
 
    @Override
    public void dumpContents(Identifier selfId, Path dir) throws IOException {
        String outputId = selfId.toDebugFileName();
        TextureUtil.writeAsPNG(dir, outputId, this.getTexture(), this.maxMipLevel, argb -> argb);
        dumpSpriteNames(dir, outputId, this.texturesByName);
    }
 
    private static void dumpSpriteNames(Path dir, String outputId, Map<Identifier, TextureAtlasSprite> regions) {
        Path outputPath = dir.resolve(outputId + ".txt");
 
        try (Writer output = Files.newBufferedWriter(outputPath)) {
            for (Entry<Identifier, TextureAtlasSprite> e : regions.entrySet().stream().sorted(Entry.comparingByKey()).toList()) {
                TextureAtlasSprite value = e.getValue();
                output.write(
                    String.format(
                        Locale.ROOT,
                        "%s\tx=%d\ty=%d\tw=%d\th=%d%n",
                        e.getKey(),
                        value.getX(),
                        value.getY(),
                        value.contents().width(),
                        value.contents().height()
                    )
                );
            }
        } catch (IOException var10) {
            LOGGER.warn("Failed to write file {}", outputPath, var10);
        }
    }
 
    public void cycleAnimationFrames() {
        if (this.texture != null) {
            for (SpriteContents.AnimationState animationState : this.animatedTexturesStates) {
                animationState.tick();
            }
 
            this.uploadAnimationFrames();
        }
    }
 
    private void uploadAnimationFrames() {
        if (this.animatedTexturesStates.stream().anyMatch(SpriteContents.AnimationState::needsToDraw)) {
            for (int level = 0; level <= this.maxMipLevel; level++) {
                try (RenderPass renderPass = RenderSystem.getDevice()
                        .createCommandEncoder()
                        .createRenderPass(() -> "Animate " + this.location, this.mipViews[level], OptionalInt.empty())) {
                    for (SpriteContents.AnimationState animationState : this.animatedTexturesStates) {
                        if (animationState.needsToDraw()) {
                            animationState.drawToAtlas(renderPass, animationState.getDrawUbo(level));
                        }
                    }
                }
            }
        }
    }
 
    @Override
    public void tick() {
        this.cycleAnimationFrames();
    }
 
    public TextureAtlasSprite getSprite(Identifier location) {
        TextureAtlasSprite result = this.texturesByName.getOrDefault(location, this.missingSprite);
        if (result == null) {
            throw new IllegalStateException("Tried to lookup sprite, but atlas is not initialized");
        } else {
            return result;
        }
    }
 
    public TextureAtlasSprite missingSprite() {
        return Objects.requireNonNull(this.missingSprite, "Atlas not initialized");
    }
 
    public void clearTextureData() {
        this.sprites.forEach(TextureAtlasSprite::close);
        this.sprites = List.of();
        this.animatedTexturesStates = List.of();
        this.texturesByName = Map.of();
        this.missingSprite = null;
    }
 
    @Override
    public void close() {
        super.close();
 
        for (GpuTextureView view : this.mipViews) {
            view.close();
        }
 
        for (SpriteContents.AnimationState animationState : this.animatedTexturesStates) {
            animationState.close();
        }
 
        if (this.spriteUbos != null) {
            this.spriteUbos.close();
            this.spriteUbos = null;
        }
    }
 
    public Identifier location() {
        return this.location;
    }
 
    public int maxSupportedTextureSize() {
        return this.maxSupportedTextureSize;
    }
 
    int getWidth() {
        return this.width;
    }
 
    int getHeight() {
        return this.height;
    }
}

引用的其他类