mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-12 05:05:03 +01:00
Improve and polish text utilities
This commit is contained in:
parent
183e2d78b7
commit
3dac11899e
26 changed files with 626 additions and 463 deletions
|
@ -2,7 +2,6 @@ package dev.engine_room.flywheel.lib.instance;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
|
||||||
import net.minecraft.util.FastColor;
|
import net.minecraft.util.FastColor;
|
||||||
|
|
||||||
public abstract class ColoredLitInstance extends AbstractInstance implements FlatLit {
|
public abstract class ColoredLitInstance extends AbstractInstance implements FlatLit {
|
||||||
|
@ -11,7 +10,6 @@ public abstract class ColoredLitInstance extends AbstractInstance implements Fla
|
||||||
public byte blue = (byte) 0xFF;
|
public byte blue = (byte) 0xFF;
|
||||||
public byte alpha = (byte) 0xFF;
|
public byte alpha = (byte) 0xFF;
|
||||||
|
|
||||||
public int overlay = OverlayTexture.NO_OVERLAY;
|
|
||||||
public int light = 0;
|
public int light = 0;
|
||||||
|
|
||||||
public ColoredLitInstance(InstanceType<? extends ColoredLitInstance> type, InstanceHandle handle) {
|
public ColoredLitInstance(InstanceType<? extends ColoredLitInstance> type, InstanceHandle handle) {
|
||||||
|
@ -49,9 +47,12 @@ public abstract class ColoredLitInstance extends AbstractInstance implements Fla
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitInstance overlay(int overlay) {
|
public ColoredLitInstance color(float red, float green, float blue, float alpha) {
|
||||||
this.overlay = overlay;
|
return color((byte) (red * 255f), (byte) (green * 255f), (byte) (blue * 255f), (byte) (alpha * 255f));
|
||||||
return this;
|
}
|
||||||
|
|
||||||
|
public ColoredLitInstance color(float red, float green, float blue) {
|
||||||
|
return color((byte) (red * 255f), (byte) (green * 255f), (byte) (blue * 255f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package dev.engine_room.flywheel.lib.instance;
|
||||||
|
|
||||||
|
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
||||||
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
|
|
||||||
|
public abstract class ColoredLitOverlayInstance extends ColoredLitInstance {
|
||||||
|
public int overlay = OverlayTexture.NO_OVERLAY;
|
||||||
|
|
||||||
|
public ColoredLitOverlayInstance(InstanceType<? extends ColoredLitOverlayInstance> type, InstanceHandle handle) {
|
||||||
|
super(type, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColoredLitOverlayInstance overlay(int overlay) {
|
||||||
|
this.overlay = overlay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,107 +1,66 @@
|
||||||
package dev.engine_room.flywheel.lib.instance;
|
package dev.engine_room.flywheel.lib.instance;
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Matrix4fc;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
|
import dev.engine_room.flywheel.lib.internal.BakedGlyphExtension;
|
||||||
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
||||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
import dev.engine_room.flywheel.lib.math.DataPacker;
|
||||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||||
import net.minecraft.util.FastColor;
|
|
||||||
|
|
||||||
public class GlyphInstance extends AbstractInstance {
|
public class GlyphInstance extends ColoredLitInstance {
|
||||||
// Skew x by 1 - 0.25 * y
|
// Skew x by 1 - 0.25 * y
|
||||||
// Note that columns are written as rows.
|
// Note that columns are written as rows.
|
||||||
private static final Matrix4f ITALIC_SKEW = new Matrix4f(1, 0, 0, 0, -0.25f, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
|
private static final Matrix4fc ITALIC_SKEW = new Matrix4f(1, 0, 0, 0, -0.25f, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
|
||||||
|
|
||||||
public final Matrix4f pose = new Matrix4f();
|
public final Matrix4f pose = new Matrix4f();
|
||||||
|
|
||||||
public int us;
|
public int packedUs;
|
||||||
public int vs;
|
public int packedVs;
|
||||||
|
|
||||||
public byte red = (byte) 0xFF;
|
public GlyphInstance(InstanceType<? extends GlyphInstance> type, InstanceHandle handle) {
|
||||||
public byte green = (byte) 0xFF;
|
|
||||||
public byte blue = (byte) 0xFF;
|
|
||||||
public byte alpha = (byte) 0xFF;
|
|
||||||
|
|
||||||
public int light = 0;
|
|
||||||
|
|
||||||
public GlyphInstance(InstanceType<?> type, InstanceHandle handle) {
|
|
||||||
super(type, handle);
|
super(type, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlyphInstance setGlyph(BakedGlyph glyph, float x, float y, boolean italic) {
|
public GlyphInstance setGlyph(BakedGlyph glyph, Matrix4fc initialPose, float x, float y, boolean italic) {
|
||||||
var glyphReader = FlwLibLink.INSTANCE.getGlyphExtension(glyph);
|
var glyphExtension = FlwLibLink.INSTANCE.getBakedGlyphExtension(glyph);
|
||||||
|
setUvs(glyphExtension);
|
||||||
|
|
||||||
setUvs(glyphReader);
|
float left = glyphExtension.flywheel$left();
|
||||||
float left = glyphReader.flywheel$left();
|
float up = glyphExtension.flywheel$up();
|
||||||
float right = glyphReader.flywheel$right();
|
|
||||||
float up = glyphReader.flywheel$up();
|
|
||||||
float down = glyphReader.flywheel$down();
|
|
||||||
|
|
||||||
pose.translate(x + left, y + up - 3.0f, 0.0f);
|
pose.set(initialPose);
|
||||||
pose.scale(right - left, down - up, 1.0f);
|
pose.translate(x, y, 0.0f);
|
||||||
|
|
||||||
if (italic) {
|
if (italic) {
|
||||||
pose.mul(ITALIC_SKEW);
|
pose.mul(ITALIC_SKEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pose.translate(left, up - 3.0f, 0.0f);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlyphInstance setEffect(BakedGlyph glyph, float x0, float y0, float x1, float y1, float depth) {
|
public GlyphInstance setEffect(BakedGlyph glyph, Matrix4fc initialPose, float x0, float y0, float x1, float y1, float depth) {
|
||||||
var glyphReader = FlwLibLink.INSTANCE.getGlyphExtension(glyph);
|
var glyphExtension = FlwLibLink.INSTANCE.getBakedGlyphExtension(glyph);
|
||||||
|
setUvs(glyphExtension);
|
||||||
setUvs(glyphReader);
|
|
||||||
|
|
||||||
|
pose.set(initialPose);
|
||||||
pose.translate(x0, y0, depth);
|
pose.translate(x0, y0, depth);
|
||||||
pose.scale(x1 - x0, y1 - y0, 1.0f);
|
pose.scale(x1 - x0, y1 - y0, 1.0f);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlyphInstance colorArgb(int argb) {
|
private void setUvs(BakedGlyphExtension glyphExtension) {
|
||||||
return color(FastColor.ARGB32.red(argb), FastColor.ARGB32.green(argb), FastColor.ARGB32.blue(argb), FastColor.ARGB32.alpha(argb));
|
float u0 = glyphExtension.flywheel$u0();
|
||||||
}
|
float u1 = glyphExtension.flywheel$u1();
|
||||||
|
float v0 = glyphExtension.flywheel$v0();
|
||||||
|
float v1 = glyphExtension.flywheel$v1();
|
||||||
|
|
||||||
public GlyphInstance colorRgb(int rgb) {
|
packedUs = ((int) DataPacker.packNormU16(u1) << 16) | (int) DataPacker.packNormU16(u0);
|
||||||
return color(FastColor.ARGB32.red(rgb), FastColor.ARGB32.green(rgb), FastColor.ARGB32.blue(rgb));
|
packedVs = ((int) DataPacker.packNormU16(v1) << 16) | (int) DataPacker.packNormU16(v0);
|
||||||
}
|
|
||||||
|
|
||||||
public GlyphInstance color(int red, int green, int blue, int alpha) {
|
|
||||||
return color((byte) red, (byte) green, (byte) blue, (byte) alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlyphInstance color(int red, int green, int blue) {
|
|
||||||
return color((byte) red, (byte) green, (byte) blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlyphInstance color(float red, float green, float blue, float alpha) {
|
|
||||||
return color((byte) (red * 255f), (byte) (green * 255f), (byte) (blue * 255f), (byte) (alpha * 255f));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlyphInstance color(byte red, byte green, byte blue, byte alpha) {
|
|
||||||
this.red = red;
|
|
||||||
this.green = green;
|
|
||||||
this.blue = blue;
|
|
||||||
this.alpha = alpha;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlyphInstance color(byte red, byte green, byte blue) {
|
|
||||||
this.red = red;
|
|
||||||
this.green = green;
|
|
||||||
this.blue = blue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUvs(GlyphExtension glyphReader) {
|
|
||||||
float u0 = glyphReader.flywheel$u0();
|
|
||||||
float u1 = glyphReader.flywheel$u1();
|
|
||||||
float v0 = glyphReader.flywheel$v0();
|
|
||||||
float v1 = glyphReader.flywheel$v1();
|
|
||||||
|
|
||||||
us = (int) (u0 * 65536) | ((int) (u1 * 65536) << 16);
|
|
||||||
vs = (int) (v0 * 65536) | ((int) (v1 * 65536) << 16);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,8 @@ public final class InstanceTypes {
|
||||||
.build())
|
.build())
|
||||||
.writer((ptr, instance) -> {
|
.writer((ptr, instance) -> {
|
||||||
ExtraMemoryOps.putMatrix4f(ptr, instance.pose);
|
ExtraMemoryOps.putMatrix4f(ptr, instance.pose);
|
||||||
ExtraMemoryOps.put2x16(ptr + 64, instance.us);
|
ExtraMemoryOps.put2x16(ptr + 64, instance.packedUs);
|
||||||
ExtraMemoryOps.put2x16(ptr + 68, instance.vs);
|
ExtraMemoryOps.put2x16(ptr + 68, instance.packedVs);
|
||||||
MemoryUtil.memPutByte(ptr + 72, instance.red);
|
MemoryUtil.memPutByte(ptr + 72, instance.red);
|
||||||
MemoryUtil.memPutByte(ptr + 73, instance.green);
|
MemoryUtil.memPutByte(ptr + 73, instance.green);
|
||||||
MemoryUtil.memPutByte(ptr + 74, instance.blue);
|
MemoryUtil.memPutByte(ptr + 74, instance.blue);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import dev.engine_room.flywheel.lib.transform.Rotate;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class OrientedInstance extends ColoredLitInstance implements Rotate<OrientedInstance> {
|
public class OrientedInstance extends ColoredLitOverlayInstance implements Rotate<OrientedInstance> {
|
||||||
public float posX;
|
public float posX;
|
||||||
public float posY;
|
public float posY;
|
||||||
public float posZ;
|
public float posZ;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
import dev.engine_room.flywheel.lib.transform.Transform;
|
import dev.engine_room.flywheel.lib.transform.Transform;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
public class PosedInstance extends ColoredLitInstance implements Transform<PosedInstance> {
|
public class PosedInstance extends ColoredLitOverlayInstance implements Transform<PosedInstance> {
|
||||||
public final Matrix4f pose = new Matrix4f();
|
public final Matrix4f pose = new Matrix4f();
|
||||||
public final Matrix3f normal = new Matrix3f();
|
public final Matrix3f normal = new Matrix3f();
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
import dev.engine_room.flywheel.lib.transform.Affine;
|
import dev.engine_room.flywheel.lib.transform.Affine;
|
||||||
|
|
||||||
public class TransformedInstance extends ColoredLitInstance implements Affine<TransformedInstance> {
|
public class TransformedInstance extends ColoredLitOverlayInstance implements Affine<TransformedInstance> {
|
||||||
public final Matrix4f pose = new Matrix4f();
|
public final Matrix4f pose = new Matrix4f();
|
||||||
|
|
||||||
public TransformedInstance(InstanceType<? extends TransformedInstance> type, InstanceHandle handle) {
|
public TransformedInstance(InstanceType<? extends TransformedInstance> type, InstanceHandle handle) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package dev.engine_room.flywheel.lib.internal;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public interface GlyphExtension {
|
public interface BakedGlyphExtension {
|
||||||
float flywheel$u0();
|
float flywheel$u0();
|
||||||
|
|
||||||
float flywheel$u1();
|
float flywheel$u1();
|
|
@ -29,7 +29,9 @@ public interface FlwLibLink {
|
||||||
|
|
||||||
Deque<PoseStack.Pose> getPoseStack(PoseStack stack);
|
Deque<PoseStack.Pose> getPoseStack(PoseStack stack);
|
||||||
|
|
||||||
GlyphExtension getGlyphExtension(BakedGlyph glyph);
|
|
||||||
|
|
||||||
FontSet getFontSet(Font font, ResourceLocation loc);
|
FontSet getFontSet(Font font, ResourceLocation loc);
|
||||||
|
|
||||||
|
boolean getFilterFishyGlyphs(Font font);
|
||||||
|
|
||||||
|
BakedGlyphExtension getBakedGlyphExtension(BakedGlyph glyph);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package dev.engine_room.flywheel.lib.internal;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public interface FontTextureExtension {
|
|
||||||
void flywheel$setName(ResourceLocation value);
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ package dev.engine_room.flywheel.lib.material;
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
import dev.engine_room.flywheel.api.Flywheel;
|
||||||
import dev.engine_room.flywheel.api.material.LightShader;
|
import dev.engine_room.flywheel.api.material.LightShader;
|
||||||
|
|
||||||
public class LightShaders {
|
public final class LightShaders {
|
||||||
public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(Flywheel.rl("light/smooth_when_embedded.glsl"));
|
public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(Flywheel.rl("light/smooth_when_embedded.glsl"));
|
||||||
public static final LightShader SMOOTH = new SimpleLightShader(Flywheel.rl("light/smooth.glsl"));
|
public static final LightShader SMOOTH = new SimpleLightShader(Flywheel.rl("light/smooth.glsl"));
|
||||||
public static final LightShader FLAT = new SimpleLightShader(Flywheel.rl("light/flat.glsl"));
|
public static final LightShader FLAT = new SimpleLightShader(Flywheel.rl("light/flat.glsl"));
|
||||||
|
|
|
@ -33,4 +33,32 @@ public final class DataPacker {
|
||||||
public static float unpackNormI8(byte b) {
|
public static float unpackNormI8(byte b) {
|
||||||
return (float) b / 127f;
|
return (float) b / 127f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack a float as an unsigned, normalized short.
|
||||||
|
*/
|
||||||
|
public static short packNormU16(float f) {
|
||||||
|
return (short) (int) (Mth.clamp(f, 0.0f, 1.0f) * 65535);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpack an unsigned, normalized short to a float.
|
||||||
|
*/
|
||||||
|
public static float unpackNormU16(short s) {
|
||||||
|
return (float) (Short.toUnsignedInt(s)) / 65535f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack a float as a signed, normalized byte.
|
||||||
|
*/
|
||||||
|
public static short packNormI16(float f) {
|
||||||
|
return (short) (Mth.clamp(f, -1.0f, 1.0f) * 32767);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpack a signed, normalized byte to a float.
|
||||||
|
*/
|
||||||
|
public static float unpackNormI16(short s) {
|
||||||
|
return (float) s / 32767f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ public final class FireComponent implements EntityComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private record FireMesh(TextureAtlasSprite sprite) implements QuadMesh {
|
private record FireMesh(TextureAtlasSprite sprite) implements QuadMesh {
|
||||||
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0, 0.5f, 0, (float) (Math.sqrt(2) * 0.5));
|
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0, 0.5f, 0, Mth.SQRT_OF_TWO * 0.5f);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int vertexCount() {
|
public int vertexCount() {
|
||||||
|
|
|
@ -3,25 +3,24 @@ package dev.engine_room.flywheel.lib.visual.text;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector2fc;
|
||||||
|
|
||||||
public record SimpleTextLayer(GlyphMeshStyle style, GlyphMaterial material, GlyphColor color, int bias, float offsetX,
|
public record SimpleTextLayer(GlyphPattern pattern, GlyphMaterial material, GlyphColor color, Vector2fc offset, int bias) implements TextLayer {
|
||||||
float offsetY, float effectOffsetX, float effectOffsetY) implements TextLayer {
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
private static final Vector2fc NO_OFFSET = new Vector2f();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlyphMeshStyle style;
|
private GlyphPattern pattern;
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlyphMaterial material;
|
private GlyphMaterial material;
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlyphColor color;
|
private GlyphColor color;
|
||||||
|
private Vector2fc offset = NO_OFFSET;
|
||||||
|
private int bias = 0;
|
||||||
|
|
||||||
private int bias;
|
public Builder pattern(GlyphPattern pattern) {
|
||||||
private float offsetX = 0;
|
this.pattern = pattern;
|
||||||
private float offsetY = 0;
|
|
||||||
private float effectOffsetX = 1;
|
|
||||||
private float effectOffsetY = 1;
|
|
||||||
|
|
||||||
public Builder style(GlyphMeshStyle style) {
|
|
||||||
this.style = style;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,37 +34,27 @@ public record SimpleTextLayer(GlyphMeshStyle style, GlyphMaterial material, Glyp
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder offset(Vector2fc offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder offset(float offsetX, float offsetY) {
|
||||||
|
offset = new Vector2f(offsetX, offsetY);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder bias(int bias) {
|
public Builder bias(int bias) {
|
||||||
this.bias = bias;
|
this.bias = bias;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder offsetX(float offsetX) {
|
|
||||||
this.offsetX = offsetX;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder offsetY(float offsetY) {
|
|
||||||
this.offsetY = offsetY;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder effectOffsetX(float effectOffsetX) {
|
|
||||||
this.effectOffsetX = effectOffsetX;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder effectOffsetY(float effectOffsetY) {
|
|
||||||
this.effectOffsetY = effectOffsetY;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleTextLayer build() {
|
public SimpleTextLayer build() {
|
||||||
Objects.requireNonNull(style);
|
Objects.requireNonNull(pattern);
|
||||||
Objects.requireNonNull(material);
|
Objects.requireNonNull(material);
|
||||||
Objects.requireNonNull(color);
|
Objects.requireNonNull(color);
|
||||||
|
|
||||||
return new SimpleTextLayer(style, material, color, bias, offsetX, offsetY, effectOffsetX, effectOffsetY);
|
return new SimpleTextLayer(pattern, material, color, offset, bias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,29 @@ package dev.engine_room.flywheel.lib.visual.text;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector2fc;
|
||||||
|
|
||||||
|
import dev.engine_room.flywheel.api.material.DepthTest;
|
||||||
import dev.engine_room.flywheel.api.material.Material;
|
import dev.engine_room.flywheel.api.material.Material;
|
||||||
|
import dev.engine_room.flywheel.api.material.Transparency;
|
||||||
|
import dev.engine_room.flywheel.api.material.WriteMask;
|
||||||
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
||||||
|
import dev.engine_room.flywheel.lib.material.FogShaders;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
import net.minecraft.network.chat.Style;
|
import net.minecraft.client.gui.Font;
|
||||||
import net.minecraft.network.chat.TextColor;
|
import net.minecraft.network.chat.TextColor;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.FastColor;
|
||||||
|
|
||||||
public interface TextLayer {
|
public interface TextLayer {
|
||||||
float ONE_PIXEL = 0.125f;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The style of individual glyphs.
|
* The pattern of individual glyphs.
|
||||||
*
|
*
|
||||||
* @return A GlyphMeshStyle.
|
* @return A GlyphPattern.
|
||||||
*/
|
*/
|
||||||
GlyphMeshStyle style();
|
GlyphPattern pattern();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping from texture ResourceLocations to Flywheel materials.
|
* A mapping from texture ResourceLocations to Flywheel materials.
|
||||||
|
@ -35,6 +40,13 @@ public interface TextLayer {
|
||||||
*/
|
*/
|
||||||
GlyphColor color();
|
GlyphColor color();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset of text in this layer.
|
||||||
|
*
|
||||||
|
* @return The offset.
|
||||||
|
*/
|
||||||
|
Vector2fc offset();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The instancer bias for this layer.
|
* The instancer bias for this layer.
|
||||||
*
|
*
|
||||||
|
@ -42,33 +54,85 @@ public interface TextLayer {
|
||||||
*/
|
*/
|
||||||
int bias();
|
int bias();
|
||||||
|
|
||||||
/**
|
// TODO: probably just convert this to Iterable<Vector2fc>
|
||||||
* The x offset of text content in this layer.
|
@FunctionalInterface
|
||||||
*
|
interface GlyphPattern {
|
||||||
* @return The x offset.
|
/**
|
||||||
*/
|
* The pattern for a single glyph with no offset.
|
||||||
float offsetX();
|
*/
|
||||||
|
GlyphPattern SINGLE = out -> out.accept(new Vector2f(0, 0));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The y offset of text content in this layer.
|
* The pattern for an 8x outline as used by glowing text on signs.
|
||||||
*
|
*/
|
||||||
* @return The y offset.
|
GlyphPattern OUTLINE = out -> {
|
||||||
*/
|
for (int x = -1; x <= 1; ++x) {
|
||||||
float offsetY();
|
for (int y = -1; y <= 1; ++y) {
|
||||||
|
if (x == 0 && y == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
out.accept(new Vector2f(x, y));
|
||||||
* The x offset of text effects such as strikethrough or underline in this layer.
|
}
|
||||||
*
|
}
|
||||||
* @return The x offset.
|
};
|
||||||
*/
|
|
||||||
float effectOffsetX();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The y offset of text effects such as strikethrough or underline in this layer.
|
* Add an arbitrary amount of glyphs. Each accepted vector represents
|
||||||
*
|
* the offset of a new glyph quad.
|
||||||
* @return The y offset.
|
*
|
||||||
*/
|
* @param out The consumer to accept the offset of a new glyph quad
|
||||||
float effectOffsetY();
|
*/
|
||||||
|
void addGlyphs(Consumer<Vector2fc> out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface GlyphMaterial {
|
||||||
|
// FIXME: account for intensity
|
||||||
|
GlyphMaterial NORMAL = texture -> SimpleMaterial.builder()
|
||||||
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
|
.texture(texture)
|
||||||
|
.mipmap(false)
|
||||||
|
.transparency(Transparency.TRANSLUCENT)
|
||||||
|
.diffuse(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
GlyphMaterial SEE_THROUGH = texture -> SimpleMaterial.builder()
|
||||||
|
.fog(FogShaders.NONE)
|
||||||
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
|
.texture(texture)
|
||||||
|
.mipmap(false)
|
||||||
|
.depthTest(DepthTest.ALWAYS)
|
||||||
|
.transparency(Transparency.TRANSLUCENT)
|
||||||
|
.writeMask(WriteMask.COLOR)
|
||||||
|
.diffuse(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
GlyphMaterial POLYGON_OFFSET = texture -> SimpleMaterial.builder()
|
||||||
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
|
.texture(texture)
|
||||||
|
.mipmap(false)
|
||||||
|
.polygonOffset(true)
|
||||||
|
.transparency(Transparency.TRANSLUCENT)
|
||||||
|
.diffuse(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
static GlyphMaterial fromDisplayMode(Font.DisplayMode displayMode) {
|
||||||
|
return switch (displayMode) {
|
||||||
|
case NORMAL -> NORMAL;
|
||||||
|
case SEE_THROUGH -> SEE_THROUGH;
|
||||||
|
case POLYGON_OFFSET -> POLYGON_OFFSET;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Flywheel material for the given glyph texture.
|
||||||
|
*
|
||||||
|
* @param texture The texture to use.
|
||||||
|
* @return A material.
|
||||||
|
*/
|
||||||
|
Material create(ResourceLocation texture);
|
||||||
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface GlyphColor {
|
interface GlyphColor {
|
||||||
|
@ -78,16 +142,47 @@ public interface TextLayer {
|
||||||
* @param color The ARGB color to default to.
|
* @param color The ARGB color to default to.
|
||||||
* @return A new GlyphColor.
|
* @return A new GlyphColor.
|
||||||
*/
|
*/
|
||||||
static GlyphColor defaultTo(int color) {
|
static GlyphColor defaultTo(int color, float dimFactor) {
|
||||||
return style -> {
|
int finalColor;
|
||||||
TextColor textColor = style.getColor();
|
if (dimFactor != 1.0f) {
|
||||||
|
finalColor = FastColor.ARGB32.color(
|
||||||
|
FastColor.ARGB32.alpha(color),
|
||||||
|
(int) (FastColor.ARGB32.red(color) * dimFactor),
|
||||||
|
(int) (FastColor.ARGB32.green(color) * dimFactor),
|
||||||
|
(int) (FastColor.ARGB32.blue(color) * dimFactor)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
finalColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textColor -> {
|
||||||
if (textColor != null) {
|
if (textColor != null) {
|
||||||
return adjustColor(textColor.getValue());
|
int textColorArgb = textColor.getValue();
|
||||||
|
if (dimFactor != 1.0f) {
|
||||||
|
return FastColor.ARGB32.color(
|
||||||
|
FastColor.ARGB32.alpha(finalColor),
|
||||||
|
(int) (FastColor.ARGB32.red(textColorArgb) * dimFactor),
|
||||||
|
(int) (FastColor.ARGB32.green(textColorArgb) * dimFactor),
|
||||||
|
(int) (FastColor.ARGB32.blue(textColorArgb) * dimFactor)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (finalColor & 0xFF000000) | (textColorArgb & 0xFFFFFF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return color;
|
return finalColor;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default to the given color if no color is specified in the style.
|
||||||
|
*
|
||||||
|
* @param color The ARGB color to default to.
|
||||||
|
* @return A new GlyphColor.
|
||||||
|
*/
|
||||||
|
static GlyphColor defaultTo(int color) {
|
||||||
|
return defaultTo(color, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always use the given color, regardless of the style.
|
* Always use the given color, regardless of the style.
|
||||||
*
|
*
|
||||||
|
@ -95,7 +190,7 @@ public interface TextLayer {
|
||||||
* @return A new GlyphColor.
|
* @return A new GlyphColor.
|
||||||
*/
|
*/
|
||||||
static GlyphColor always(int color) {
|
static GlyphColor always(int color) {
|
||||||
return style -> color;
|
return textColor -> color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,68 +207,11 @@ public interface TextLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a style to a color.
|
* Convert a nullable text color to a color.
|
||||||
*
|
*
|
||||||
* @param style The style of the text to colorize.
|
* @param textColor The color of the text to colorize.
|
||||||
* @return The color to use, in ARGB format.
|
* @return The color to use, in ARGB format.
|
||||||
*/
|
*/
|
||||||
int color(Style style);
|
int color(@Nullable TextColor textColor);
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface GlyphMaterial {
|
|
||||||
GlyphMaterial SIMPLE = texture -> SimpleMaterial.builder()
|
|
||||||
.texture(texture)
|
|
||||||
.cutout(CutoutShaders.ONE_TENTH)
|
|
||||||
.diffuse(false)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
GlyphMaterial POLYGON_OFFSET = texture -> SimpleMaterial.builder()
|
|
||||||
.texture(texture)
|
|
||||||
.cutout(CutoutShaders.ONE_TENTH)
|
|
||||||
.diffuse(false)
|
|
||||||
.polygonOffset(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Flywheel material for the given glyph texture.
|
|
||||||
*
|
|
||||||
* @param texture The texture to use.
|
|
||||||
* @return A material.
|
|
||||||
*/
|
|
||||||
Material create(ResourceLocation texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface GlyphMeshStyle {
|
|
||||||
/**
|
|
||||||
* The standard style for glyphs with no repetition.
|
|
||||||
*/
|
|
||||||
GlyphMeshStyle SIMPLE = out -> out.accept(new Vector3f(0, 0, 0));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The style for glyphs with a 8x outline as used by glowing text on signs.
|
|
||||||
*/
|
|
||||||
GlyphMeshStyle OUTLINE = out -> {
|
|
||||||
for (int x = -1; x <= 1; ++x) {
|
|
||||||
for (int y = -1; y <= 1; ++y) {
|
|
||||||
if (x == 0 && y == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.accept(new Vector3f(x * ONE_PIXEL, y * ONE_PIXEL, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add quads to the mesh. Each vec3 submitted to out will be expanded
|
|
||||||
* into a unit quad in the XY plane with the lowest corner at the given vec3.
|
|
||||||
* You can think of each submitted vec3 as a duplication of a glyph.
|
|
||||||
*
|
|
||||||
* @param out The consumer to accept the quads
|
|
||||||
*/
|
|
||||||
void addQuads(Consumer<Vector3f> out);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package dev.engine_room.flywheel.lib.visual.text;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
|
||||||
|
public final class TextLayers {
|
||||||
|
public static TextLayer normal(int color, Font.DisplayMode displayMode, int bias) {
|
||||||
|
return new SimpleTextLayer.Builder().pattern(TextLayer.GlyphPattern.SINGLE)
|
||||||
|
.material(TextLayer.GlyphMaterial.fromDisplayMode(displayMode))
|
||||||
|
.color(TextLayer.GlyphColor.defaultTo(TextLayer.GlyphColor.adjustColor(color)))
|
||||||
|
.bias(bias)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextLayer normal(int color, Font.DisplayMode displayMode) {
|
||||||
|
return normal(color, displayMode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextLayer dropShadow(int color, Font.DisplayMode displayMode, int bias) {
|
||||||
|
return new SimpleTextLayer.Builder().pattern(TextLayer.GlyphPattern.SINGLE)
|
||||||
|
.material(TextLayer.GlyphMaterial.fromDisplayMode(displayMode))
|
||||||
|
.color(TextLayer.GlyphColor.defaultTo(TextLayer.GlyphColor.adjustColor(color), 0.25f))
|
||||||
|
.offset(1, 1)
|
||||||
|
.bias(bias)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextLayer dropShadow(int color, Font.DisplayMode displayMode) {
|
||||||
|
return dropShadow(color, displayMode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextLayer outline(int color, int bias) {
|
||||||
|
return new SimpleTextLayer.Builder().pattern(TextLayer.GlyphPattern.OUTLINE)
|
||||||
|
.material(TextLayer.GlyphMaterial.NORMAL)
|
||||||
|
.color(TextLayer.GlyphColor.always(TextLayer.GlyphColor.adjustColor(color)))
|
||||||
|
.bias(bias)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextLayer outline(int color) {
|
||||||
|
return outline(color, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextLayers() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,15 +4,18 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector2fc;
|
||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
import org.joml.Vector4fc;
|
import org.joml.Vector4fc;
|
||||||
|
|
||||||
import com.mojang.blaze3d.font.GlyphInfo;
|
import com.mojang.blaze3d.font.GlyphInfo;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||||
|
import dev.engine_room.flywheel.api.model.Mesh;
|
||||||
import dev.engine_room.flywheel.api.model.Model;
|
import dev.engine_room.flywheel.api.model.Model;
|
||||||
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
|
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
|
||||||
import dev.engine_room.flywheel.lib.instance.GlyphInstance;
|
import dev.engine_room.flywheel.lib.instance.GlyphInstance;
|
||||||
|
@ -27,65 +30,66 @@ import net.minecraft.client.gui.Font;
|
||||||
import net.minecraft.client.gui.font.FontSet;
|
import net.minecraft.client.gui.font.FontSet;
|
||||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||||
import net.minecraft.client.gui.font.glyphs.EmptyGlyph;
|
import net.minecraft.client.gui.font.glyphs.EmptyGlyph;
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
import net.minecraft.network.chat.Style;
|
import net.minecraft.network.chat.Style;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.util.FormattedCharSequence;
|
import net.minecraft.util.FormattedCharSequence;
|
||||||
import net.minecraft.util.FormattedCharSink;
|
import net.minecraft.util.FormattedCharSink;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visual that renders a single line of text.
|
* A visual that renders a single line of text.
|
||||||
*/
|
*/
|
||||||
public class TextVisual {
|
public final class TextVisual {
|
||||||
|
private static final Font FONT = Minecraft.getInstance().font;
|
||||||
|
|
||||||
|
private static final ResourceReloadCache<GlyphMeshKey, GlyphMesh> GLYPH_MESH_CACHE = new ResourceReloadCache<>(GlyphMeshKey::into);
|
||||||
|
private static final ResourceReloadCache<GlyphModelKey, Model> GLYPH_MODEL_CACHE = new ResourceReloadCache<>(GlyphModelKey::into);
|
||||||
|
|
||||||
private static final ThreadLocal<Sink> SINKS = ThreadLocal.withInitial(Sink::new);
|
private static final ThreadLocal<Sink> SINKS = ThreadLocal.withInitial(Sink::new);
|
||||||
|
|
||||||
private final SmartRecycler<GlyphInstancerKey, GlyphInstance> recycler;
|
private final SmartRecycler<GlyphInstanceKey, GlyphInstance> recycler;
|
||||||
|
private final List<TextLayer> layers = new ArrayList<>();
|
||||||
|
private final Matrix4f pose = new Matrix4f();
|
||||||
|
|
||||||
private FormattedCharSequence content = FormattedCharSequence.EMPTY;
|
private FormattedCharSequence text = FormattedCharSequence.EMPTY;
|
||||||
private float x;
|
private float x;
|
||||||
private float y;
|
private float y;
|
||||||
private int backgroundColor = 0;
|
private int backgroundColor = 0;
|
||||||
private int light;
|
private int light;
|
||||||
private boolean fullBright;
|
|
||||||
|
|
||||||
private final List<TextLayer> layers = new ArrayList<>();
|
|
||||||
|
|
||||||
private final Matrix4f pose = new Matrix4f();
|
|
||||||
|
|
||||||
public TextVisual(InstancerProvider provider) {
|
public TextVisual(InstancerProvider provider) {
|
||||||
recycler = new SmartRecycler<>(key -> provider.instancer(InstanceTypes.GLYPH, GLYPH_CACHE.get(key.modelKey), key.bias)
|
recycler = new SmartRecycler<>(key -> provider.instancer(InstanceTypes.GLYPH, GLYPH_MODEL_CACHE.get(key.modelKey), key.bias)
|
||||||
.createInstance());
|
.createInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextVisual content(FormattedCharSequence content) {
|
|
||||||
this.content = content;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matrix4f pose() {
|
|
||||||
return pose;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextVisual clearLayers() {
|
|
||||||
layers.clear();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextVisual addLayer(TextLayer layer) {
|
public TextVisual addLayer(TextLayer layer) {
|
||||||
layers.add(layer);
|
layers.add(layer);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextVisual addLayers(Collection<TextLayer> layers) {
|
||||||
|
this.layers.addAll(layers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TextVisual layers(Collection<TextLayer> layers) {
|
public TextVisual layers(Collection<TextLayer> layers) {
|
||||||
this.layers.clear();
|
this.layers.clear();
|
||||||
this.layers.addAll(layers);
|
this.layers.addAll(layers);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextVisual pos(float x, float y) {
|
public TextVisual clearLayers() {
|
||||||
this.x = x;
|
layers.clear();
|
||||||
this.y = y;
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4f pose() {
|
||||||
|
return pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextVisual text(FormattedCharSequence text) {
|
||||||
|
this.text = text;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +103,12 @@ public class TextVisual {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextVisual pos(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TextVisual backgroundColor(int backgroundColor) {
|
public TextVisual backgroundColor(int backgroundColor) {
|
||||||
this.backgroundColor = backgroundColor;
|
this.backgroundColor = backgroundColor;
|
||||||
return this;
|
return this;
|
||||||
|
@ -109,33 +119,35 @@ public class TextVisual {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextVisual fullBright(boolean fullBright) {
|
public TextVisual reset() {
|
||||||
this.fullBright = fullBright;
|
layers.clear();
|
||||||
|
pose.identity();
|
||||||
|
|
||||||
|
text = FormattedCharSequence.EMPTY;
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
backgroundColor = 0;
|
||||||
|
light = 0;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: method to just update pose or light without recalculating text
|
// TODO: track glyph instances and add method to update only UVs of obfuscated glyphs, method to update only
|
||||||
|
// background color, and method to only update light
|
||||||
public void setup() {
|
public void setup() {
|
||||||
recycler.resetCount();
|
recycler.resetCount();
|
||||||
|
|
||||||
var sink = SINKS.get();
|
var sink = SINKS.get();
|
||||||
|
|
||||||
var light = fullBright ? LightTexture.FULL_BRIGHT : this.light;
|
|
||||||
sink.prepare(recycler, pose, light);
|
sink.prepare(recycler, pose, light);
|
||||||
|
|
||||||
int maxX = 0;
|
int maxX = 0;
|
||||||
// Can we flip the inner and outer loops here?
|
|
||||||
// Would that even be better?
|
|
||||||
for (TextLayer layer : layers) {
|
for (TextLayer layer : layers) {
|
||||||
sink.x = x;
|
sink.prepareForLayer(layer, x, y);
|
||||||
sink.y = y;
|
text.accept(sink);
|
||||||
sink.layer = layer;
|
|
||||||
content.accept(sink);
|
|
||||||
maxX = Math.max(maxX, (int) sink.x);
|
maxX = Math.max(maxX, (int) sink.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
sink.addBackground(backgroundColor, x, maxX);
|
sink.addBackground(backgroundColor, x, maxX);
|
||||||
|
|
||||||
sink.clear();
|
sink.clear();
|
||||||
|
|
||||||
recycler.discardExtra();
|
recycler.discardExtra();
|
||||||
|
@ -145,166 +157,226 @@ public class TextVisual {
|
||||||
recycler.delete();
|
recycler.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Sink implements FormattedCharSink {
|
private record GlyphMeshKey(float glyphWidth, float glyphHeight, TextLayer.GlyphPattern pattern, boolean bold, float boldOffset, float shadowOffset) {
|
||||||
private final Font font;
|
public GlyphMesh into() {
|
||||||
|
List<Vector2fc> out = new ArrayList<>();
|
||||||
|
|
||||||
|
pattern.addGlyphs(offsetc -> {
|
||||||
|
Vector2f offset = new Vector2f(offsetc).mul(shadowOffset);
|
||||||
|
out.add(offset);
|
||||||
|
|
||||||
|
if (bold) {
|
||||||
|
out.add(new Vector2f(offset.x() + boldOffset, offset.y()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new GlyphMesh(glyphWidth, glyphHeight, out.toArray(Vector2fc[]::new));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record GlyphModelKey(@Nullable GlyphMeshKey meshKey, TextLayer.GlyphMaterial material, ResourceLocation texture) {
|
||||||
|
public Model into() {
|
||||||
|
Mesh mesh;
|
||||||
|
|
||||||
|
if (meshKey != null) {
|
||||||
|
mesh = GLYPH_MESH_CACHE.get(meshKey);
|
||||||
|
} else {
|
||||||
|
mesh = GlyphEffectMesh.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SingleMeshModel(mesh, material.create(texture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record GlyphInstanceKey(GlyphModelKey modelKey, int bias) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Sink implements FormattedCharSink {
|
||||||
@UnknownNullability
|
@UnknownNullability
|
||||||
private SmartRecycler<GlyphInstancerKey, GlyphInstance> recycler;
|
private SmartRecycler<GlyphInstanceKey, GlyphInstance> recycler;
|
||||||
@UnknownNullability
|
@UnknownNullability
|
||||||
private Matrix4f pose;
|
private Matrix4f pose;
|
||||||
@UnknownNullability
|
|
||||||
private TextLayer layer;
|
|
||||||
|
|
||||||
private int light;
|
private int light;
|
||||||
|
|
||||||
|
@UnknownNullability
|
||||||
|
private TextLayer layer;
|
||||||
private float x;
|
private float x;
|
||||||
private float y;
|
private float y;
|
||||||
|
|
||||||
private Sink() {
|
public void prepare(SmartRecycler<GlyphInstanceKey, GlyphInstance> recycler, Matrix4f pose, int light) {
|
||||||
font = Minecraft.getInstance().font;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepare(SmartRecycler<GlyphInstancerKey, GlyphInstance> recycler, Matrix4f pose, int light) {
|
|
||||||
this.recycler = recycler;
|
this.recycler = recycler;
|
||||||
this.pose = pose;
|
this.pose = pose;
|
||||||
this.light = light;
|
this.light = light;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clear() {
|
public void prepareForLayer(TextLayer layer, float x, float y) {
|
||||||
|
this.layer = layer;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
recycler = null;
|
recycler = null;
|
||||||
pose = null;
|
pose = null;
|
||||||
layer = null;
|
layer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(int i, Style style, int j) {
|
public boolean accept(int index, Style style, int codePoint) {
|
||||||
FontSet fontSet = FlwLibLink.INSTANCE.getFontSet(font, style.getFont());
|
FontSet fontSet = FlwLibLink.INSTANCE.getFontSet(FONT, style.getFont());
|
||||||
GlyphInfo glyphInfo = fontSet.getGlyphInfo(j, false);
|
GlyphInfo glyphInfo = fontSet.getGlyphInfo(codePoint, FlwLibLink.INSTANCE.getFilterFishyGlyphs(FONT));
|
||||||
BakedGlyph bakedGlyph = style.isObfuscated() && j != 32 ? fontSet.getRandomGlyph(glyphInfo) : fontSet.getGlyph(j);
|
BakedGlyph glyph = style.isObfuscated() && codePoint != ' ' ? fontSet.getRandomGlyph(glyphInfo) : fontSet.getGlyph(codePoint);
|
||||||
|
|
||||||
boolean bold = style.isBold();
|
boolean bold = style.isBold();
|
||||||
|
|
||||||
int color = layer.color()
|
int color = layer.color()
|
||||||
.color(style);
|
.color(style.getColor());
|
||||||
|
Vector2fc offset = layer.offset();
|
||||||
|
|
||||||
if (!(bakedGlyph instanceof EmptyGlyph)) {
|
if (!(glyph instanceof EmptyGlyph)) {
|
||||||
var glyphExtension = FlwLibLink.INSTANCE.getGlyphExtension(bakedGlyph);
|
GlyphInstance instance = recycler.get(key(glyphInfo, glyph, layer.pattern(), bold));
|
||||||
|
float shadowOffset = glyphInfo.getShadowOffset();
|
||||||
GlyphInstance glyph = recycler.get(key(glyphExtension.flywheel$texture(), bold, layer.style()));
|
instance.setGlyph(glyph, pose, x + offset.x() * shadowOffset, y + offset.y() * shadowOffset, style.isItalic());
|
||||||
|
instance.colorArgb(color);
|
||||||
glyph.pose.set(pose);
|
instance.light(light);
|
||||||
glyph.setGlyph(bakedGlyph, this.x + layer.offsetX(), this.y + layer.offsetY(), style.isItalic());
|
instance.setChanged();
|
||||||
glyph.colorArgb(color);
|
|
||||||
glyph.light = light;
|
|
||||||
glyph.setChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float advance = glyphInfo.getAdvance(bold);
|
float advance = glyphInfo.getAdvance(bold);
|
||||||
float effectX = layer.effectOffsetX();
|
// SpecialGlyphs.WHITE, which effects use, has a shadowOffset of 1, so don't modify the offset returned by the layer.
|
||||||
float effectY = layer.effectOffsetY();
|
float effectOffsetX = offset.x();
|
||||||
|
float effectOffsetY = offset.y();
|
||||||
if (style.isStrikethrough()) {
|
if (style.isStrikethrough()) {
|
||||||
this.addEffect(this.x + effectX - 1.0f, this.y + effectY + 4.5f, this.x + effectX + advance, this.y + effectY + 4.5f - 1.0f, 0.01f, color);
|
addEffect(x + effectOffsetX - 1.0f, y + effectOffsetY + 4.5f, x + effectOffsetX + advance, y + effectOffsetY + 4.5f - 1.0f, 0.01f, color);
|
||||||
}
|
}
|
||||||
if (style.isUnderlined()) {
|
if (style.isUnderlined()) {
|
||||||
this.addEffect(this.x + effectX - 1.0f, this.y + effectY + 9.0f, this.x + effectX + advance, this.y + effectY + 9.0f - 1.0f, 0.01f, color);
|
addEffect(x + effectOffsetX - 1.0f, y + effectOffsetY + 9.0f, x + effectOffsetX + advance, y + effectOffsetY + 9.0f - 1.0f, 0.01f, color);
|
||||||
}
|
}
|
||||||
this.x += advance;
|
|
||||||
|
x += advance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEffect(float x0, float y0, float x1, float y1, float depth, int colorArgb) {
|
|
||||||
BakedGlyph bakedGlyph = FlwLibLink.INSTANCE.getFontSet(font, Style.DEFAULT_FONT)
|
|
||||||
.whiteGlyph();
|
|
||||||
|
|
||||||
var glyphExtension = FlwLibLink.INSTANCE.getGlyphExtension(bakedGlyph);
|
|
||||||
|
|
||||||
GlyphInstance glyph = recycler.get(key(glyphExtension.flywheel$texture(), false, TextLayer.GlyphMeshStyle.SIMPLE));
|
|
||||||
|
|
||||||
glyph.pose.set(pose);
|
|
||||||
glyph.setEffect(bakedGlyph, x0, y0, x1, y1, depth);
|
|
||||||
glyph.colorArgb(colorArgb);
|
|
||||||
glyph.light = light;
|
|
||||||
glyph.setChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addBackground(int backgroundColor, float startX, float endX) {
|
public void addBackground(int backgroundColor, float startX, float endX) {
|
||||||
if (backgroundColor != 0) {
|
if (backgroundColor != 0) {
|
||||||
this.addEffect(startX - 1.0f, this.y + 9.0f, endX + 1.0f, this.y - 1.0f, 0.01f, backgroundColor);
|
addEffect(startX - 1.0f, y + 9.0f, endX + 1.0f, y - 1.0f, 0.01f, backgroundColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlyphInstancerKey key(ResourceLocation texture, boolean bold, TextLayer.GlyphMeshStyle style) {
|
private void addEffect(float x0, float y0, float x1, float y1, float depth, int colorArgb) {
|
||||||
var meshKey = new GlyphMeshKey(style, bold);
|
BakedGlyph glyph = FlwLibLink.INSTANCE.getFontSet(FONT, Style.DEFAULT_FONT)
|
||||||
var modelKey = new GlyphModelKey(texture, meshKey, layer.material());
|
.whiteGlyph();
|
||||||
return new GlyphInstancerKey(modelKey, layer.bias());
|
|
||||||
|
GlyphInstance instance = recycler.get(effectKey(glyph));
|
||||||
|
instance.setEffect(glyph, pose, x0, y0, x1, y1, depth);
|
||||||
|
instance.colorArgb(colorArgb);
|
||||||
|
instance.light(light);
|
||||||
|
instance.setChanged();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private record GlyphInstancerKey(GlyphModelKey modelKey, int bias) {
|
private GlyphInstanceKey key(GlyphInfo glyphInfo, BakedGlyph glyph, TextLayer.GlyphPattern pattern, boolean bold) {
|
||||||
}
|
var glyphExtension = FlwLibLink.INSTANCE.getBakedGlyphExtension(glyph);
|
||||||
|
float glyphWidth = glyphExtension.flywheel$right() - glyphExtension.flywheel$left();
|
||||||
|
float glyphHeight = glyphExtension.flywheel$down() - glyphExtension.flywheel$up();
|
||||||
|
|
||||||
private static final ResourceReloadCache<GlyphModelKey, Model> GLYPH_CACHE = new ResourceReloadCache<>(GlyphModelKey::into);
|
return key(glyphWidth, glyphHeight, glyphExtension.flywheel$texture(), pattern, bold, bold ? glyphInfo.getBoldOffset() : 0, glyphInfo.getShadowOffset());
|
||||||
private static final ResourceReloadCache<GlyphMeshKey, GlyphMesh> MESH_CACHE = new ResourceReloadCache<>(GlyphMeshKey::into);
|
|
||||||
|
|
||||||
private record GlyphModelKey(ResourceLocation font, GlyphMeshKey meshKey, TextLayer.GlyphMaterial material) {
|
|
||||||
private Model into() {
|
|
||||||
return new SingleMeshModel(MESH_CACHE.get(meshKey), material.create(font));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private record GlyphMeshKey(TextLayer.GlyphMeshStyle style, boolean bold) {
|
private GlyphInstanceKey key(float glyphWidth, float glyphHeight, ResourceLocation texture, TextLayer.GlyphPattern pattern, boolean bold, float boldOffset, float shadowOffset) {
|
||||||
public GlyphMesh into() {
|
var meshKey = new GlyphMeshKey(glyphWidth, glyphHeight, pattern, bold, boldOffset, shadowOffset);
|
||||||
List<Vector3f> out = new ArrayList<>();
|
var modelKey = new GlyphModelKey(meshKey, layer.material(), texture);
|
||||||
|
return new GlyphInstanceKey(modelKey, layer.bias());
|
||||||
|
}
|
||||||
|
|
||||||
style.addQuads(quad -> {
|
private GlyphInstanceKey effectKey(BakedGlyph glyph) {
|
||||||
out.add(quad);
|
var glyphExtension = FlwLibLink.INSTANCE.getBakedGlyphExtension(glyph);
|
||||||
if (bold) {
|
return effectKey(glyphExtension.flywheel$texture());
|
||||||
out.add(new Vector3f(quad.x + TextLayer.ONE_PIXEL, quad.y, quad.z));
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new GlyphMesh(out.toArray(new Vector3f[0]));
|
private GlyphInstanceKey effectKey(ResourceLocation texture) {
|
||||||
|
var modelKey = new GlyphModelKey(null, layer.material(), texture);
|
||||||
|
return new GlyphInstanceKey(modelKey, layer.bias());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mesh that represents a single glyph. Expects to be drawn with the glyph instance type.
|
* A mesh that represents a pattern of a glyph with a certain width and height. Expects to be drawn with the glyph
|
||||||
|
* instance type.
|
||||||
*
|
*
|
||||||
* @param quads Each quad will be expanded into 4 vertices.
|
* @param offsets Each offset will be expanded into a glyph quad.
|
||||||
*/
|
*/
|
||||||
private record GlyphMesh(Vector3f[] quads) implements QuadMesh {
|
private record GlyphMesh(float glyphWidth, float glyphHeight, Vector2fc[] offsets, Vector4fc boundingSphere) implements QuadMesh {
|
||||||
private static final float[] X = new float[]{0, 0, 1, 1};
|
private static final float[] X = new float[] { 0, 0, 1, 1 };
|
||||||
private static final float[] Y = new float[]{0, 1, 1, 0};
|
private static final float[] Y = new float[] { 0, 1, 1, 0 };
|
||||||
|
|
||||||
|
// FIXME: what is the actual bounding sphere??
|
||||||
|
public GlyphMesh(float glyphWidth, float glyphHeight, Vector2fc[] offsets) {
|
||||||
|
this(glyphWidth, glyphHeight, offsets, new Vector4f(0, 0, 0, Math.max(glyphWidth, glyphHeight) * 2 * Mth.SQRT_OF_TWO));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int vertexCount() {
|
public int vertexCount() {
|
||||||
return 4 * quads.length;
|
return 4 * offsets.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(MutableVertexList vertexList) {
|
public void write(MutableVertexList vertexList) {
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < offsets.length; i++) {
|
||||||
Vector3f quad = quads[i];
|
Vector2fc offset = offsets[i];
|
||||||
var quadStart = i * 4;
|
var startVertex = i * 4;
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
vertexList.x(quadStart + j, quad.x + X[j]);
|
vertexList.x(startVertex + j, offset.x() + (glyphWidth * X[j]));
|
||||||
vertexList.y(quadStart + j, quad.y + Y[j]);
|
vertexList.y(startVertex + j, offset.y() + (glyphHeight * Y[j]));
|
||||||
vertexList.z(quadStart + j, quad.z);
|
vertexList.z(startVertex + j, 0);
|
||||||
vertexList.normalX(quadStart + j, 0);
|
vertexList.r(startVertex + j, 1);
|
||||||
vertexList.normalY(quadStart + j, 0);
|
vertexList.g(startVertex + j, 1);
|
||||||
vertexList.normalZ(quadStart + j, 1);
|
vertexList.b(startVertex + j, 1);
|
||||||
vertexList.overlay(quadStart + j, OverlayTexture.NO_OVERLAY);
|
vertexList.a(startVertex + j, 1);
|
||||||
vertexList.r(quadStart + j, 1);
|
vertexList.overlay(startVertex + j, OverlayTexture.NO_OVERLAY);
|
||||||
vertexList.g(quadStart + j, 1);
|
vertexList.normalX(startVertex + j, 0);
|
||||||
vertexList.b(quadStart + j, 1);
|
vertexList.normalY(startVertex + j, 0);
|
||||||
vertexList.a(quadStart + j, 1);
|
vertexList.normalZ(startVertex + j, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector4fc boundingSphere() {
|
public Vector4fc boundingSphere() {
|
||||||
// FIXME: what is the actual bounding sphere??
|
return boundingSphere;
|
||||||
return new Vector4f(0, 0, 0, 2);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record GlyphEffectMesh() implements QuadMesh {
|
||||||
|
private static final float[] X = new float[] { 0, 1, 1, 0 };
|
||||||
|
private static final float[] Y = new float[] { 0, 0, 1, 1 };
|
||||||
|
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0.5f, 0.5f, 0, Mth.SQRT_OF_TWO * 0.5f);
|
||||||
|
|
||||||
|
public static final GlyphEffectMesh INSTANCE = new GlyphEffectMesh();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int vertexCount() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(MutableVertexList vertexList) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
vertexList.x(i, X[i]);
|
||||||
|
vertexList.y(i, Y[i]);
|
||||||
|
vertexList.z(i, 0);
|
||||||
|
vertexList.r(i, 1);
|
||||||
|
vertexList.g(i, 1);
|
||||||
|
vertexList.b(i, 1);
|
||||||
|
vertexList.a(i, 1);
|
||||||
|
vertexList.normalX(i, 0);
|
||||||
|
vertexList.normalY(i, 0);
|
||||||
|
vertexList.normalZ(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector4fc boundingSphere() {
|
||||||
|
return BOUNDING_SPHERE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ void flw_instanceVertex(in FlwInstance i) {
|
||||||
flw_vertexTexCoord.s = i.u0u1v0v1[(vertexInGlyph & 2u) >> 1u];
|
flw_vertexTexCoord.s = i.u0u1v0v1[(vertexInGlyph & 2u) >> 1u];
|
||||||
flw_vertexTexCoord.t = i.u0u1v0v1[2u + yIndex];
|
flw_vertexTexCoord.t = i.u0u1v0v1[2u + yIndex];
|
||||||
|
|
||||||
flw_vertexColor = i.color;
|
flw_vertexColor *= i.color;
|
||||||
|
|
||||||
// Some drivers have a bug where uint over float division is invalid, so use an explicit cast.
|
// Some drivers have a bug where uint over float division is invalid, so use an explicit cast.
|
||||||
flw_vertexLight = vec2(i.light) / 256.0;
|
flw_vertexLight = vec2(i.light) / 256.0;
|
||||||
|
|
|
@ -12,8 +12,8 @@ import dev.engine_room.flywheel.impl.extension.PoseStackExtension;
|
||||||
import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor;
|
import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor;
|
||||||
import dev.engine_room.flywheel.impl.mixin.PoseStackAccessor;
|
import dev.engine_room.flywheel.impl.mixin.PoseStackAccessor;
|
||||||
import dev.engine_room.flywheel.impl.mixin.text.FontAccessor;
|
import dev.engine_room.flywheel.impl.mixin.text.FontAccessor;
|
||||||
|
import dev.engine_room.flywheel.lib.internal.BakedGlyphExtension;
|
||||||
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
||||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
|
||||||
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
|
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
|
||||||
import net.minecraft.client.gui.Font;
|
import net.minecraft.client.gui.Font;
|
||||||
import net.minecraft.client.gui.font.FontSet;
|
import net.minecraft.client.gui.font.FontSet;
|
||||||
|
@ -47,13 +47,18 @@ public class FlwLibLinkImpl implements FlwLibLink {
|
||||||
return ((PoseStackAccessor) stack).flywheel$getPoseStack();
|
return ((PoseStackAccessor) stack).flywheel$getPoseStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GlyphExtension getGlyphExtension(BakedGlyph glyph) {
|
|
||||||
return (GlyphExtension) glyph;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FontSet getFontSet(Font font, ResourceLocation loc) {
|
public FontSet getFontSet(Font font, ResourceLocation loc) {
|
||||||
return ((FontAccessor) font).flywheel$getFontSet(loc);
|
return ((FontAccessor) font).flywheel$getFontSet(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFilterFishyGlyphs(Font font) {
|
||||||
|
return ((FontAccessor) font).flywheel$getFilterFishyGlyphs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BakedGlyphExtension getBakedGlyphExtension(BakedGlyph glyph) {
|
||||||
|
return (BakedGlyphExtension) glyph;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.engine_room.flywheel.impl.extension;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public interface FontTextureExtension {
|
||||||
|
void flywheel$setName(ResourceLocation name);
|
||||||
|
}
|
|
@ -5,12 +5,12 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
import dev.engine_room.flywheel.lib.internal.BakedGlyphExtension;
|
||||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
@Mixin(BakedGlyph.class)
|
@Mixin(BakedGlyph.class)
|
||||||
public class BakedGlyphMixin implements GlyphExtension {
|
public class BakedGlyphMixin implements BakedGlyphExtension {
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
private float u0;
|
private float u0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.engine_room.flywheel.impl.mixin.text;
|
package dev.engine_room.flywheel.impl.mixin.text;
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
import net.minecraft.client.gui.Font;
|
import net.minecraft.client.gui.Font;
|
||||||
|
@ -9,6 +10,9 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
@Mixin(Font.class)
|
@Mixin(Font.class)
|
||||||
public interface FontAccessor {
|
public interface FontAccessor {
|
||||||
|
@Accessor("filterFishyGlyphs")
|
||||||
|
boolean flywheel$getFilterFishyGlyphs();
|
||||||
|
|
||||||
@Invoker("getFontSet")
|
@Invoker("getFontSet")
|
||||||
FontSet flywheel$getFontSet(ResourceLocation fontLocation);
|
FontSet flywheel$getFontSet(ResourceLocation fontLocation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.lib.internal.FontTextureExtension;
|
import dev.engine_room.flywheel.impl.extension.FontTextureExtension;
|
||||||
import net.minecraft.client.gui.font.FontSet;
|
import net.minecraft.client.gui.font.FontSet;
|
||||||
import net.minecraft.client.gui.font.FontTexture;
|
import net.minecraft.client.gui.font.FontTexture;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
|
@ -21,8 +21,8 @@ import com.mojang.blaze3d.platform.NativeImage;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.impl.FontTextureUpload;
|
import dev.engine_room.flywheel.impl.FontTextureUpload;
|
||||||
import dev.engine_room.flywheel.lib.internal.FontTextureExtension;
|
import dev.engine_room.flywheel.impl.extension.FontTextureExtension;
|
||||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
import dev.engine_room.flywheel.lib.internal.BakedGlyphExtension;
|
||||||
import net.minecraft.client.gui.font.FontTexture;
|
import net.minecraft.client.gui.font.FontTexture;
|
||||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||||
|
@ -90,7 +90,7 @@ public abstract class FontTextureMixin extends AbstractTexture implements FontTe
|
||||||
|
|
||||||
@ModifyExpressionValue(method = "add", at = @At(value = "NEW", target = "net/minecraft/client/gui/font/glyphs/BakedGlyph"))
|
@ModifyExpressionValue(method = "add", at = @At(value = "NEW", target = "net/minecraft/client/gui/font/glyphs/BakedGlyph"))
|
||||||
private BakedGlyph flywheel$setGlyphExtensionName(BakedGlyph original) {
|
private BakedGlyph flywheel$setGlyphExtensionName(BakedGlyph original) {
|
||||||
((GlyphExtension) original).flywheel$texture(flywheel$name);
|
((BakedGlyphExtension) original).flywheel$texture(flywheel$name);
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ public abstract class FontTextureMixin extends AbstractTexture implements FontTe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flywheel$setName(ResourceLocation value) {
|
public void flywheel$setName(ResourceLocation name) {
|
||||||
flywheel$name = value;
|
flywheel$name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,13 @@ import dev.engine_room.flywheel.lib.model.part.ModelTrees;
|
||||||
import dev.engine_room.flywheel.lib.util.ResourceReloadCache;
|
import dev.engine_room.flywheel.lib.util.ResourceReloadCache;
|
||||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||||
import dev.engine_room.flywheel.lib.visual.text.SimpleTextLayer;
|
|
||||||
import dev.engine_room.flywheel.lib.visual.text.TextLayer;
|
import dev.engine_room.flywheel.lib.visual.text.TextLayer;
|
||||||
|
import dev.engine_room.flywheel.lib.visual.text.TextLayers;
|
||||||
import dev.engine_room.flywheel.lib.visual.text.TextVisual;
|
import dev.engine_room.flywheel.lib.visual.text.TextVisual;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
import net.minecraft.client.model.geom.ModelLayers;
|
import net.minecraft.client.model.geom.ModelLayers;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
import net.minecraft.client.renderer.Sheets;
|
import net.minecraft.client.renderer.Sheets;
|
||||||
import net.minecraft.util.FastColor;
|
import net.minecraft.util.FastColor;
|
||||||
import net.minecraft.util.FormattedCharSequence;
|
import net.minecraft.util.FormattedCharSequence;
|
||||||
|
@ -36,8 +38,8 @@ import net.minecraft.world.level.block.state.properties.WoodType;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> implements SimpleDynamicVisual {
|
public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> implements SimpleDynamicVisual {
|
||||||
|
|
||||||
private static final Vec3 TEXT_OFFSET = new Vec3(0.0, 0.3333333432674408, 0.046666666865348816);
|
private static final Vec3 TEXT_OFFSET = new Vec3(0.0, 0.3333333432674408, 0.046666666865348816);
|
||||||
|
private static final Font FONT = Minecraft.getInstance().font;
|
||||||
|
|
||||||
private static final ResourceReloadCache<WoodType, ModelTree> SIGN_MODELS = new ResourceReloadCache<>(SignVisual::createSignModel);
|
private static final ResourceReloadCache<WoodType, ModelTree> SIGN_MODELS = new ResourceReloadCache<>(SignVisual::createSignModel);
|
||||||
|
|
||||||
|
@ -47,12 +49,12 @@ public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> imple
|
||||||
.backfaceCulling(false)
|
.backfaceCulling(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private final Matrix4f pose = new Matrix4f();
|
|
||||||
private final InstanceTree instances;
|
private final InstanceTree instances;
|
||||||
|
private final Matrix4f initialPose;
|
||||||
|
|
||||||
// The 8 lines of text we render
|
// The 8 lines of text we render
|
||||||
private final TextVisual[] frontText = new TextVisual[4];
|
private final TextVisual[] frontTextVisuals = new TextVisual[4];
|
||||||
private final TextVisual[] backText = new TextVisual[4];
|
private final TextVisual[] backTextVisuals = new TextVisual[4];
|
||||||
|
|
||||||
// Need to update these every frame, so just remember which ones are obfuscated
|
// Need to update these every frame, so just remember which ones are obfuscated
|
||||||
// Most of the time this will be empty.
|
// Most of the time this will be empty.
|
||||||
|
@ -65,8 +67,8 @@ public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> imple
|
||||||
super(ctx, blockEntity, partialTick);
|
super(ctx, blockEntity, partialTick);
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
frontText[i] = new TextVisual(ctx.instancerProvider());
|
frontTextVisuals[i] = new TextVisual(ctx.instancerProvider());
|
||||||
backText[i] = new TextVisual(ctx.instancerProvider());
|
backTextVisuals[i] = new TextVisual(ctx.instancerProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
var block = (SignBlock) blockState.getBlock();
|
var block = (SignBlock) blockState.getBlock();
|
||||||
|
@ -79,46 +81,48 @@ public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> imple
|
||||||
instances.childOrThrow("stick")
|
instances.childOrThrow("stick")
|
||||||
.visible(isStanding);
|
.visible(isStanding);
|
||||||
|
|
||||||
var rotation = -block.getYRotationDegrees(blockState);
|
|
||||||
|
|
||||||
var visualPosition = getVisualPosition();
|
var visualPosition = getVisualPosition();
|
||||||
var signModelRenderScale = this.getSignModelRenderScale();
|
var signModelRenderScale = getSignModelRenderScale();
|
||||||
pose.translate(visualPosition.getX() + 0.5f, visualPosition.getY() + 0.75f * signModelRenderScale, visualPosition.getZ() + 0.5f)
|
var rotation = -block.getYRotationDegrees(blockState);
|
||||||
|
initialPose = new Matrix4f().translate(visualPosition.getX() + 0.5f, visualPosition.getY() + 0.75f * signModelRenderScale, visualPosition.getZ() + 0.5f)
|
||||||
.rotateY(Mth.DEG_TO_RAD * rotation);
|
.rotateY(Mth.DEG_TO_RAD * rotation);
|
||||||
|
|
||||||
if (!(isStanding)) {
|
if (!isStanding) {
|
||||||
pose.translate(0.0f, -0.3125f, -0.4375f);
|
initialPose.translate(0.0f, -0.3125f, -0.4375f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only apply this to the instances because text gets a separate scaling.
|
// Only apply this to the instances because text gets a separate scaling.
|
||||||
instances.scale(signModelRenderScale, -signModelRenderScale, -signModelRenderScale);
|
Matrix4f initialModelPose = new Matrix4f(initialPose).scale(signModelRenderScale, -signModelRenderScale, -signModelRenderScale);
|
||||||
|
instances.updateInstancesStatic(initialModelPose);
|
||||||
instances.updateInstancesStatic(pose);
|
|
||||||
|
|
||||||
lastFrontText = blockEntity.getFrontText();
|
lastFrontText = blockEntity.getFrontText();
|
||||||
lastBackText = blockEntity.getBackText();
|
lastBackText = blockEntity.getBackText();
|
||||||
this.setupText(lastFrontText, true);
|
setupText(lastFrontText, true);
|
||||||
this.setupText(lastBackText, false);
|
setupText(lastBackText, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelTree createSignModel(WoodType woodType) {
|
||||||
|
return ModelTrees.of(ModelLayers.createSignModelName(woodType), Sheets.getSignMaterial(woodType), MATERIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(Context ctx) {
|
public void beginFrame(Context ctx) {
|
||||||
boolean setup = false;
|
boolean doSetup = false;
|
||||||
if (lastFrontText != blockEntity.getFrontText()) {
|
if (lastFrontText != blockEntity.getFrontText()) {
|
||||||
lastFrontText = blockEntity.getFrontText();
|
lastFrontText = blockEntity.getFrontText();
|
||||||
setup = true;
|
doSetup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastBackText != blockEntity.getBackText()) {
|
if (lastBackText != blockEntity.getBackText()) {
|
||||||
lastBackText = blockEntity.getBackText();
|
lastBackText = blockEntity.getBackText();
|
||||||
setup = true;
|
doSetup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setup) {
|
if (doSetup) {
|
||||||
// Setup both to make it easier to track obfuscation
|
// Setup both to make it easier to track obfuscation
|
||||||
obfuscated.clear();
|
obfuscated.clear();
|
||||||
this.setupText(lastBackText, false);
|
setupText(lastFrontText, true);
|
||||||
this.setupText(lastFrontText, true);
|
setupText(lastBackText, false);
|
||||||
} else {
|
} else {
|
||||||
// The is visible check is relatively expensive compared to the boolean checks above,
|
// The is visible check is relatively expensive compared to the boolean checks above,
|
||||||
// so only do it when it'll actually save some work in obfuscating.
|
// so only do it when it'll actually save some work in obfuscating.
|
||||||
|
@ -128,11 +132,6 @@ public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void collectCrumblingInstances(Consumer<@Nullable Instance> consumer) {
|
|
||||||
instances.traverse(consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateLight(float partialTick) {
|
public void updateLight(float partialTick) {
|
||||||
int packedLight = computePackedLight();
|
int packedLight = computePackedLight();
|
||||||
|
@ -141,120 +140,123 @@ public class SignVisual extends AbstractBlockEntityVisual<SignBlockEntity> imple
|
||||||
.setChanged();
|
.setChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var text : frontText) {
|
if (!lastFrontText.hasGlowingText()) {
|
||||||
text.light(packedLight);
|
for (var text : frontTextVisuals) {
|
||||||
|
text.light(packedLight);
|
||||||
|
text.setup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (var text : backText) {
|
|
||||||
text.light(packedLight);
|
if (!lastBackText.hasGlowingText()) {
|
||||||
|
for (var text : backTextVisuals) {
|
||||||
|
text.light(packedLight);
|
||||||
|
text.setup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectCrumblingInstances(Consumer<@Nullable Instance> consumer) {
|
||||||
|
instances.traverse(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void _delete() {
|
protected void _delete() {
|
||||||
instances.delete();
|
instances.delete();
|
||||||
|
|
||||||
for (var text : frontText) {
|
for (var text : frontTextVisuals) {
|
||||||
text.delete();
|
text.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var text : backText) {
|
for (var text : backTextVisuals) {
|
||||||
text.delete();
|
text.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getSignModelRenderScale() {
|
protected float getSignModelRenderScale() {
|
||||||
return 0.6666667f;
|
return 0.6666667f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getSignTextRenderScale() {
|
protected float getSignTextRenderScale() {
|
||||||
return 0.6666667f;
|
return 0.6666667f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3 getTextOffset() {
|
protected Vec3 getTextOffset() {
|
||||||
return TEXT_OFFSET;
|
return TEXT_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupText(SignText text, boolean isFrontText) {
|
private void setupText(SignText text, boolean isFrontText) {
|
||||||
FormattedCharSequence[] formattedCharSequences = text.getRenderMessages(Minecraft.getInstance()
|
FormattedCharSequence[] textLines = text.getRenderMessages(Minecraft.getInstance()
|
||||||
.isTextFilteringEnabled(), component -> {
|
.isTextFilteringEnabled(), component -> {
|
||||||
List<FormattedCharSequence> list = Minecraft.getInstance().font.split(component, blockEntity.getMaxTextLineWidth());
|
List<FormattedCharSequence> list = FONT.split(component, blockEntity.getMaxTextLineWidth());
|
||||||
return list.isEmpty() ? FormattedCharSequence.EMPTY : list.get(0);
|
return list.isEmpty() ? FormattedCharSequence.EMPTY : list.get(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
List<TextLayer> layers = new ArrayList<>();
|
List<TextLayer> layers = new ArrayList<>();
|
||||||
|
|
||||||
int darkColor = TextLayer.GlyphColor.adjustColor(getDarkColor(text));
|
int darkColor = getDarkColor(text);
|
||||||
int textColor;
|
int textColor;
|
||||||
if (text.hasGlowingText()) {
|
if (text.hasGlowingText()) {
|
||||||
textColor = TextLayer.GlyphColor.adjustColor(text.getColor()
|
textColor = text.getColor()
|
||||||
.getTextColor());
|
.getTextColor();
|
||||||
|
|
||||||
layers.add(new SimpleTextLayer.Builder().style(TextLayer.GlyphMeshStyle.OUTLINE)
|
layers.add(TextLayers.outline(darkColor));
|
||||||
.material(TextLayer.GlyphMaterial.SIMPLE)
|
|
||||||
.color(TextLayer.GlyphColor.always(darkColor))
|
|
||||||
.build());
|
|
||||||
} else {
|
} else {
|
||||||
textColor = darkColor;
|
textColor = darkColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.add(new SimpleTextLayer.Builder().style(TextLayer.GlyphMeshStyle.SIMPLE)
|
layers.add(TextLayers.normal(textColor, Font.DisplayMode.POLYGON_OFFSET, 1));
|
||||||
.material(TextLayer.GlyphMaterial.POLYGON_OFFSET)
|
|
||||||
.color(TextLayer.GlyphColor.defaultTo(textColor))
|
|
||||||
.bias(1)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
var dst = isFrontText ? frontText : backText;
|
var textVisuals = isFrontText ? frontTextVisuals : backTextVisuals;
|
||||||
|
|
||||||
int lineHeight = blockEntity.getTextLineHeight();
|
int lineHeight = blockEntity.getTextLineHeight();
|
||||||
int lineDelta = 4 * lineHeight / 2;
|
int lineDelta = 4 * lineHeight / 2;
|
||||||
for (int m = 0; m < 4; ++m) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
FormattedCharSequence formattedCharSequence = formattedCharSequences[m];
|
FormattedCharSequence textLine = textLines[i];
|
||||||
float f = (float) -Minecraft.getInstance().font.width(formattedCharSequence) / 2;
|
float x = (float) (-FONT.width(textLine) / 2);
|
||||||
|
float y = i * lineHeight - lineDelta;
|
||||||
|
|
||||||
var textVisual = dst[m].content(formattedCharSequence)
|
var textVisual = textVisuals[i].layers(layers)
|
||||||
.layers(layers)
|
.text(textLine)
|
||||||
.fullBright(text.hasGlowingText())
|
.pos(x, y)
|
||||||
.backgroundColor(0)
|
.backgroundColor(0);
|
||||||
.x(f)
|
|
||||||
.y(m * lineHeight - lineDelta);
|
|
||||||
|
|
||||||
var textPose = textVisual.pose();
|
|
||||||
|
|
||||||
textPose.set(pose);
|
|
||||||
|
|
||||||
|
var pose = textVisual.pose().set(initialPose);
|
||||||
if (!isFrontText) {
|
if (!isFrontText) {
|
||||||
textPose.rotateY(Mth.PI);
|
pose.rotateY(Mth.PI);
|
||||||
}
|
}
|
||||||
var offset = getTextOffset();
|
float scale = 0.015625f * getSignTextRenderScale();
|
||||||
float scale = 0.015625f * this.getSignTextRenderScale();
|
var textOffset = getTextOffset();
|
||||||
textPose.translate((float) offset.x, (float) offset.y, (float) offset.z);
|
pose.translate((float) textOffset.x, (float) textOffset.y, (float) textOffset.z);
|
||||||
textPose.scale(scale, -scale, scale);
|
pose.scale(scale, -scale, scale);
|
||||||
|
|
||||||
|
if (text.hasGlowingText()) {
|
||||||
|
textVisual.light(LightTexture.FULL_BRIGHT);
|
||||||
|
}
|
||||||
|
// FIXME: incorrect light when going from glowing to non-glowing
|
||||||
|
|
||||||
textVisual.setup();
|
textVisual.setup();
|
||||||
|
|
||||||
if (hasObfuscation(formattedCharSequence)) {
|
if (hasObfuscation(textLine)) {
|
||||||
this.obfuscated.add(textVisual);
|
obfuscated.add(textVisual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasObfuscation(FormattedCharSequence text) {
|
private static int getDarkColor(SignText signText) {
|
||||||
|
int colorArgb = signText.getColor()
|
||||||
|
.getTextColor();
|
||||||
|
if (colorArgb == DyeColor.BLACK.getTextColor() && signText.hasGlowingText()) {
|
||||||
|
return 0xFFF0EBCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = (int) ((double) FastColor.ARGB32.red(colorArgb) * 0.4);
|
||||||
|
int g = (int) ((double) FastColor.ARGB32.green(colorArgb) * 0.4);
|
||||||
|
int b = (int) ((double) FastColor.ARGB32.blue(colorArgb) * 0.4);
|
||||||
|
return FastColor.ARGB32.color(0, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasObfuscation(FormattedCharSequence text) {
|
||||||
return text.accept((i, s, j) -> s.isObfuscated());
|
return text.accept((i, s, j) -> s.isObfuscated());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDarkColor(SignText signText) {
|
|
||||||
int i = signText.getColor()
|
|
||||||
.getTextColor();
|
|
||||||
if (i == DyeColor.BLACK.getTextColor() && signText.hasGlowingText()) {
|
|
||||||
return -988212;
|
|
||||||
}
|
|
||||||
int j = (int) ((double) FastColor.ARGB32.red(i) * 0.4);
|
|
||||||
int k = (int) ((double) FastColor.ARGB32.green(i) * 0.4);
|
|
||||||
int l = (int) ((double) FastColor.ARGB32.blue(i) * 0.4);
|
|
||||||
return FastColor.ARGB32.color(0, j, k, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ModelTree createSignModel(WoodType woodType) {
|
|
||||||
return ModelTrees.of(ModelLayers.createSignModelName(woodType), Sheets.getSignMaterial(woodType), MATERIAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ parchment_version = 2023.09.03
|
||||||
# Minecraft build dependency versions
|
# Minecraft build dependency versions
|
||||||
minecraft_version = 1.20.1
|
minecraft_version = 1.20.1
|
||||||
forge_version = 47.2.19
|
forge_version = 47.2.19
|
||||||
fabric_loader_version=0.16.5
|
fabric_loader_version = 0.16.5
|
||||||
fabric_api_version = 0.92.1+1.20.1
|
fabric_api_version = 0.92.1+1.20.1
|
||||||
|
|
||||||
# Build dependency mod versions
|
# Build dependency mod versions
|
||||||
|
|
Loading…
Reference in a new issue