Skip to content

fix font render with Angelica 2.1.x#184

Open
PinkYuDeer wants to merge 6 commits intoGTNewHorizons:masterfrom
PinkYuDeer:master
Open

fix font render with Angelica 2.1.x#184
PinkYuDeer wants to merge 6 commits intoGTNewHorizons:masterfrom
PinkYuDeer:master

Conversation

@PinkYuDeer
Copy link
Copy Markdown

@PinkYuDeer PinkYuDeer commented Apr 12, 2026

Summary

Fixes garbled text on OpenComputers screens (and in Drone/Robot GUIs, manual code segments) when Angelica 2.x is installed. Replaces the legacy immediate-mode + display-list text rendering path with Tessellator, which is compatible with Angelica's modernized pipeline.
Solve GTNewHorizons/Angelica#1661

Problem

After installing Angelica 2.0+, any text drawn on the OC screen that contains Chinese or other characters but is not displayed in CP437 encoding may appear as garbled characters. The "wrong" characters visibly come from the CP437 / basic ASCII set (the same set stored in assets/opencomputers/textures/font/chars.txt), so the symptom looks like UVs are being rebound to random cells of the font atlas. Uninstalling Angelica, or rolling back to Angelica < 2.0, makes the problem disappear.

Root cause

The screen text renderer relied on three legacy GL paths that Angelica 2.x no longer supports correctly:

  1. Immediate modeTextureFontRenderer.drawBuffer / drawString and their backends (StaticFontRenderer.drawChar, DynamicFontRenderer.CharIcon.draw) used glBegin(GL_QUADS) with per-vertex glTexCoord2d / glColor3ub / glVertex3d. Under Angelica's shader-based pipeline these per-vertex attributes are no longer forwarded through gl_TexCoord / gl_Color, so UVs end up undefined or stuck on a single atlas cell.

  2. Display listsTextBufferRenderCache wrapped each per-screen draw in glNewList(list, GL_COMPILE_AND_EXECUTE) / glCallList(list) and reused the compiled list on subsequent frames. Angelica does not capture/replay legacy vertex attribute state through display lists, so non-dirty frames replay a partially-correct state machine.

  3. Raw glBindTexture for dynamically-generated atlasesDynamicFontRenderer.CharTexture bypasses Minecraft's TextureManager and binds atlas textures directly via glGenTextures / glBindTexture. On its own this is fine, but combined with (1) and (2) it amplifies the failure: the texture is bound but nothing correctly samples it.

Because DynamicFontRenderer.initialize() pre-populates the first atlas with basicChars (the CP437 set, identical to chars.txt), the corrupted UVs sample glyphs from that atlas — which is why the garbled output visibly looks like chars.txt characters.

Fix

Port the entire screen-text rendering path from immediate mode + display lists to Tessellator, which is the same vertex-buffer path used by vanilla TESRs and block rendering, and which Angelica must (and does) support.

Changes

File Change
client/renderer/font/TextureFontRenderer.scala drawBuffer, drawString, and drawQuad rewritten to use Tessellator.startDrawingQuads() / addVertex(WithUV) / setColorOpaque_I / draw(). Added a drawString(s, x, y, color) overload so callers can pass a color through the vertex stream instead of relying on external glColor4f state.
client/renderer/font/StaticFontRenderer.scala drawChar now emits via Tessellator.addVertexWithUV.
client/renderer/font/DynamicFontRenderer.scala CharIcon.draw now emits via Tessellator.addVertexWithUV.
client/renderer/TextBufferRenderCache.scala Display-list cache removed (the Callable / RemovalListener / Guava cache / @SubscribeEvent onTick are all gone). Each render() call now: (a) generates missing glyphs if the buffer is dirty, then (b) calls renderer.drawBuffer directly every frame. The dirty flag still guards glyph generation (the only actually expensive step — GPU texture uploads).
client/Proxy.scala Drops the now-obsolete FMLCommonHandler.instance.bus.register(TextBufferRenderCache).
client/renderer/markdown/segment/CodeSegment.scala Replaces the external glColor4f(0.75f, 0.8f, 1, 1) with a color argument passed through the new drawString overload, so the tint survives Angelica's pipeline.

DynamicFontRenderer.CharTexture's raw glGenTextures / glBindTexture calls are kept as-is: once drawing goes through Tessellator, the texture that is currently bound to GL_TEXTURE_2D at draw() time is sampled correctly, the same way every other TESR in the game works.

Performance notes

The previous code cached a compiled display list per TextBufferRenderData and replayed it on non-dirty frames. That cache is gone because it was the display list — the exact construct Angelica breaks — so it could not be kept.

What remains behind the dirty flag is the expensive part: glyph generation (generateChars), which uploads new glyphs to the GPU atlas via glTexSubImage2D. On clean frames we now re-walk the buffer and re-feed Tessellator instead of replaying a compiled list. For a worst-case T3 screen (160x50 = 8000 cells) this is ~32k vertices per frame — well within normal TESR budgets, and negligible compared to a single vanilla chunk batch.

If this later shows up in profiles, two Angelica-safe caching strategies are available as follow-ups (not included here to keep the fix minimal and data-driven):

  1. Snapshot Tessellator's raw vertex ByteBuffer on dirty frames and re-upload it on clean frames (saves the Scala loop + per-vertex method calls).
  2. A per-screen VBO that is re-uploaded on dirty and drawn via glDrawArrays every frame (requires reintroducing resource-lifetime management similar to the old RemovalListener).

Scope / Non-goals

  • client/renderer/gui/BufferRenderer.scala still uses a display list + immediate mode to draw the GUI border frame around screens. It's a separate code path that does not exhibit the reported garbling (no per-char UV mapping, no per-quad color changes, and it's static), so it is intentionally left alone for this PR. It can be migrated in a follow-up if needed.
  • No API/behavior changes outside rendering. No settings, no content, no Lua-visible changes.

Testing

  • ./gradlew compileScala — BUILD SUCCESSFUL.
  • Manually verified in a dev client with Angelica 2.x: text on tier-1/2/3 screens renders correctly; GUI screen text renders correctly; Drone and Robot GUI buffers render correctly; manual code segments (CodeSegment) render in the expected light-blue tint.
  • Verified without Angelica that the renamed code path still produces identical output to the previous version.
  • It has been fully tested. In the LuV base of GTNH Daily 449, three instances of OC automation were used.

@dmarcuse
Copy link
Copy Markdown

It's hard to tell whether this is related based on the textual description alone, but I've been playing daily 444 and ran into an issue where the bottom line of text on all screens (both GUI and in-world) is blocky, like this example where I just held down the enter key for a bit:
image
I've tinkered with client configs a bit to no avail (font AA settings in angelica, texture- vs unifont-based rendering in OC, etc) - do you think this bug is related to this PR? If so, I can build this branch and see if it's fixed

@PinkYuDeer
Copy link
Copy Markdown
Author

PinkYuDeer commented Apr 18, 2026

do you think this bug is related to this PR?

Yes. Fixed. I create a release here

image

@guneykabel guneykabel self-assigned this Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants