package net.minecraft.client.gui.font.glyphs; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.vertex.VertexConsumer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.font.GlyphRenderTypes; import net.minecraft.client.renderer.RenderType; import net.minecraft.network.chat.Style; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; @Environment(EnvType.CLIENT) public class BakedGlyph { public static final float Z_FIGHTER = 0.001F; private final GlyphRenderTypes renderTypes; @Nullable private final GpuTextureView textureView; private final float u0; private final float u1; private final float v0; private final float v1; private final float left; private final float right; private final float up; private final float down; public BakedGlyph( GlyphRenderTypes renderTypes, @Nullable GpuTextureView textureView, float u0, float u1, float v0, float v1, float left, float right, float up, float down ) { this.renderTypes = renderTypes; this.textureView = textureView; this.u0 = u0; this.u1 = u1; this.v0 = v0; this.v1 = v1; this.left = left; this.right = right; this.up = up; this.down = down; } public float left(BakedGlyph.GlyphInstance glyph) { return glyph.x + this.left + (glyph.style.isItalic() ? Math.min(this.shearTop(), this.shearBottom()) : 0.0F) - extraThickness(glyph.style.isBold()); } public float top(BakedGlyph.GlyphInstance glyph) { return glyph.y + this.up - extraThickness(glyph.style.isBold()); } public float right(BakedGlyph.GlyphInstance glyph) { return glyph.x + this.right + (glyph.hasShadow() ? glyph.shadowOffset : 0.0F) + (glyph.style.isItalic() ? Math.max(this.shearTop(), this.shearBottom()) : 0.0F) + extraThickness(glyph.style.isBold()); } public float bottom(BakedGlyph.GlyphInstance glyph) { return glyph.y + this.down + (glyph.hasShadow() ? glyph.shadowOffset : 0.0F) + extraThickness(glyph.style.isBold()); } public void renderChar(BakedGlyph.GlyphInstance glyph, Matrix4f pose, VertexConsumer buffer, int packedLight, boolean noDepth) { Style style = glyph.style(); boolean bl = style.isItalic(); float f = glyph.x(); float g = glyph.y(); int i = glyph.color(); boolean bl2 = style.isBold(); float h = noDepth ? 0.0F : 0.001F; float k; if (glyph.hasShadow()) { int j = glyph.shadowColor(); this.render(bl, f + glyph.shadowOffset(), g + glyph.shadowOffset(), 0.0F, pose, buffer, j, bl2, packedLight); if (bl2) { this.render(bl, f + glyph.boldOffset() + glyph.shadowOffset(), g + glyph.shadowOffset(), h, pose, buffer, j, true, packedLight); } k = noDepth ? 0.0F : 0.03F; } else { k = 0.0F; } this.render(bl, f, g, k, pose, buffer, i, bl2, packedLight); if (bl2) { this.render(bl, f + glyph.boldOffset(), g, k + h, pose, buffer, i, true, packedLight); } } private void render(boolean italic, float x, float y, float z, Matrix4f pose, VertexConsumer buffer, int color, boolean bold, int packedLight) { float f = x + this.left; float g = x + this.right; float h = y + this.up; float i = y + this.down; float j = italic ? this.shearTop() : 0.0F; float k = italic ? this.shearBottom() : 0.0F; float l = extraThickness(bold); buffer.addVertex(pose, f + j - l, h - l, z).setColor(color).setUv(this.u0, this.v0).setLight(packedLight); buffer.addVertex(pose, f + k - l, i + l, z).setColor(color).setUv(this.u0, this.v1).setLight(packedLight); buffer.addVertex(pose, g + k + l, i + l, z).setColor(color).setUv(this.u1, this.v1).setLight(packedLight); buffer.addVertex(pose, g + j + l, h - l, z).setColor(color).setUv(this.u1, this.v0).setLight(packedLight); } private static float extraThickness(boolean bold) { return bold ? 0.1F : 0.0F; } private float shearBottom() { return 1.0F - 0.25F * this.down; } private float shearTop() { return 1.0F - 0.25F * this.up; } public void renderEffect(BakedGlyph.Effect effect, Matrix4f pose, VertexConsumer buffer, int packedLight, boolean noDepth) { float f = noDepth ? 0.0F : effect.depth; if (effect.hasShadow()) { this.buildEffect(effect, effect.shadowOffset(), f, effect.shadowColor(), buffer, packedLight, pose); f += noDepth ? 0.0F : 0.03F; } this.buildEffect(effect, 0.0F, f, effect.color, buffer, packedLight, pose); } private void buildEffect( BakedGlyph.Effect effect, float shadowOffset, float depthOffset, int shadowColor, VertexConsumer buffer, int packedLight, Matrix4f pose ) { buffer.addVertex(pose, effect.x0 + shadowOffset, effect.y1 + shadowOffset, depthOffset).setColor(shadowColor).setUv(this.u0, this.v0).setLight(packedLight); buffer.addVertex(pose, effect.x1 + shadowOffset, effect.y1 + shadowOffset, depthOffset).setColor(shadowColor).setUv(this.u0, this.v1).setLight(packedLight); buffer.addVertex(pose, effect.x1 + shadowOffset, effect.y0 + shadowOffset, depthOffset).setColor(shadowColor).setUv(this.u1, this.v1).setLight(packedLight); buffer.addVertex(pose, effect.x0 + shadowOffset, effect.y0 + shadowOffset, depthOffset).setColor(shadowColor).setUv(this.u1, this.v0).setLight(packedLight); } @Nullable public GpuTextureView textureView() { return this.textureView; } public RenderPipeline guiPipeline() { return this.renderTypes.guiPipeline(); } public RenderType renderType(Font.DisplayMode displayMode) { return this.renderTypes.select(displayMode); } @Environment(EnvType.CLIENT) public record Effect(float x0, float y0, float x1, float y1, float depth, int color, int shadowColor, float shadowOffset) { public Effect(float x0, float y0, float x1, float y1, float depth, int color) { this(x0, y0, x1, y1, depth, color, 0, 0.0F); } public float left() { return this.x0; } public float top() { return this.y0; } public float right() { return this.x1 + (this.hasShadow() ? this.shadowOffset : 0.0F); } public float bottom() { return this.y1 + (this.hasShadow() ? this.shadowOffset : 0.0F); } boolean hasShadow() { return this.shadowColor() != 0; } } @Environment(EnvType.CLIENT) public record GlyphInstance(float x, float y, int color, int shadowColor, BakedGlyph glyph, Style style, float boldOffset, float shadowOffset) { public float left() { return this.glyph.left(this); } public float top() { return this.glyph.top(this); } public float right() { return this.glyph.right(this); } public float bottom() { return this.glyph.bottom(this); } boolean hasShadow() { return this.shadowColor() != 0; } } }