Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rewrite TextBuffer rendering logic, optimize, adopt VBOs #3138

Open
wants to merge 5 commits into
base: master-MC1.12
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ opencomputers {
# Wikipedia.)
textLinearFiltering: false

# Which text renderer to use for screens. Available options include:
# - auto: Let the mod decide on its own. Preferred.
# - vbo: New, VBO-based renderer.
# - displaylist: Old, display list-based renderer.
textRenderingEngine: "auto"

# If you prefer the text on the screens to be aliased (you know, *not*
# anti-aliased / smoothed) turn this option off.
textAntiAlias: true
Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/li/cil/oc/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ class Settings(val config: Config) {
val maxScreenTextRenderDistance = config.getDouble("client.maxScreenTextRenderDistance")
val textLinearFiltering = config.getBoolean("client.textLinearFiltering")
val textAntiAlias = config.getBoolean("client.textAntiAlias")
val textRenderingEngine = config.getString("client.textRenderingEngine")
val robotLabels = config.getBoolean("client.robotLabels")
val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 min 2
val fontCharScale = config.getDouble("client.fontCharScale") max 0.5 min 2
val soundVolume = config.getDouble("client.soundVolume").toFloat max 0.0f min 2.0f
val fontCharScale = config.getDouble("client.fontCharScale").toFloat max 0.5f min 2.0f
val hologramFadeStartDistance = config.getDouble("client.hologramFadeStartDistance") max 0
val hologramRenderDistance = config.getDouble("client.hologramRenderDistance") max 0
val hologramFlickerFrequency = config.getDouble("client.hologramFlickerFrequency") max 0
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/li/cil/oc/client/Proxy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import li.cil.oc.client
import li.cil.oc.client.renderer.HighlightRenderer
import li.cil.oc.client.renderer.MFUTargetRenderer
import li.cil.oc.client.renderer.PetRenderer
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer
import li.cil.oc.client.renderer.block.ModelInitialization
import li.cil.oc.client.renderer.block.NetSplitterModel
import li.cil.oc.client.renderer.entity.DroneRenderer
import li.cil.oc.client.renderer.textbuffer.TextBufferRenderCache
import li.cil.oc.client.renderer.tileentity._
import li.cil.oc.common.component.TextBuffer
import li.cil.oc.common.entity.Drone
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/li/cil/oc/client/gui/Drone.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package li.cil.oc.client.gui
import li.cil.oc.Localization
import li.cil.oc.client.Textures
import li.cil.oc.client.gui.widget.ProgressBar
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.font.TextBufferRenderData
import li.cil.oc.client.renderer.textbuffer.TextBufferRenderCache
import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common.container
import li.cil.oc.common.entity
Expand Down
10 changes: 5 additions & 5 deletions src/main/scala/li/cil/oc/client/gui/Robot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import li.cil.oc.api
import li.cil.oc.api.internal.TextBuffer
import li.cil.oc.client.Textures
import li.cil.oc.client.gui.widget.ProgressBar
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.gui.BufferRenderer
import li.cil.oc.client.renderer.textbuffer.TextBufferRenderCache
import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common.container
import li.cil.oc.common.tileentity
Expand Down Expand Up @@ -56,9 +56,9 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
private val maxBufferWidth = 240.0
private val maxBufferHeight = 140.0

private def bufferRenderWidth = math.min(maxBufferWidth, TextBufferRenderCache.renderer.charRenderWidth * Settings.screenResolutionsByTier(0)._1)
private def bufferRenderWidth = math.min(maxBufferWidth, TextBufferRenderCache.fontTextureProvider.getCharWidth / 2 * Settings.screenResolutionsByTier(0)._1)

private def bufferRenderHeight = math.min(maxBufferHeight, TextBufferRenderCache.renderer.charRenderHeight * Settings.screenResolutionsByTier(0)._2)
private def bufferRenderHeight = math.min(maxBufferHeight, TextBufferRenderCache.fontTextureProvider.getCharHeight / 2 * Settings.screenResolutionsByTier(0)._2)

override protected def bufferX: Int = (8 + (maxBufferWidth - bufferRenderWidth) / 2).toInt

Expand Down Expand Up @@ -245,8 +245,8 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
}

override protected def changeSize(w: Double, h: Double, recompile: Boolean): Double = {
val bw = w * TextBufferRenderCache.renderer.charRenderWidth
val bh = h * TextBufferRenderCache.renderer.charRenderHeight
val bw = w * TextBufferRenderCache.fontTextureProvider.getCharWidth / 2
val bh = h * TextBufferRenderCache.fontTextureProvider.getCharHeight / 2
val scaleX = math.min(bufferRenderWidth / bw, 1)
val scaleY = math.min(bufferRenderHeight / bh, 1)
if (recompile) {
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/li/cil/oc/client/gui/Screen.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package li.cil.oc.client.gui

import li.cil.oc.api
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.gui.BufferRenderer
import li.cil.oc.client.renderer.textbuffer.TextBufferRenderCache
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.GlStateManager
import org.lwjgl.input.Mouse
Expand Down Expand Up @@ -82,8 +82,8 @@ class Screen(val buffer: api.internal.TextBuffer, val hasMouse: Boolean, val has
}

private def toBufferCoordinates(mouseX: Int, mouseY: Int): Option[(Double, Double)] = {
val bx = (mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth
val by = (mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight
val bx = (mouseX - x - bufferMargin) / scale / (TextBufferRenderCache.fontTextureProvider.getCharWidth / 2)
val by = (mouseY - y - bufferMargin) / scale / (TextBufferRenderCache.fontTextureProvider.getCharHeight / 2)
val bw = buffer.getViewportWidth
val bh = buffer.getViewportHeight
if (bx >= 0 && by >= 0 && bx < bw && by < bh) Some((bx, by))
Expand Down
119 changes: 0 additions & 119 deletions src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package li.cil.oc.client.renderer.font;

public class CouldNotFitTextureException extends RuntimeException {
public CouldNotFitTextureException() {
super();
}

public CouldNotFitTextureException(String s) {
super(s);
}
}
76 changes: 35 additions & 41 deletions src/main/scala/li/cil/oc/client/renderer/font/FontParserHex.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import li.cil.oc.Settings;
import li.cil.oc.util.FontUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IResource;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.BufferUtils;

Expand All @@ -14,70 +15,63 @@
import java.nio.ByteBuffer;

public class FontParserHex implements IGlyphProvider {
private static final byte[] OPAQUE = {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
private static final byte[] TRANSPARENT = {0, 0, 0, 0};

private final byte[][] glyphs = new byte[FontUtils.codepoint_limit()][];

@Override
public void initialize() {
for (int i = 0; i < glyphs.length; ++i) {
glyphs[i] = null;
}
try {
final InputStream font = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(Settings.resourceDomain(), "font.hex")).getInputStream();
try {
OpenComputers.log().info("Initializing unicode glyph provider.");
final BufferedReader input = new BufferedReader(new InputStreamReader(font));
String line;
int glyphCount = 0;
while ((line = input.readLine()) != null) {
final String[] info = line.split(":");
final int charCode = Integer.parseInt(info[0], 16);
if (charCode < 0 || charCode >= glyphs.length) continue; // Out of bounds.
final int expectedWidth = FontUtils.wcwidth(charCode);
if (expectedWidth < 1) continue; // Skip control characters.
// Two chars representing one byte represent one row of eight pixels.
final byte[] glyph = new byte[info[1].length() >> 1];
final int glyphWidth = glyph.length / getGlyphHeight();
if (expectedWidth == glyphWidth) {
for (int i = 0; i < glyph.length; i++) {
glyph[i] = (byte) Integer.parseInt(info[1].substring(i * 2, i * 2 + 2), 16);
}
if (glyphs[charCode] == null) {
glyphCount++;
}
glyphs[charCode] = glyph;
} else if (Settings.get().logHexFontErrors()) {
OpenComputers.log().warn(String.format("Size of glyph for code point U+%04X (%s) in font (%d) does not match expected width (%d), ignoring.", charCode, String.valueOf((char) charCode), glyphWidth, expectedWidth));
ResourceLocation fontLocation = new ResourceLocation(Settings.resourceDomain(), "font.hex");
try (IResource fontResource = Minecraft.getMinecraft().getResourceManager().getResource(fontLocation); InputStream font = fontResource.getInputStream()){
OpenComputers.log().info("Initializing unicode glyph provider.");
final BufferedReader input = new BufferedReader(new InputStreamReader(font));
String line;
int glyphCount = 0;
while ((line = input.readLine()) != null) {
final String[] info = line.split(":");
final int charCode = Integer.parseInt(info[0], 16);
if (charCode < 0 || charCode >= glyphs.length) continue; // Out of bounds.
final int expectedWidth = FontUtils.wcwidth(charCode);
if (expectedWidth < 1) continue; // Skip control characters.
// Two chars representing one byte represent one row of eight pixels.
final byte[] glyph = new byte[info[1].length() >> 1];
final int glyphWidth = glyph.length / getGlyphHeight();
if (expectedWidth == glyphWidth) {
for (int i = 0; i < glyph.length; i++) {
glyph[i] = (byte) Integer.parseInt(info[1].substring(i * 2, i * 2 + 2), 16);
}
}
OpenComputers.log().info("Loaded " + glyphCount + " glyphs.");
} finally {
try {
font.close();
} catch (IOException ex) {
OpenComputers.log().warn("Error parsing font.", ex);
if (glyphs[charCode] == null) {
glyphCount++;
}
glyphs[charCode] = glyph;
} else if (Settings.get().logHexFontErrors()) {
OpenComputers.log().warn(String.format("Size of glyph for code point U+%04X (%s) in font (%d) does not match expected width (%d), ignoring.", charCode, String.valueOf((char) charCode), glyphWidth, expectedWidth));
}
}
OpenComputers.log().info("Loaded " + glyphCount + " glyphs.");
} catch (IOException ex) {
OpenComputers.log().warn("Failed loading glyphs.", ex);
}
}

@Override
public boolean containsGlyph(int charCode) {
return charCode >= 0 && charCode < glyphs.length && glyphs[charCode] != null && glyphs[charCode].length != 0;
}

@Override
public ByteBuffer getGlyph(int charCode) {
if (charCode < 0 || charCode >= glyphs.length || glyphs[charCode] == null || glyphs[charCode].length == 0)
if (!containsGlyph(charCode))
return null;
final byte[] glyph = glyphs[charCode];
final ByteBuffer buffer = BufferUtils.createByteBuffer(glyph.length * getGlyphWidth() * 4);
final ByteBuffer buffer = BufferUtils.createByteBuffer(glyph.length * getGlyphWidth());
for (byte aGlyph : glyph) {
int c = ((int) aGlyph) & 0xFF;
// Grab all bits by grabbing the leftmost one then shifting.
for (int j = 0; j < 8; j++) {
final boolean isBitSet = (c & 0x80) > 0;
if (isBitSet) buffer.put(OPAQUE);
else buffer.put(TRANSPARENT);
final boolean isBitSet = (c & 0x80) != 0;
buffer.put(isBitSet ? (byte)255 : 0);
c <<= 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package li.cil.oc.client.renderer.font;

public interface FontTextureProvider {
interface Receiver {
void draw(float x1, float x2, float y1, float y2, float u1, float u2, float v1, float v2);
}

int getCharWidth();
int getCharHeight();

boolean isDynamic();
int getTextureCount();
void begin(int texture);
void loadCodePoint(int codePoint);
void drawCodePoint(int codePoint, float tx, float ty, Receiver receiver);
void end(int texture);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/**
* Common interface for classes providing glyph data in a format that can be
* rendered using the {@link li.cil.oc.client.renderer.font.DynamicFontRenderer}.
* rendered using the {@link li.cil.oc.client.renderer.font.FontTextureProvider}.
*/
public interface IGlyphProvider {
/**
Expand All @@ -14,6 +14,8 @@ public interface IGlyphProvider {
*/
void initialize();

boolean containsGlyph(int charCode);

/**
* Get a byte array of RGBA data describing the specified char.
* <p/>
Expand Down
Loading