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;
}
}引用的其他类
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
TextureUtil.getDebugTexturePath(), TextureUtil.writeAsPNG()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
RenderSystem.getDevice(), RenderSystem.getSamplerCache()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
实现
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
MissingTextureAtlasSprite.getLocation()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
实现
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/返回值 - 关联成员:
Identifier.withDefaultNamespace()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.roundToward()
- 引用位置: