mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-08 14:50:51 -04:00
Some preparatory changes for #41.
This commit is contained in:
parent
20a7c9c83f
commit
e6154c6ab7
@ -101,7 +101,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
|
||||
|
||||
def getLabel = "OpenComputers.TextBufferRenderer"
|
||||
|
||||
def ticks() = util.EnumSet.of(TickType.CLIENT)
|
||||
def ticks = util.EnumSet.of(TickType.CLIENT)
|
||||
|
||||
def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) = cache.cleanUp()
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import li.cil.oc.util.FontUtil
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
||||
class DataCharRenderer extends DynamicCharRenderer {
|
||||
val parser: FontParser = ???
|
||||
|
||||
override def charWidth: Double = parser.getGlyphWidth
|
||||
|
||||
override def charHeight: Double = parser.getGlyphHeight
|
||||
|
||||
override def drawChar(charCode: Int) {
|
||||
val w = parser.getGlyphWidth * FontUtil.wcwidth(charCode)
|
||||
val h = parser.getGlyphHeight
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||
GL11.glColor4f(1, 1, 1, 1)
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
|
||||
val texture = GL11.glGenTextures()
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture)
|
||||
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, w, h, 0, GL11.GL_RGBA, GL11.GL_BYTE, parser.getGlyph(charCode))
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST)
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST)
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS)
|
||||
GL11.glTexCoord2f(0, 1)
|
||||
GL11.glVertex2f(0, h)
|
||||
GL11.glTexCoord2f(1, 1)
|
||||
GL11.glVertex2f(w, h)
|
||||
GL11.glTexCoord2f(1, 0)
|
||||
GL11.glVertex2f(w, 0)
|
||||
GL11.glTexCoord2f(0, 0)
|
||||
GL11.glVertex2f(0, 0)
|
||||
GL11.glEnd()
|
||||
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0)
|
||||
GL11.glDeleteTextures(texture)
|
||||
|
||||
GL11.glPopAttrib()
|
||||
}
|
||||
}
|
@ -1,125 +1,9 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import java.awt.Font
|
||||
import java.awt.font.FontRenderContext
|
||||
import java.awt.geom.{PathIterator, Point2D}
|
||||
trait DynamicCharRenderer {
|
||||
def charWidth: Double
|
||||
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.lwjgl.util.glu.{GLU, GLUtessellatorCallbackAdapter}
|
||||
def charHeight: Double
|
||||
|
||||
class DynamicCharRenderer(val font: Font) {
|
||||
private val context = new FontRenderContext(font.getTransform, true, true)
|
||||
private val callback = new DynamicCharRenderer.Callback()
|
||||
private val maxCharBounds = font.getMaxCharBounds(context)
|
||||
|
||||
def charWidth = maxCharBounds.getWidth.toFloat
|
||||
|
||||
def charHeight = maxCharBounds.getHeight.toFloat
|
||||
|
||||
def drawChar(char: Char) {
|
||||
GL11.glPushMatrix()
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||
GL11.glTranslated(-maxCharBounds.getX, -maxCharBounds.getY, 0)
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D)
|
||||
GL11.glColor4f(1, 1, 1, 1)
|
||||
|
||||
val vector = font.createGlyphVector(context, Array(char))
|
||||
|
||||
val tess = GLU.gluNewTess()
|
||||
tess.gluTessCallback(GLU.GLU_TESS_BEGIN, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_END, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_VERTEX, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_EDGE_FLAG, callback)
|
||||
tess.gluTessNormal(0, 0, -1)
|
||||
|
||||
for (i <- 0 until vector.getNumGlyphs) {
|
||||
val outline = vector.getGlyphOutline(i).getPathIterator(null)
|
||||
tess.gluTessBeginPolygon(null)
|
||||
val current = new Point2D.Double(0, 0)
|
||||
val coords = new Array[Double](6)
|
||||
while (!outline.isDone) {
|
||||
if (outline.getWindingRule == PathIterator.WIND_EVEN_ODD)
|
||||
tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD)
|
||||
else
|
||||
tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO)
|
||||
outline.currentSegment(coords) match {
|
||||
case PathIterator.SEG_MOVETO =>
|
||||
tess.gluTessBeginContour()
|
||||
current.setLocation(coords(0), coords(1))
|
||||
case PathIterator.SEG_LINETO =>
|
||||
val buffer = Array[Float](coords(0).toFloat, coords(1).toFloat)
|
||||
tess.gluTessVertex(coords, 0, buffer)
|
||||
current.setLocation(coords(0), coords(1))
|
||||
case PathIterator.SEG_QUADTO =>
|
||||
// From SEG_QUADTO:
|
||||
// P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
|
||||
// with 0 <= t <= 1
|
||||
// B(n,m) = mth coefficient of nth degree Bernstein polynomial
|
||||
// = C(n,m) * t^(m) * (1 - t)^(n-m)
|
||||
// C(n,m) = Combinations of n things, taken m at a time
|
||||
// = n! / (m! * (n-m)!)
|
||||
// So:
|
||||
// P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
|
||||
// = C(2,0) * t^(0) * (1 - t)^(2-0) * CP +
|
||||
// C(2,1) * t^(1) * (1 - t)^(2-1) * P1 +
|
||||
// C(2,2) * t^(2) * (1 - t)^(2-2) * P2
|
||||
// = 2! / (0! * (2-0)!) * (1 - t)^2 * CP +
|
||||
// 2! / (1! * (2-1)!) * t * (1 - t) * P1 +
|
||||
// 2! / (2! * (2-2)!) * t^2 * P2
|
||||
// = (1 - t)^2 * CP +
|
||||
// 2 * t * (1 - t) * P1 +
|
||||
// t^2 * P2
|
||||
// = (1 - 2*t + t^2) * CP +
|
||||
// 2 * (t - t^2) * P1 +
|
||||
// t^2 * P2
|
||||
val interpolated = new Array[Double](3)
|
||||
def p(t: Double) = {
|
||||
val tSquared = t * t
|
||||
val fc = 1 - 2 * t + tSquared
|
||||
val f1 = 2 * (t - tSquared)
|
||||
val f2 = tSquared
|
||||
interpolated(0) = fc * current.x + f1 * coords(0) + f2 * coords(2)
|
||||
interpolated(1) = fc * current.y + f1 * coords(1) + f2 * coords(3)
|
||||
val buffer = Array[Float](interpolated(0).toFloat, interpolated(1).toFloat)
|
||||
tess.gluTessVertex(interpolated, 0, buffer)
|
||||
}
|
||||
for (t <- 0.0 until 1.0 by 0.25) {
|
||||
p(t)
|
||||
}
|
||||
current.setLocation(coords(2), coords(3))
|
||||
case PathIterator.SEG_CUBICTO =>
|
||||
// Not supported.
|
||||
case PathIterator.SEG_CLOSE =>
|
||||
tess.gluTessEndContour()
|
||||
case _ => // Wuh?
|
||||
}
|
||||
outline.next()
|
||||
}
|
||||
tess.gluTessEndPolygon()
|
||||
}
|
||||
|
||||
tess.gluDeleteTess()
|
||||
|
||||
GL11.glPopAttrib()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
def drawChar(charCode: Int)
|
||||
}
|
||||
|
||||
object DynamicCharRenderer {
|
||||
|
||||
private class Callback extends GLUtessellatorCallbackAdapter {
|
||||
override def begin(mode: Int) = GL11.glBegin(mode)
|
||||
|
||||
override def end() = GL11.glEnd()
|
||||
|
||||
override def vertex(coords: scala.Any) {
|
||||
val point = coords.asInstanceOf[Array[Float]]
|
||||
GL11.glVertex2f(point(0), point(1))
|
||||
}
|
||||
|
||||
override def edgeFlag(boundaryEdge: Boolean) {
|
||||
GL11.glEdgeFlag(boundaryEdge)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@ import java.awt.Font
|
||||
import java.io.InputStream
|
||||
|
||||
import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture
|
||||
import li.cil.oc.util.RenderState
|
||||
import li.cil.oc.util.{FontUtil, RenderState}
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.opengl._
|
||||
|
||||
@ -19,7 +19,7 @@ class DynamicFontRenderer(val font: Font) extends TextureFontRenderer {
|
||||
|
||||
def this(stream: InputStream, size: Int) = this(Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(size))
|
||||
|
||||
private val charRenderer = new DynamicCharRenderer(font)
|
||||
private val charRenderer: DynamicCharRenderer = new FontCharRenderer(font)
|
||||
|
||||
private val textures = mutable.ArrayBuffer(new DynamicFontRenderer.CharTexture(this))
|
||||
|
||||
@ -63,14 +63,7 @@ class DynamicFontRenderer(val font: Font) extends TextureFontRenderer {
|
||||
override protected def drawChar(tx: Float, ty: Float, char: Char) {
|
||||
val icon = charMap(char)
|
||||
if (icon.texture == activeTexture) {
|
||||
GL11.glTexCoord2f(icon.u1, icon.v2)
|
||||
GL11.glVertex2f(tx, ty + charHeight)
|
||||
GL11.glTexCoord2f(icon.u2, icon.v2)
|
||||
GL11.glVertex2f(tx + charWidth, ty + charHeight)
|
||||
GL11.glTexCoord2f(icon.u2, icon.v1)
|
||||
GL11.glVertex2f(tx + charWidth, ty)
|
||||
GL11.glTexCoord2f(icon.u1, icon.v1)
|
||||
GL11.glVertex2f(tx, ty)
|
||||
icon.draw(tx, ty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,8 +113,11 @@ object DynamicFontRenderer {
|
||||
def isFull = chars >= capacity
|
||||
|
||||
def add(char: Char) = {
|
||||
// TODO force to next row if wide char and won't fit into row.
|
||||
val x = chars % cols
|
||||
val y = chars / cols
|
||||
val w = owner.charWidth * FontUtil.wcwidth(char)
|
||||
val h = owner.charHeight
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST)
|
||||
GL11.glDepthMask(false)
|
||||
@ -131,13 +127,13 @@ object DynamicFontRenderer {
|
||||
GL20.glDrawBuffers(GL30.GL_COLOR_ATTACHMENT0)
|
||||
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT)
|
||||
|
||||
GL11.glViewport(0, 0, owner.charWidth, owner.charHeight)
|
||||
GL11.glViewport(0, 0, w, h)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION)
|
||||
GL11.glPushMatrix()
|
||||
GL11.glLoadIdentity()
|
||||
|
||||
GL11.glOrtho(0, owner.charWidth, owner.charHeight, 0, 0, 1)
|
||||
GL11.glOrtho(0, w, h, 0, 0, 1)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_MODELVIEW)
|
||||
GL11.glPushMatrix()
|
||||
@ -146,7 +142,7 @@ object DynamicFontRenderer {
|
||||
|
||||
owner.charRenderer.drawChar(char)
|
||||
|
||||
GL43.glCopyImageSubData(owner.rbo, GL30.GL_RENDERBUFFER, 0, 0, 0, 0, id, GL11.GL_TEXTURE_2D, 0, 1 + x * cellWidth, 1 + y * cellHeight, 0, owner.charWidth, owner.charHeight, 1)
|
||||
GL43.glCopyImageSubData(owner.rbo, GL30.GL_RENDERBUFFER, 0, 0, 0, 0, id, GL11.GL_TEXTURE_2D, 0, 1 + x * cellWidth, 1 + y * cellHeight, 0, w, h, 1)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION)
|
||||
GL11.glPopMatrix()
|
||||
@ -155,12 +151,23 @@ object DynamicFontRenderer {
|
||||
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
||||
|
||||
chars += 1
|
||||
chars += FontUtil.wcwidth(char)
|
||||
|
||||
new CharIcon(this, pad + x * uStep, pad + y * vStep, (x + 1) * uStep - 2 * pad, (y + 1) * vStep - 2 * pad)
|
||||
new CharIcon(this, w, h, pad + x * uStep, pad + y * vStep, (x + 1) * uStep - 2 * pad, (y + 1) * vStep - 2 * pad)
|
||||
}
|
||||
}
|
||||
|
||||
class CharIcon(val texture: CharTexture, val u1: Float, val v1: Float, val u2: Float, val v2: Float)
|
||||
class CharIcon(val texture: CharTexture, val w: Float, val h: Float, val u1: Float, val v1: Float, val u2: Float, val v2: Float) {
|
||||
def draw(tx: Float, ty: Float) {
|
||||
GL11.glTexCoord2f(u1, v1)
|
||||
GL11.glVertex2f(tx, ty + h)
|
||||
GL11.glTexCoord2f(u2, v1)
|
||||
GL11.glVertex2f(tx + w, ty + h)
|
||||
GL11.glTexCoord2f(u2, v2)
|
||||
GL11.glVertex2f(tx + w, ty)
|
||||
GL11.glTexCoord2f(u1, v2)
|
||||
GL11.glVertex2f(tx, ty)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import java.awt.Font
|
||||
import java.awt.font.FontRenderContext
|
||||
import java.awt.geom.{PathIterator, Point2D}
|
||||
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.lwjgl.util.glu.{GLU, GLUtessellatorCallbackAdapter}
|
||||
|
||||
class FontCharRenderer(val font: Font) extends DynamicCharRenderer {
|
||||
private val context = new FontRenderContext(font.getTransform, true, true)
|
||||
private val callback = new FontCharRenderer.Callback()
|
||||
private val maxCharBounds = font.getMaxCharBounds(context)
|
||||
|
||||
def charWidth = maxCharBounds.getWidth
|
||||
|
||||
def charHeight = maxCharBounds.getHeight
|
||||
|
||||
def drawChar(charCode: Int) {
|
||||
GL11.glPushMatrix()
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||
GL11.glTranslated(-maxCharBounds.getX, -maxCharBounds.getY, 0)
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D)
|
||||
GL11.glColor4f(1, 1, 1, 1)
|
||||
|
||||
val vector = font.createGlyphVector(context, Array(charCode.toChar))
|
||||
|
||||
val tess = GLU.gluNewTess()
|
||||
tess.gluTessCallback(GLU.GLU_TESS_BEGIN, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_END, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_VERTEX, callback)
|
||||
tess.gluTessCallback(GLU.GLU_TESS_EDGE_FLAG, callback)
|
||||
tess.gluTessNormal(0, 0, -1)
|
||||
|
||||
for (i <- 0 until vector.getNumGlyphs) {
|
||||
val outline = vector.getGlyphOutline(i).getPathIterator(null)
|
||||
tess.gluTessBeginPolygon(null)
|
||||
val current = new Point2D.Double(0, 0)
|
||||
val coords = new Array[Double](6)
|
||||
while (!outline.isDone) {
|
||||
if (outline.getWindingRule == PathIterator.WIND_EVEN_ODD)
|
||||
tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD)
|
||||
else
|
||||
tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO)
|
||||
outline.currentSegment(coords) match {
|
||||
case PathIterator.SEG_MOVETO =>
|
||||
tess.gluTessBeginContour()
|
||||
current.setLocation(coords(0), coords(1))
|
||||
case PathIterator.SEG_LINETO =>
|
||||
val buffer = Array[Float](coords(0).toFloat, coords(1).toFloat)
|
||||
tess.gluTessVertex(coords, 0, buffer)
|
||||
current.setLocation(coords(0), coords(1))
|
||||
case PathIterator.SEG_QUADTO =>
|
||||
// From SEG_QUADTO:
|
||||
// P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
|
||||
// with 0 <= t <= 1
|
||||
// B(n,m) = mth coefficient of nth degree Bernstein polynomial
|
||||
// = C(n,m) * t^(m) * (1 - t)^(n-m)
|
||||
// C(n,m) = Combinations of n things, taken m at a time
|
||||
// = n! / (m! * (n-m)!)
|
||||
// So:
|
||||
// P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
|
||||
// = C(2,0) * t^(0) * (1 - t)^(2-0) * CP +
|
||||
// C(2,1) * t^(1) * (1 - t)^(2-1) * P1 +
|
||||
// C(2,2) * t^(2) * (1 - t)^(2-2) * P2
|
||||
// = 2! / (0! * (2-0)!) * (1 - t)^2 * CP +
|
||||
// 2! / (1! * (2-1)!) * t * (1 - t) * P1 +
|
||||
// 2! / (2! * (2-2)!) * t^2 * P2
|
||||
// = (1 - t)^2 * CP +
|
||||
// 2 * t * (1 - t) * P1 +
|
||||
// t^2 * P2
|
||||
// = (1 - 2*t + t^2) * CP +
|
||||
// 2 * (t - t^2) * P1 +
|
||||
// t^2 * P2
|
||||
val interpolated = new Array[Double](3)
|
||||
def p(t: Double) = {
|
||||
val tSquared = t * t
|
||||
val fc = 1 - 2 * t + tSquared
|
||||
val f1 = 2 * (t - tSquared)
|
||||
val f2 = tSquared
|
||||
interpolated(0) = fc * current.x + f1 * coords(0) + f2 * coords(2)
|
||||
interpolated(1) = fc * current.y + f1 * coords(1) + f2 * coords(3)
|
||||
val buffer = Array[Float](interpolated(0).toFloat, interpolated(1).toFloat)
|
||||
tess.gluTessVertex(interpolated, 0, buffer)
|
||||
}
|
||||
for (t <- 0.0 until 1.0 by 0.25) {
|
||||
p(t)
|
||||
}
|
||||
current.setLocation(coords(2), coords(3))
|
||||
case PathIterator.SEG_CUBICTO =>
|
||||
// Not supported.
|
||||
case PathIterator.SEG_CLOSE =>
|
||||
tess.gluTessEndContour()
|
||||
case _ => // Wuh?
|
||||
}
|
||||
outline.next()
|
||||
}
|
||||
tess.gluTessEndPolygon()
|
||||
}
|
||||
|
||||
tess.gluDeleteTess()
|
||||
|
||||
GL11.glPopAttrib()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
}
|
||||
|
||||
object FontCharRenderer {
|
||||
|
||||
private class Callback extends GLUtessellatorCallbackAdapter {
|
||||
override def begin(mode: Int) = GL11.glBegin(mode)
|
||||
|
||||
override def end() = GL11.glEnd()
|
||||
|
||||
override def vertex(coords: scala.Any) {
|
||||
val point = coords.asInstanceOf[Array[Float]]
|
||||
GL11.glVertex2f(point(0), point(1))
|
||||
}
|
||||
|
||||
override def edgeFlag(boundaryEdge: Boolean) {
|
||||
GL11.glEdgeFlag(boundaryEdge)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
trait FontParser {
|
||||
def getGlyph(charCode: Int): ByteBuffer
|
||||
|
||||
def getGlyphWidth: Int
|
||||
|
||||
def getGlyphHeight: Int
|
||||
}
|
5
src/main/scala/li/cil/oc/util/FontUtil.scala
Normal file
5
src/main/scala/li/cil/oc/util/FontUtil.scala
Normal file
@ -0,0 +1,5 @@
|
||||
package li.cil.oc.util
|
||||
|
||||
object FontUtil {
|
||||
def wcwidth(charCode: Int) = 1 // TODO
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user