SpriteContents.java
net.minecraft.client.renderer.texture.SpriteContents
信息
- 全限定名:net.minecraft.client.renderer.texture.SpriteContents
- 类型:public class
- 包:net.minecraft.client.renderer.texture
- 源码路径:src/main/java/net/minecraft/client/renderer/texture/SpriteContents.java
- 起始行号:L47
- 实现:AutoCloseable, Stitcher.Entry
- 职责:
TODO
字段/常量
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L48 - 说明:
TODO
- 类型:
-
UBO_SIZE- 类型:
int - 修饰符:
public static final - 源码定位:
L49 - 说明:
TODO
- 类型:
-
name- 类型:
Identifier - 修饰符:
private final - 源码定位:
L50 - 说明:
TODO
- 类型:
-
width- 类型:
int - 修饰符:
private final - 源码定位:
L51 - 说明:
TODO
- 类型:
-
height- 类型:
int - 修饰符:
private final - 源码定位:
L52 - 说明:
TODO
- 类型:
-
originalImage- 类型:
NativeImage - 修饰符:
private final - 源码定位:
L53 - 说明:
TODO
- 类型:
-
byMipLevel- 类型:
NativeImage[] - 修饰符:
private - 源码定位:
L54 - 说明:
TODO
- 类型:
-
animatedTexture- 类型:
SpriteContents.AnimatedTexture - 修饰符:
private final - 源码定位:
L55 - 说明:
TODO
- 类型:
-
additionalMetadata- 类型:
List<MetadataSectionType.WithValue<?>> - 修饰符:
private final - 源码定位:
L56 - 说明:
TODO
- 类型:
-
mipmapStrategy- 类型:
MipmapStrategy - 修饰符:
private final - 源码定位:
L57 - 说明:
TODO
- 类型:
-
alphaCutoffBias- 类型:
float - 修饰符:
private final - 源码定位:
L58 - 说明:
TODO
- 类型:
-
transparency- 类型:
Transparency - 修饰符:
private final - 源码定位:
L59 - 说明:
TODO
- 类型:
内部类/嵌套类型
-
net.minecraft.client.renderer.texture.SpriteContents.AnimatedTexture- 类型:
class - 修饰符:
private - 源码定位:
L263 - 说明:
TODO
- 类型:
-
net.minecraft.client.renderer.texture.SpriteContents.AnimationState- 类型:
class - 修饰符:
public - 源码定位:
L338 - 说明:
TODO
- 类型:
-
net.minecraft.client.renderer.texture.SpriteContents.FrameInfo- 类型:
record - 修饰符:
private - 源码定位:
L410 - 说明:
TODO
- 类型:
构造器
public SpriteContents(Identifier name, FrameSize frameSize, NativeImage image) @ L61
- 构造器名:SpriteContents
- 源码定位:L61
- 修饰符:public
参数:
- name: Identifier
- frameSize: FrameSize
- image: NativeImage
说明:
TODO
public SpriteContents(Identifier name, FrameSize frameSize, NativeImage image, Optional<AnimationMetadataSection> animationInfo, List<MetadataSectionType.WithValue<?>> additionalMetadata, Optional<TextureMetadataSection> textureInfo) @ L65
- 构造器名:SpriteContents
- 源码定位:L65
- 修饰符:public
参数:
- name: Identifier
- frameSize: FrameSize
- image: NativeImage
- animationInfo: Optional
- additionalMetadata: List<MetadataSectionType.WithValue<?>>
- textureInfo: Optional
说明:
TODO
方法
下面的方法块按源码顺序生成。
public void increaseMipLevel(int mipLevel) @ L88
- 方法名:increaseMipLevel
- 源码定位:L88
- 返回类型:void
- 修饰符:public
参数:
- mipLevel: int
说明:
TODO
private int getFrameCount() @ L105
- 方法名:getFrameCount
- 源码定位:L105
- 返回类型:int
- 修饰符:private
参数:
- 无
说明:
TODO
public boolean isAnimated() @ L109
- 方法名:isAnimated
- 源码定位:L109
- 返回类型:boolean
- 修饰符:public
参数:
- 无
说明:
TODO
public Transparency transparency() @ L113
- 方法名:transparency
- 源码定位:L113
- 返回类型:Transparency
- 修饰符:public
参数:
- 无
说明:
TODO
private SpriteContents.AnimatedTexture createAnimatedTexture(FrameSize frameSize, int fullWidth, int fullHeight, AnimationMetadataSection metadata) @ L117
- 方法名:createAnimatedTexture
- 源码定位:L117
- 返回类型:SpriteContents.AnimatedTexture
- 修饰符:private
参数:
- frameSize: FrameSize
- fullWidth: int
- fullHeight: int
- metadata: AnimationMetadataSection
说明:
TODO
public int width() @ L171
- 方法名:width
- 源码定位:L171
- 返回类型:int
- 修饰符:public
参数:
- 无
说明:
TODO
public int height() @ L176
- 方法名:height
- 源码定位:L176
- 返回类型:int
- 修饰符:public
参数:
- 无
说明:
TODO
public Identifier name() @ L181
- 方法名:name
- 源码定位:L181
- 返回类型:Identifier
- 修饰符:public
参数:
- 无
说明:
TODO
public IntList getUniqueFrames() @ L186
- 方法名:getUniqueFrames
- 源码定位:L186
- 返回类型:IntList
- 修饰符:public
参数:
- 无
说明:
TODO
public SpriteContents.AnimationState createAnimationState(GpuBufferSlice uboSlice, int spriteUboSize) @ L190
- 方法名:createAnimationState
- 源码定位:L190
- 返回类型:SpriteContents.AnimationState
- 修饰符:public
参数:
- uboSlice: GpuBufferSlice
- spriteUboSize: int
说明:
TODO
public <T> Optional<T> getAdditionalMetadata(MetadataSectionType<T> type) @ L194
- 方法名:getAdditionalMetadata
- 源码定位:L194
- 返回类型:
Optional - 修饰符:public
参数:
- type: MetadataSectionType
说明:
TODO
public void close() @ L205
- 方法名:close
- 源码定位:L205
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public String toString() @ L212
- 方法名:toString
- 源码定位:L212
- 返回类型:String
- 修饰符:public
参数:
- 无
说明:
TODO
public boolean isTransparent(int frame, int x, int y) @ L217
- 方法名:isTransparent
- 源码定位:L217
- 返回类型:boolean
- 修饰符:public
参数:
- frame: int
- x: int
- y: int
说明:
TODO
public Transparency computeTransparency(float u0, float v0, float u1, float v1) @ L228
- 方法名:computeTransparency
- 源码定位:L228
- 返回类型:Transparency
- 修饰符:public
参数:
- u0: float
- v0: float
- u1: float
- v1: float
说明:
TODO
public void uploadFirstFrame(GpuTexture destination, int level) @ L256
- 方法名:uploadFirstFrame
- 源码定位:L256
- 返回类型:void
- 修饰符:public
参数:
- destination: GpuTexture
- level: int
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class SpriteContents implements AutoCloseable, Stitcher.Entry {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int UBO_SIZE = new Std140SizeCalculator().putMat4f().putMat4f().putFloat().putFloat().putInt().get();
private final Identifier name;
private final int width;
private final int height;
private final NativeImage originalImage;
private NativeImage[] byMipLevel;
private final SpriteContents.@Nullable AnimatedTexture animatedTexture;
private final List<MetadataSectionType.WithValue<?>> additionalMetadata;
private final MipmapStrategy mipmapStrategy;
private final float alphaCutoffBias;
private final Transparency transparency;
public SpriteContents(Identifier name, FrameSize frameSize, NativeImage image) {
this(name, frameSize, image, Optional.empty(), List.of(), Optional.empty());
}
public SpriteContents(
Identifier name,
FrameSize frameSize,
NativeImage image,
Optional<AnimationMetadataSection> animationInfo,
List<MetadataSectionType.WithValue<?>> additionalMetadata,
Optional<TextureMetadataSection> textureInfo
) {
this.name = name;
this.width = frameSize.width();
this.height = frameSize.height();
this.additionalMetadata = additionalMetadata;
this.animatedTexture = animationInfo.<SpriteContents.AnimatedTexture>map(
animation -> this.createAnimatedTexture(frameSize, image.getWidth(), image.getHeight(), animation)
)
.orElse(null);
this.originalImage = image;
this.byMipLevel = new NativeImage[]{this.originalImage};
this.mipmapStrategy = textureInfo.map(TextureMetadataSection::mipmapStrategy).orElse(MipmapStrategy.AUTO);
this.alphaCutoffBias = textureInfo.map(TextureMetadataSection::alphaCutoffBias).orElse(0.0F);
this.transparency = image.computeTransparency();
}
public void increaseMipLevel(int mipLevel) {
try {
this.byMipLevel = MipmapGenerator.generateMipLevels(
this.name, this.byMipLevel, mipLevel, this.mipmapStrategy, this.alphaCutoffBias, this.transparency
);
} catch (Throwable var5) {
CrashReport report = CrashReport.forThrowable(var5, "Generating mipmaps for frame");
CrashReportCategory frameCategory = report.addCategory("Frame being iterated");
frameCategory.setDetail("Sprite name", this.name);
frameCategory.setDetail("Sprite size", () -> this.width + " x " + this.height);
frameCategory.setDetail("Sprite frames", () -> this.getFrameCount() + " frames");
frameCategory.setDetail("Mipmap levels", mipLevel);
frameCategory.setDetail("Original image size", () -> this.originalImage.getWidth() + "x" + this.originalImage.getHeight());
throw new ReportedException(report);
}
}
private int getFrameCount() {
return this.animatedTexture != null ? this.animatedTexture.frames.size() : 1;
}
public boolean isAnimated() {
return this.getFrameCount() > 1;
}
public Transparency transparency() {
return this.transparency;
}
private SpriteContents.@Nullable AnimatedTexture createAnimatedTexture(
FrameSize frameSize, int fullWidth, int fullHeight, AnimationMetadataSection metadata
) {
int frameRowSize = fullWidth / frameSize.width();
int frameColumnSize = fullHeight / frameSize.height();
int totalFrameCount = frameRowSize * frameColumnSize;
int defaultFrameTime = metadata.defaultFrameTime();
List<SpriteContents.FrameInfo> frames;
if (metadata.frames().isEmpty()) {
frames = new ArrayList<>(totalFrameCount);
for (int i = 0; i < totalFrameCount; i++) {
frames.add(new SpriteContents.FrameInfo(i, defaultFrameTime));
}
} else {
List<AnimationFrame> metadataFrames = metadata.frames().get();
frames = new ArrayList<>(metadataFrames.size());
for (AnimationFrame frame : metadataFrames) {
frames.add(new SpriteContents.FrameInfo(frame.index(), frame.timeOr(defaultFrameTime)));
}
int index = 0;
IntSet usedFrameIndices = new IntOpenHashSet();
for (Iterator<SpriteContents.FrameInfo> iterator = frames.iterator(); iterator.hasNext(); index++) {
SpriteContents.FrameInfo frame = iterator.next();
boolean isValid = true;
if (frame.time <= 0) {
LOGGER.warn("Invalid frame duration on sprite {} frame {}: {}", this.name, index, frame.time);
isValid = false;
}
if (frame.index < 0 || frame.index >= totalFrameCount) {
LOGGER.warn("Invalid frame index on sprite {} frame {}: {}", this.name, index, frame.index);
isValid = false;
}
if (isValid) {
usedFrameIndices.add(frame.index);
} else {
iterator.remove();
}
}
int[] unusedFrameIndices = IntStream.range(0, totalFrameCount).filter(i -> !usedFrameIndices.contains(i)).toArray();
if (unusedFrameIndices.length > 0) {
LOGGER.warn("Unused frames in sprite {}: {}", this.name, Arrays.toString(unusedFrameIndices));
}
}
return frames.size() <= 1 ? null : new SpriteContents.AnimatedTexture(List.copyOf(frames), frameRowSize, metadata.interpolatedFrames());
}
@Override
public int width() {
return this.width;
}
@Override
public int height() {
return this.height;
}
@Override
public Identifier name() {
return this.name;
}
public IntList getUniqueFrames() {
return this.animatedTexture != null ? this.animatedTexture.getUniqueFrames() : IntList.of(1);
}
public SpriteContents.@Nullable AnimationState createAnimationState(GpuBufferSlice uboSlice, int spriteUboSize) {
return this.animatedTexture != null ? this.animatedTexture.createAnimationState(uboSlice, spriteUboSize) : null;
}
public <T> Optional<T> getAdditionalMetadata(MetadataSectionType<T> type) {
for (MetadataSectionType.WithValue<?> metadata : this.additionalMetadata) {
Optional<T> result = metadata.unwrapToType(type);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}
@Override
public void close() {
for (NativeImage image : this.byMipLevel) {
image.close();
}
}
@Override
public String toString() {
return "SpriteContents{name=" + this.name + ", frameCount=" + this.getFrameCount() + ", height=" + this.height + ", width=" + this.width + "}";
}
public boolean isTransparent(int frame, int x, int y) {
int actualX = x;
int actualY = y;
if (this.animatedTexture != null) {
actualX = x + this.animatedTexture.getFrameX(frame) * this.width;
actualY = y + this.animatedTexture.getFrameY(frame) * this.height;
}
return ARGB.alpha(this.originalImage.getPixel(actualX, actualY)) == 0;
}
public Transparency computeTransparency(float u0, float v0, float u1, float v1) {
if (this.transparency.isOpaque()) {
return this.transparency;
} else if (u0 == 0.0F && v0 == 0.0F && u1 == 1.0F && v1 == 1.0F) {
return this.transparency;
} else {
int x0 = Mth.floor(u0 * this.width);
int y0 = Mth.floor(v0 * this.height);
int x1 = Mth.ceil(u1 * this.width);
int y1 = Mth.ceil(v1 * this.height);
if (this.animatedTexture == null) {
return this.originalImage.computeTransparency(x0, y0, x1, y1);
} else {
IntList uniqueFrames = this.animatedTexture.uniqueFrames;
Transparency transparency = Transparency.NONE;
for (int i = 0; i < uniqueFrames.size(); i++) {
int frame = uniqueFrames.getInt(i);
int frameX = this.animatedTexture.getFrameX(frame) * this.width;
int frameY = this.animatedTexture.getFrameY(frame) * this.height;
transparency = transparency.or(this.originalImage.computeTransparency(frameX + x0, frameY + y0, frameX + x1, frameY + y1));
}
return transparency;
}
}
}
public void uploadFirstFrame(GpuTexture destination, int level) {
RenderSystem.getDevice()
.createCommandEncoder()
.writeToTexture(destination, this.byMipLevel[level], level, 0, 0, 0, this.width >> level, this.height >> level, 0, 0);
}
@OnlyIn(Dist.CLIENT)
private class AnimatedTexture {
private final List<SpriteContents.FrameInfo> frames;
private final IntList uniqueFrames;
private final int frameRowSize;
private final boolean interpolateFrames;
private AnimatedTexture(List<SpriteContents.FrameInfo> frames, int frameRowSize, boolean interpolateFrames) {
Objects.requireNonNull(SpriteContents.this);
super();
this.frames = frames;
this.frameRowSize = frameRowSize;
this.interpolateFrames = interpolateFrames;
this.uniqueFrames = IntArrayList.toList(frames.stream().mapToInt(SpriteContents.FrameInfo::index).distinct());
}
private int getFrameX(int index) {
return index % this.frameRowSize;
}
private int getFrameY(int index) {
return index / this.frameRowSize;
}
public SpriteContents.AnimationState createAnimationState(GpuBufferSlice uboSlice, int spriteUboSize) {
GpuDevice device = RenderSystem.getDevice();
Int2ObjectMap<GpuTextureView> frameTexturesByIndex = new Int2ObjectOpenHashMap<>();
GpuBufferSlice[] spriteUbosByMip = new GpuBufferSlice[SpriteContents.this.byMipLevel.length];
for (int i = 0; i < this.uniqueFrames.size(); i++) {
int frame = this.uniqueFrames.getInt(i);
GpuTexture texture = device.createTexture(
() -> SpriteContents.this.name + " animation frame " + frame,
5,
TextureFormat.RGBA8,
SpriteContents.this.width,
SpriteContents.this.height,
1,
SpriteContents.this.byMipLevel.length
);
int offsetX = this.getFrameX(frame) * SpriteContents.this.width;
int offsetY = this.getFrameY(frame) * SpriteContents.this.height;
for (int level = 0; level < SpriteContents.this.byMipLevel.length; level++) {
RenderSystem.getDevice()
.createCommandEncoder()
.writeToTexture(
texture,
SpriteContents.this.byMipLevel[level],
level,
0,
0,
0,
SpriteContents.this.width >> level,
SpriteContents.this.height >> level,
offsetX >> level,
offsetY >> level
);
}
frameTexturesByIndex.put(frame, RenderSystem.getDevice().createTextureView(texture));
}
for (int level = 0; level < SpriteContents.this.byMipLevel.length; level++) {
spriteUbosByMip[level] = uboSlice.slice(level * spriteUboSize, spriteUboSize);
}
return SpriteContents.this.new AnimationState(this, frameTexturesByIndex, spriteUbosByMip);
}
public IntList getUniqueFrames() {
return this.uniqueFrames;
}
}
@OnlyIn(Dist.CLIENT)
public class AnimationState implements AutoCloseable {
private int frame;
private int subFrame;
private final SpriteContents.AnimatedTexture animationInfo;
private final Int2ObjectMap<GpuTextureView> frameTexturesByIndex;
private final GpuBufferSlice[] spriteUbosByMip;
private boolean isDirty;
private AnimationState(
SpriteContents.AnimatedTexture animationInfo, Int2ObjectMap<GpuTextureView> frameTexturesByIndex, GpuBufferSlice[] spriteUbosByMip
) {
Objects.requireNonNull(SpriteContents.this);
super();
this.isDirty = true;
this.animationInfo = animationInfo;
this.frameTexturesByIndex = frameTexturesByIndex;
this.spriteUbosByMip = spriteUbosByMip;
}
public void tick() {
this.subFrame++;
this.isDirty = false;
SpriteContents.FrameInfo currentFrame = this.animationInfo.frames.get(this.frame);
if (this.subFrame >= currentFrame.time) {
int oldFrame = currentFrame.index;
this.frame = (this.frame + 1) % this.animationInfo.frames.size();
this.subFrame = 0;
int newFrame = this.animationInfo.frames.get(this.frame).index;
if (oldFrame != newFrame) {
this.isDirty = true;
}
}
}
public GpuBufferSlice getDrawUbo(int level) {
return this.spriteUbosByMip[level];
}
public boolean needsToDraw() {
return this.animationInfo.interpolateFrames || this.isDirty;
}
public void drawToAtlas(RenderPass renderPass, GpuBufferSlice ubo) {
GpuSampler sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.NEAREST, true);
List<SpriteContents.FrameInfo> frames = this.animationInfo.frames;
int oldFrame = frames.get(this.frame).index;
float frameProgress = (float)this.subFrame / this.animationInfo.frames.get(this.frame).time;
int frameProgressAsInt = (int)(frameProgress * 1000.0F);
if (this.animationInfo.interpolateFrames) {
int newFrame = frames.get((this.frame + 1) % frames.size()).index;
renderPass.setPipeline(RenderPipelines.ANIMATE_SPRITE_INTERPOLATE);
renderPass.bindTexture("CurrentSprite", this.frameTexturesByIndex.get(oldFrame), sampler);
renderPass.bindTexture("NextSprite", this.frameTexturesByIndex.get(newFrame), sampler);
} else if (this.isDirty) {
renderPass.setPipeline(RenderPipelines.ANIMATE_SPRITE_BLIT);
renderPass.bindTexture("Sprite", this.frameTexturesByIndex.get(oldFrame), sampler);
}
renderPass.setUniform("SpriteAnimationInfo", ubo);
renderPass.draw(frameProgressAsInt << 3, 6);
}
@Override
public void close() {
for (GpuTextureView view : this.frameTexturesByIndex.values()) {
view.texture().close();
view.close();
}
}
}
@OnlyIn(Dist.CLIENT)
private record FrameInfo(int index, int time) {
}
}引用的其他类
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
Std140SizeCalculator()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
字段/返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
RenderSystem.getDevice(), RenderSystem.getSamplerCache()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
CrashReport.forThrowable()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ReportedException()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
MipmapGenerator.generateMipLevels()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
实现
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ARGB.alpha()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.ceil(), Mth.floor()
- 引用位置: