Cleaned up terminal rendering code, in particular made it more robust when scaled.

This commit is contained in:
Florian Nücke
2020-10-09 12:07:08 +02:00
parent fb34a90131
commit e9bb1e2b8d
9 changed files with 290 additions and 331 deletions

View File

@@ -20,11 +20,11 @@ public final class TerminalScreen extends Screen {
private static final ResourceLocation BACKGROUND = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal.png");
private static final ResourceLocation BACKGROUND_TERMINAL_FOCUSED = new ResourceLocation(API.MOD_ID, "textures/gui/screen/terminal_focused.png");
private static final int TEXTURE_SIZE = 512;
private static final int SCREEN_WIDTH = 8 + 80 * 9 / 2 + 8;
private static final int SCREEN_WIDTH = 8 + 80 * 8 / 2 + 8;
private static final int SCREEN_HEIGHT = 8 + 24 * 16 / 2 + 8;
private static final int TERMINAL_AREA_X = 8;
private static final int TERMINAL_AREA_Y = 8;
private static final int TERMINAL_AREA_WIDTH = 80 * 9 / 2;
private static final int TERMINAL_AREA_WIDTH = 80 * 8 / 2;
private static final int TERMINAL_AREA_HEIGHT = 24 * 16 / 2;
private final ComputerTileEntity tileEntity;

View File

@@ -6,7 +6,6 @@ import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
import li.cil.ceres.api.Serialized;
import li.cil.oc2.api.API;
import li.cil.oc2.client.render.font.MonospaceFontRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SimpleSound;
import net.minecraft.client.renderer.BufferBuilder;
@@ -29,18 +28,6 @@ import java.util.concurrent.atomic.AtomicInteger;
// Implements a couple of control sequences from here: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
@Serialized
public final class Terminal {
private static final int CHAR_WIDTH = 8;
private static final int CHAR_HEIGHT = 16;
private static final ResourceLocation LOCATION_FONT_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/font/terminus.png");
private static final int TEXTURE_RESOLUTION = 256;
private static final int TEXTURE_COLUMNS = 16;
private static final int TEXTURE_BOLD_SHIFT = TEXTURE_COLUMNS; // Bold chars are in right half of texture.
private static final float U_SIZE = CHAR_WIDTH / (float) TEXTURE_RESOLUTION;
private static final float V_SIZE = CHAR_HEIGHT / (float) TEXTURE_RESOLUTION;
private static final float U_STEP = CHAR_WIDTH / (float) TEXTURE_RESOLUTION;
private static final float V_STEP = CHAR_HEIGHT / (float) TEXTURE_RESOLUTION;
private static final int TAB_WIDTH = 4;
private static final int WIDTH = 80, HEIGHT = 24;
@@ -53,27 +40,8 @@ public final class Terminal {
private static final int COLOR_CYAN = 6;
private static final int COLOR_WHITE = 7;
private static final int[] COLORS = {
0x010101, // Black
0xEE3322, // Red
0x33DD44, // Green
0xFFCC11, // Yellow
0x1188EE, // Blue
0xDD33CC, // Magenta
0x22CCDD, // Cyan
0xEEEEEE, // White
};
private static final int[] DIM_COLORS = {
0x010101, // Black
0x772211, // Red
0x116622, // Green
0x886611, // Yellow
0x115588, // Blue
0x771177, // Magenta
0x116677, // Cyan
0x777777, // White
};
private static final int CHAR_WIDTH = 8;
private static final int CHAR_HEIGHT = 16;
private static final int COLOR_MASK = 0b111;
private static final int COLOR_FOREGROUND_SHIFT = 3;
@@ -112,29 +80,28 @@ public final class Terminal {
// Style info packed into one byte for compact storage
private byte style = DEFAULT_STYLE;
private final transient Object[] lines = new Object[HEIGHT]; // Cached vertex buffers for rendering, untyped for server.
// Rendering data for client
private final transient AtomicInteger dirty = new AtomicInteger(-1);
private transient Object lastMatrix; // Untyped for server.
private transient Object renderer;
public Terminal() {
clear();
}
public int getWidth() {
return WIDTH * MonospaceFontRenderer.INSTANCE.getCharWidth();
return WIDTH * CHAR_WIDTH;
}
public int getHeight() {
return HEIGHT * MonospaceFontRenderer.INSTANCE.getCharHeight();
return HEIGHT * CHAR_HEIGHT;
}
@OnlyIn(Dist.CLIENT)
public void render(final MatrixStack stack) {
renderBuffer(stack);
if (System.currentTimeMillis() % 1000 > 500) {
renderCursor(stack);
if (renderer == null) {
renderer = new Renderer(this);
}
((Renderer) renderer).render(dirty, stack);
}
public synchronized int readInput() {
@@ -433,141 +400,6 @@ public final class Terminal {
}
}
@OnlyIn(Dist.CLIENT)
private void renderBuffer(final MatrixStack stack) {
validateLineCache(stack);
GlStateManager.depthMask(false);
Minecraft.getInstance().getTextureManager().bindTexture(LOCATION_FONT_TEXTURE);
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
for (final Object line : lines) {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX);
buffer.setVertexState((BufferBuilder.State) line);
buffer.finishDrawing();
WorldVertexBufferUploader.draw(buffer);
}
GlStateManager.depthMask(true);
}
@OnlyIn(Dist.CLIENT)
private void validateLineCache(final MatrixStack stack) {
if (!Objects.equals(lastMatrix, stack.getLast().getMatrix())) {
lastMatrix = stack.getLast().getMatrix();
dirty.set(-1);
}
if (dirty.get() == 0) {
return;
}
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
final int mask = dirty.getAndSet(0);
for (int row = 0; row < lines.length; row++) {
if ((mask & (1 << row)) == 0) {
continue;
}
stack.push();
stack.translate(0, row * CHAR_HEIGHT, 0);
final Matrix4f matrix = stack.getLast().getMatrix();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX);
float tx = 0f;
for (int col = 0, index = row * WIDTH; col < WIDTH; col++, index++) {
final int character = this.buffer[index] & 0xFF;
final byte colors = this.colors[index];
final byte style = this.styles[index];
if ((style & STYLE_HIDDEN_MASK) != 0) continue;
final int x = character % TEXTURE_COLUMNS + ((style & STYLE_BOLD_MASK) != 0 ? TEXTURE_BOLD_SHIFT : 0);
final int y = character / TEXTURE_COLUMNS;
final float u = x * U_STEP;
final float v = y * V_STEP;
final int[] palette = (style & STYLE_DIM_MASK) != 0 ? DIM_COLORS : COLORS;
final int foreground = palette[(colors >> COLOR_FOREGROUND_SHIFT) & COLOR_MASK];
final int background = palette[colors & COLOR_MASK];
final int color = (style & STYLE_INVERT_MASK) != 0 ? background : foreground;
final float r = ((color >> 16) & 0xFF) / 255f;
final float g = ((color >> 8) & 0xFF) / 255f;
final float b = ((color) & 0xFF) / 255f;
if (isPrintableCharacter((char) character)) {
buffer.pos(matrix, tx, CHAR_HEIGHT, 0).color(r, g, b, 1).tex(u, v + V_SIZE).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT, 0).color(r, g, b, 1).tex(u + U_SIZE, v + V_SIZE).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, 0, 0).color(r, g, b, 1).tex(u + U_SIZE, v).endVertex();
buffer.pos(matrix, tx, 0, 0).color(r, g, b, 1).tex(u, v).endVertex();
}
if ((style & STYLE_UNDERLINE_MASK) != 0) {
final float ulu = (TEXTURE_RESOLUTION - 1) / (float) TEXTURE_RESOLUTION;
final float ulv = 1 / (float) TEXTURE_RESOLUTION;
buffer.pos(matrix, tx, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
}
tx += CHAR_WIDTH;
}
lines[row] = buffer.getVertexState();
buffer.finishDrawing();
buffer.discard();
stack.pop();
}
}
private static boolean isPrintableCharacter(final char ch) {
return ch == 0 ||
(ch > ' ' && ch <= '~') ||
ch >= 177;
}
@OnlyIn(Dist.CLIENT)
private void renderCursor(final MatrixStack stack) {
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) {
return;
}
GlStateManager.depthMask(false);
RenderSystem.disableTexture();
stack.push();
stack.translate(x * CHAR_WIDTH, y * CHAR_HEIGHT, 0);
final Matrix4f matrix = stack.getLast().getMatrix();
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
final int foreground = COLORS[COLOR_WHITE];
final float r = ((foreground >> 16) & 0xFF) / 255f;
final float g = ((foreground >> 8) & 0xFF) / 255f;
final float b = ((foreground) & 0xFF) / 255f;
buffer.pos(matrix, 0, CHAR_HEIGHT, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, CHAR_WIDTH, CHAR_HEIGHT, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, CHAR_WIDTH, 0, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, 0, 0, 0).color(r, g, b, 1).endVertex();
buffer.finishDrawing();
WorldVertexBufferUploader.draw(buffer);
stack.pop();
RenderSystem.enableTexture();
GlStateManager.depthMask(true);
}
private void setCursorPos(final int x, final int y) {
this.x = Math.max(0, Math.min(WIDTH - 1, x));
this.y = Math.max(0, Math.min(HEIGHT - 1, y));
@@ -627,7 +459,6 @@ public final class Terminal {
System.arraycopy(buffer, WIDTH, buffer, 0, buffer.length - WIDTH);
System.arraycopy(colors, WIDTH, colors, 0, colors.length - WIDTH);
System.arraycopy(styles, WIDTH, styles, 0, styles.length - WIDTH);
System.arraycopy(lines, 1, lines, 0, lines.length - 1);
Arrays.fill(buffer, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, (byte) ' ');
Arrays.fill(colors, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_COLORS);
Arrays.fill(styles, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_STYLE);
@@ -635,4 +466,189 @@ public final class Terminal {
// Offset is baked into buffers so we must rebuild them all.
dirty.set(-1);
}
@OnlyIn(Dist.CLIENT)
private static final class Renderer {
private static final ResourceLocation LOCATION_FONT_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/font/terminus.png");
private static final int TEXTURE_RESOLUTION = 256;
private static final float ONE_OVER_TEXTURE_RESOLUTION = 1.0f / (float) TEXTURE_RESOLUTION;
private static final int TEXTURE_COLUMNS = 16;
private static final int TEXTURE_BOLD_SHIFT = TEXTURE_COLUMNS; // Bold chars are in right half of texture.
private static final int[] COLORS = {
0x010101, // Black
0xEE3322, // Red
0x33DD44, // Green
0xFFCC11, // Yellow
0x1188EE, // Blue
0xDD33CC, // Magenta
0x22CCDD, // Cyan
0xEEEEEE, // White
};
private static final int[] DIM_COLORS = {
0x010101, // Black
0x772211, // Red
0x116622, // Green
0x886611, // Yellow
0x115588, // Blue
0x771177, // Magenta
0x116677, // Cyan
0x777777, // White
};
private final Terminal terminal;
private final Object[] lines = new Object[HEIGHT]; // Cached vertex buffers for rendering, untyped for server.
private Object lastMatrix; // Untyped for server.
public Renderer(final Terminal terminal) {
this.terminal = terminal;
}
public void render(final AtomicInteger dirty, final MatrixStack stack) {
validateLineCache(dirty, stack);
renderBuffer();
if (System.currentTimeMillis() % 1000 > 500) {
renderCursor(stack);
}
}
private void renderBuffer() {
GlStateManager.depthMask(false);
Minecraft.getInstance().getTextureManager().bindTexture(LOCATION_FONT_TEXTURE);
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
for (final Object line : lines) {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX);
buffer.setVertexState((BufferBuilder.State) line);
buffer.finishDrawing();
WorldVertexBufferUploader.draw(buffer);
}
GlStateManager.depthMask(true);
}
private void validateLineCache(final AtomicInteger dirty, final MatrixStack stack) {
if (!Objects.equals(lastMatrix, stack.getLast().getMatrix())) {
lastMatrix = stack.getLast().getMatrix();
dirty.set(-1);
}
if (dirty.get() == 0) {
return;
}
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
final int mask = dirty.getAndSet(0);
for (int row = 0; row < lines.length; row++) {
if ((mask & (1 << row)) == 0) {
continue;
}
stack.push();
stack.translate(0, row * CHAR_HEIGHT, 0);
final Matrix4f matrix = stack.getLast().getMatrix();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX);
float tx = 0f;
for (int col = 0, index = row * WIDTH; col < WIDTH; col++, index++) {
final int character = terminal.buffer[index] & 0xFF;
final byte colors = terminal.colors[index];
final byte style = terminal.styles[index];
if ((style & STYLE_HIDDEN_MASK) != 0) continue;
final int x = character % TEXTURE_COLUMNS + ((style & STYLE_BOLD_MASK) != 0 ? TEXTURE_BOLD_SHIFT : 0);
final int y = character / TEXTURE_COLUMNS;
// The +0.5f is to sample the center of our texture pixels. This counteracts small
// floating point inaccuracies in our texture coordinates introduced by varying
// render resolution. And since we use point sampling it doesn't matter where
// exactly inside a pixel we sample.
final float u0 = (x * CHAR_WIDTH + 0.5f) * ONE_OVER_TEXTURE_RESOLUTION;
final float u1 = ((x + 1) * CHAR_WIDTH + 0.5f) * ONE_OVER_TEXTURE_RESOLUTION;
final float v0 = (y * CHAR_HEIGHT + 0.5f) * ONE_OVER_TEXTURE_RESOLUTION;
final float v1 = ((y + 1) * CHAR_HEIGHT + 0.5f) * ONE_OVER_TEXTURE_RESOLUTION;
final int[] palette = (style & STYLE_DIM_MASK) != 0 ? DIM_COLORS : COLORS;
final int foreground = palette[(colors >> COLOR_FOREGROUND_SHIFT) & COLOR_MASK];
final int background = palette[colors & COLOR_MASK];
final int color = (style & STYLE_INVERT_MASK) != 0 ? background : foreground;
final float r = ((color >> 16) & 0xFF) / 255f;
final float g = ((color >> 8) & 0xFF) / 255f;
final float b = ((color) & 0xFF) / 255f;
if (isPrintableCharacter((char) character)) {
buffer.pos(matrix, tx, CHAR_HEIGHT, 0).color(r, g, b, 1).tex(u0, v1).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT, 0).color(r, g, b, 1).tex(u1, v1).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, 0, 0).color(r, g, b, 1).tex(u1, v0).endVertex();
buffer.pos(matrix, tx, 0, 0).color(r, g, b, 1).tex(u0, v0).endVertex();
}
if ((style & STYLE_UNDERLINE_MASK) != 0) {
final float ulu = (TEXTURE_RESOLUTION - 1) / (float) TEXTURE_RESOLUTION;
final float ulv = 1 / (float) TEXTURE_RESOLUTION;
buffer.pos(matrix, tx, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT - 3, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx + CHAR_WIDTH, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
buffer.pos(matrix, tx, CHAR_HEIGHT - 2, 0).color(r, g, b, 1).tex(ulu, ulv).endVertex();
}
tx += CHAR_WIDTH;
}
lines[row] = buffer.getVertexState();
buffer.finishDrawing();
buffer.discard();
stack.pop();
}
}
private void renderCursor(final MatrixStack stack) {
if (terminal.x < 0 || terminal.x >= WIDTH || terminal.y < 0 || terminal.y >= HEIGHT) {
return;
}
GlStateManager.depthMask(false);
RenderSystem.disableTexture();
stack.push();
stack.translate(terminal.x * CHAR_WIDTH, terminal.y * CHAR_HEIGHT, 0);
final Matrix4f matrix = stack.getLast().getMatrix();
final BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
final int foreground = COLORS[COLOR_WHITE];
final float r = ((foreground >> 16) & 0xFF) / 255f;
final float g = ((foreground >> 8) & 0xFF) / 255f;
final float b = ((foreground) & 0xFF) / 255f;
buffer.pos(matrix, 0, CHAR_HEIGHT, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, CHAR_WIDTH, CHAR_HEIGHT, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, CHAR_WIDTH, 0, 0).color(r, g, b, 1).endVertex();
buffer.pos(matrix, 0, 0, 0).color(r, g, b, 1).endVertex();
buffer.finishDrawing();
WorldVertexBufferUploader.draw(buffer);
stack.pop();
RenderSystem.enableTexture();
GlStateManager.depthMask(true);
}
private static boolean isPrintableCharacter(final char ch) {
return ch == 0 ||
(ch > ' ' && ch <= '~') ||
ch >= 177;
}
}
}

View File

@@ -1,42 +0,0 @@
package li.cil.oc2.client.render.font;
import net.minecraft.client.renderer.Matrix4f;
/**
* Base interface for font renderers.
*/
public interface FontRenderer {
/**
* Render the specified string.
*
* @param matrix transformation of the text to draw.
* @param value the string to render.
*/
void drawString(final Matrix4f matrix, final CharSequence value);
/**
* Render up to the specified amount of characters of the specified string.
* <p>
* This is intended as a convenience method for clamped-width rendering,
* avoiding additional string operations such as <tt>substring</tt>.
*
* @param matrix transformation of the text to draw.
* @param value the string to render.
* @param maxChars the maximum number of characters to render.
*/
void drawString(final Matrix4f matrix, final CharSequence value, final int maxChars);
/**
* Get the width of the characters drawn with the font renderer, in pixels.
*
* @return the width of the drawn characters.
*/
int getCharWidth();
/**
* Get the height of the characters drawn with the font renderer, in pixels.
*
* @return the height of the drawn characters.
*/
int getCharHeight();
}

View File

@@ -1,102 +0,0 @@
package li.cil.oc2.client.render.font;
import com.mojang.blaze3d.platform.GlStateManager;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import li.cil.oc2.api.API;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
public final class MonospaceFontRenderer implements FontRenderer {
public static final FontRenderer INSTANCE = new MonospaceFontRenderer();
private static final ResourceLocation LOCATION_FONT_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/font/monospace.png");
private static final String CHARS = "\0\u263a\u263b\u2665\u2666\u2663\u2660\u2022\u25d8\u25cb\u25d9\u2642\u2640\u266a\u266b\u263c\u25ba\u25c4\u2195\u203c\u00b6\u00a7\u25ac\u21a8\u2191\u2193\u2192\u2190\u221f\u2194\u25b2\u25bc !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00a2\u00a3\u00a5\u20a7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u2310\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u00df\u0393\u03c0\u03a3\u03c3\u00b5\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0";
private static final int TEXTURE_RESOLUTION = 256;
private final Int2IntMap CHAR_MAP;
private final int COLUMNS = TEXTURE_RESOLUTION / getCharWidth();
private final float U_SIZE = getCharWidth() / (float) TEXTURE_RESOLUTION;
private final float V_SIZE = getCharHeight() / (float) TEXTURE_RESOLUTION;
private final float U_STEP = getCharWidth() / (float) TEXTURE_RESOLUTION;
private final float V_STEP = getCharHeight() / (float) TEXTURE_RESOLUTION;
private MonospaceFontRenderer() {
CHAR_MAP = new Int2IntOpenHashMap();
final CharSequence chars = CHARS;
for (int index = 0; index < chars.length(); index++) {
CHAR_MAP.put(chars.charAt(index), index);
}
}
public void drawString(final Matrix4f matrix, final CharSequence value) {
drawString(matrix, value, value.length());
}
public void drawString(final Matrix4f matrix, final CharSequence value, final int maxChars) {
GlStateManager.depthMask(false);
Minecraft.getInstance().getTextureManager().bindTexture(LOCATION_FONT_TEXTURE);
final Tessellator tessellator = Tessellator.getInstance();
final BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
float tx = 0f;
final int end = Math.min(maxChars, value.length());
for (int i = 0; i < end; i++) {
final char ch = value.charAt(i);
drawChar(matrix, tx, ch, buffer);
tx += getCharWidth();
}
tessellator.draw();
GlStateManager.depthMask(true);
}
// --------------------------------------------------------------------- //
// FontRenderer
@Override
public int getCharWidth() {
return 9;
}
@Override
public int getCharHeight() {
return 16;
}
// --------------------------------------------------------------------- //
private void drawChar(final Matrix4f matrix, final float x, final char ch, final BufferBuilder buffer) {
if (Character.isWhitespace(ch) || Character.isISOControl(ch)) {
return;
}
final int index = getCharIndex(ch);
final int column = index % COLUMNS;
final int row = index / COLUMNS;
final float u = column * U_STEP;
final float v = row * V_STEP;
buffer.pos(matrix, x, getCharHeight(), 0).tex(u, v + V_SIZE).endVertex();
buffer.pos(matrix, x + getCharWidth(), getCharHeight(), 0).tex(u + U_SIZE, v + V_SIZE).endVertex();
buffer.pos(matrix, x + getCharWidth(), 0, 0).tex(u + U_SIZE, v).endVertex();
buffer.pos(matrix, x, 0, 0).tex(u, v).endVertex();
}
private int getCharIndex(final char ch) {
if (!CHAR_MAP.containsKey(ch)) {
return CHAR_MAP.get('?');
}
return CHAR_MAP.get(ch);
}
}

View File

@@ -1,7 +0,0 @@
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package li.cil.oc2.client.render.font;
import mcp.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -0,0 +1,94 @@
Copyright (c) 2019 Dimitar Toshkov Zhekov,
with Reserved Font Name "Terminus Font".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB