mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-24 04:50:30 -04:00
Merge branch 'master' of github.com:MightyPirates/OpenComputers into MC1.7
Conflicts: src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala
This commit is contained in:
commit
47d5d884cd
@ -66,14 +66,14 @@ hdd1 {
|
||||
["oc:circuitChip1", "oc:materialDisk", ingotIron]]
|
||||
}
|
||||
hdd2 {
|
||||
input: [[ingotGold, "oc:craftingHDDTier1", ingotGold]
|
||||
input: [[ingotGold, "oc:hdd1", ingotGold]
|
||||
["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"]
|
||||
[ingotGold, "oc:craftingHDDTier1", ingotGold]]
|
||||
[ingotGold, "oc:hdd1", ingotGold]]
|
||||
}
|
||||
hdd3 {
|
||||
input: [["oc:circuitChip3", "oc:craftingHDDTier2", "oc:circuitChip3"]
|
||||
input: [["oc:circuitChip3", "oc:hdd2", "oc:circuitChip3"]
|
||||
["oc:ram1", "oc:materialCircuitBoardPrinted", "oc:ram1"]
|
||||
["oc:circuitChip3", "oc:craftingHDDTier2", "oc:circuitChip3"]]
|
||||
["oc:circuitChip3", "oc:hdd2", "oc:circuitChip3"]]
|
||||
}
|
||||
|
||||
abstractBusCard {
|
||||
|
@ -1,2 +1,2 @@
|
||||
☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
||||
5 9
|
||||
10 18
|
@ -3,7 +3,7 @@ package li.cil.oc.client.gui
|
||||
import java.util
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.client.renderer.MonospaceFontRenderer
|
||||
import li.cil.oc.client.renderer.TextBufferRenderCache
|
||||
import li.cil.oc.client.renderer.gui.BufferRenderer
|
||||
import li.cil.oc.client.{PacketSender => ClientPacketSender, Textures}
|
||||
import li.cil.oc.common.container
|
||||
@ -261,8 +261,8 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
|
||||
}
|
||||
|
||||
override protected def changeSize(w: Double, h: Double, recompile: Boolean) = {
|
||||
val bw = w * MonospaceFontRenderer.fontWidth
|
||||
val bh = h * MonospaceFontRenderer.fontHeight
|
||||
val bw = w * TextBufferRenderCache.renderer.charRenderWidth
|
||||
val bh = h * TextBufferRenderCache.renderer.charRenderHeight
|
||||
val scaleX = math.min(bufferWidth / (bw + bufferMargin * 2.0), 1)
|
||||
val scaleY = math.min(bufferHeight / (bh + bufferMargin * 2.0), 1)
|
||||
math.min(scaleX, scaleY)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package li.cil.oc.client.gui
|
||||
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.client.renderer.MonospaceFontRenderer
|
||||
import li.cil.oc.client.renderer.TextBufferRenderCache
|
||||
import li.cil.oc.client.renderer.gui.BufferRenderer
|
||||
import li.cil.oc.util.RenderState
|
||||
import org.lwjgl.input.Mouse
|
||||
@ -27,8 +27,8 @@ class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val ha
|
||||
if (Mouse.hasWheel && Mouse.getEventDWheel != 0) {
|
||||
val mouseX = Mouse.getEventX * width / mc.displayWidth
|
||||
val mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1
|
||||
val bx = (mouseX - x - bufferMargin) / MonospaceFontRenderer.fontWidth + 1
|
||||
val by = (mouseY - y - bufferMargin) / MonospaceFontRenderer.fontHeight + 1
|
||||
val bx = (mouseX - x - bufferMargin) / TextBufferRenderCache.renderer.charRenderWidth + 1
|
||||
val by = (mouseY - y - bufferMargin) / TextBufferRenderCache.renderer.charRenderHeight + 1
|
||||
val bw = buffer.getWidth
|
||||
val bh = buffer.getHeight
|
||||
if (bx > 0 && by > 0 && bx <= bw && by <= bh) {
|
||||
@ -60,8 +60,8 @@ class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val ha
|
||||
super.mouseMovedOrUp(mouseX, mouseY, button)
|
||||
if (button >= 0) {
|
||||
if (didDrag) {
|
||||
val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1
|
||||
val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).toInt + 1
|
||||
val bx = ((mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth).toInt + 1
|
||||
val by = ((mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight).toInt + 1
|
||||
val bw = buffer.getWidth
|
||||
val bh = buffer.getHeight
|
||||
if (bx > 0 && by > 0 && bx <= bw && by <= bh) {
|
||||
@ -78,8 +78,8 @@ class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val ha
|
||||
}
|
||||
|
||||
private def clickOrDrag(mouseX: Int, mouseY: Int, button: Int) {
|
||||
val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1
|
||||
val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).toInt + 1
|
||||
val bx = ((mouseX - x - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderWidth).toInt + 1
|
||||
val by = ((mouseY - y - bufferMargin) / scale / TextBufferRenderCache.renderer.charRenderHeight).toInt + 1
|
||||
val bw = buffer.getWidth
|
||||
val bh = buffer.getHeight
|
||||
if (bx > 0 && by > 0 && bx <= bw && by <= bh) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package li.cil.oc.client.gui
|
||||
|
||||
import li.cil.oc.client.{Textures, KeyBindings}
|
||||
import li.cil.oc.client.renderer.MonospaceFontRenderer
|
||||
import li.cil.oc.client.renderer.gui.BufferRenderer
|
||||
import li.cil.oc.util.RenderState
|
||||
import li.cil.oc.util.mods.NEI
|
||||
@ -35,7 +34,6 @@ trait TextBuffer extends GuiScreen {
|
||||
|
||||
override def initGui() = {
|
||||
super.initGui()
|
||||
MonospaceFontRenderer.init(Minecraft.getMinecraft.renderEngine)
|
||||
BufferRenderer.init(Minecraft.getMinecraft.renderEngine)
|
||||
Keyboard.enableRepeatEvents(true)
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
package li.cil.oc.client.renderer
|
||||
|
||||
import org.apache.logging.log4j.Level
|
||||
import li.cil.oc.client.Textures
|
||||
import li.cil.oc.util.{RenderState, PackedColor}
|
||||
import li.cil.oc.{OpenComputers, Settings}
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.texture.TextureManager
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.lwjgl.opengl.GL11
|
||||
import scala.io.Source
|
||||
|
||||
// IMPORTANT: we must not use the tessellator here. Doing so can cause
|
||||
// crashes on certain graphics cards with certain drivers (reported for
|
||||
// ATI/AMD and Intel chip sets). These crashes have been reported to
|
||||
// happen I have no idea why, and can only guess that it's related to
|
||||
// using the VBO/ARB the tessellator uses inside a display list (since
|
||||
// this stuff is eventually only rendered via display lists).
|
||||
|
||||
object MonospaceFontRenderer {
|
||||
val (chars, fontWidth, fontHeight) = try {
|
||||
val lines = Source.fromInputStream(Minecraft.getMinecraft.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "textures/font/chars.txt")).getInputStream)("UTF-8").getLines()
|
||||
val chars = lines.next()
|
||||
val (w, h) = if (lines.hasNext) {
|
||||
val size = lines.next().split(" ", 2)
|
||||
(size(0).toInt, size(1).toInt)
|
||||
} else (5, 9)
|
||||
(chars, w, h)
|
||||
}
|
||||
catch {
|
||||
case t: Throwable =>
|
||||
OpenComputers.log.log(Level.WARN, "Failed reading font metadata, using defaults.", t)
|
||||
( """☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■""", 5, 9)
|
||||
}
|
||||
|
||||
private var instance: Option[Renderer] = None
|
||||
|
||||
def init(textureManager: TextureManager) = this.synchronized(
|
||||
instance = instance.orElse(Some(new Renderer(textureManager))))
|
||||
|
||||
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Short], format: PackedColor.ColorFormat) = this.synchronized(instance match {
|
||||
case None => OpenComputers.log.warn("Trying to render string with uninitialized MonospaceFontRenderer.")
|
||||
case Some(renderer) => renderer.drawString(x, y, value, color, format)
|
||||
})
|
||||
|
||||
private class Renderer(private val textureManager: TextureManager) {
|
||||
private val (charWidth, charHeight) = (MonospaceFontRenderer.fontWidth * 2, MonospaceFontRenderer.fontHeight * 2)
|
||||
private val cols = 256 / charWidth
|
||||
private val uStep = charWidth / 256.0
|
||||
private val uSize = uStep
|
||||
private val vStep = (charHeight + 1) / 256.0
|
||||
private val vSize = charHeight / 256.0
|
||||
private val s = Settings.get.fontCharScale
|
||||
private val dw = charWidth * s - charWidth
|
||||
private val dh = charHeight * s - charHeight
|
||||
|
||||
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Short], format: PackedColor.ColorFormat) = {
|
||||
if (color.length != value.length) throw new IllegalArgumentException("Color count must match char count.")
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawString: entering (aka: wasntme)")
|
||||
|
||||
if (Settings.get.textAntiAlias)
|
||||
textureManager.bindTexture(Textures.fontAntiAliased)
|
||||
else
|
||||
textureManager.bindTexture(Textures.fontAliased)
|
||||
GL11.glPushMatrix()
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||
|
||||
GL11.glTranslatef(x, y, 0)
|
||||
GL11.glScalef(0.5f, 0.5f, 1)
|
||||
GL11.glDepthMask(false)
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D)
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawString: configure state")
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS)
|
||||
// Background first. We try to merge adjacent backgrounds of the same
|
||||
// color to reduce the number of quads we have to draw.
|
||||
var cbg = 0x000000
|
||||
var offset = 0
|
||||
var width = 0
|
||||
for (col <- color.map(PackedColor.unpackBackground(_, format))) {
|
||||
if (col != cbg) {
|
||||
draw(cbg, offset, width)
|
||||
cbg = col
|
||||
offset += width
|
||||
width = 0
|
||||
}
|
||||
width = width + 1
|
||||
}
|
||||
draw(cbg, offset, width)
|
||||
GL11.glEnd()
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawString: background")
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D)
|
||||
|
||||
if (Settings.get.textLinearFiltering) {
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR)
|
||||
}
|
||||
|
||||
// Foreground second. We only have to flush when the color changes, so
|
||||
// unless every char has a different color this should be quite efficient.
|
||||
GL11.glBegin(GL11.GL_QUADS)
|
||||
var cfg = -1
|
||||
var posX = 0.0
|
||||
for ((ch, col) <- value.zip(color.map(PackedColor.unpackForeground(_, format)))) {
|
||||
val index = 1 + (chars.indexOf(ch) match {
|
||||
case -1 => chars.indexOf('?')
|
||||
case i => i
|
||||
})
|
||||
if (col != cfg) {
|
||||
// Color changed.
|
||||
cfg = col
|
||||
GL11.glColor3ub(
|
||||
((cfg & 0xFF0000) >> 16).toByte,
|
||||
((cfg & 0x00FF00) >> 8).toByte,
|
||||
((cfg & 0x0000FF) >> 0).toByte)
|
||||
}
|
||||
if (ch != ' ') {
|
||||
// Don't render whitespace.
|
||||
val x = (index - 1) % cols
|
||||
val y = (index - 1) / cols
|
||||
val u = x * uStep
|
||||
val v = y * vStep
|
||||
GL11.glTexCoord2d(u, v + vSize)
|
||||
GL11.glVertex3d(posX - dw, charHeight * s, 0)
|
||||
GL11.glTexCoord2d(u + uSize, v + vSize)
|
||||
GL11.glVertex3d(posX + charWidth * s, charHeight * s, 0)
|
||||
GL11.glTexCoord2d(u + uSize, v)
|
||||
GL11.glVertex3d(posX + charWidth * s, -dh, 0)
|
||||
GL11.glTexCoord2d(u, v)
|
||||
GL11.glVertex3d(posX - dw, -dh, 0)
|
||||
}
|
||||
posX += charWidth
|
||||
}
|
||||
GL11.glEnd()
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawString: foreground")
|
||||
|
||||
GL11.glPopAttrib()
|
||||
GL11.glPopMatrix()
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawString: leaving")
|
||||
}
|
||||
|
||||
private def draw(color: Int, offset: Int, width: Int) = if (color != 0 && width > 0) {
|
||||
GL11.glColor3ub(((color >> 16) & 0xFF).toByte, ((color >> 8) & 0xFF).toByte, (color & 0xFF).toByte)
|
||||
GL11.glVertex3d(charWidth * offset, charHeight, 0)
|
||||
GL11.glVertex3d(charWidth * (offset + width), charHeight, 0)
|
||||
GL11.glVertex3d(charWidth * (offset + width), 0, 0)
|
||||
GL11.glVertex3d(charWidth * offset, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,23 @@
|
||||
package li.cil.oc.client.renderer
|
||||
|
||||
import com.google.common.cache.{RemovalListener, RemovalNotification, CacheBuilder}
|
||||
import java.util.concurrent.{Callable, TimeUnit}
|
||||
|
||||
import com.google.common.cache.{CacheBuilder, RemovalListener, RemovalNotification}
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
|
||||
import li.cil.oc.client.renderer.font.StaticFontRenderer
|
||||
import li.cil.oc.common.component.TextBuffer
|
||||
import li.cil.oc.util.RenderState
|
||||
import net.minecraft.client.renderer.GLAllocation
|
||||
import net.minecraft.tileentity.TileEntity
|
||||
import org.lwjgl.opengl.GL11
|
||||
import net.minecraft.client.Minecraft
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
|
||||
|
||||
object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEntity, Int] {
|
||||
val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||
val renderer =
|
||||
new StaticFontRenderer()
|
||||
// new DynamicFontRenderer("Terminal")
|
||||
|
||||
private val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||
expireAfterAccess(2, TimeUnit.SECONDS).
|
||||
removalListener(this).
|
||||
asInstanceOf[CacheBuilder[TextBuffer, Int]].
|
||||
@ -26,7 +31,6 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def render(buffer: TextBuffer) {
|
||||
MonospaceFontRenderer.init(Minecraft.getMinecraft.getTextureManager)
|
||||
currentBuffer = buffer
|
||||
compileOrDraw(cache.get(currentBuffer, this))
|
||||
}
|
||||
@ -35,6 +39,10 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
|
||||
if (currentBuffer.proxy.dirty) {
|
||||
RenderState.checkError(getClass.getName + ".compileOrDraw: entering (aka: wasntme)")
|
||||
|
||||
for (line <- currentBuffer.data.buffer) {
|
||||
renderer.generateChars(line)
|
||||
}
|
||||
|
||||
val doCompile = !RenderState.compilingDisplayList
|
||||
if (doCompile) {
|
||||
currentBuffer.proxy.dirty = false
|
||||
@ -43,9 +51,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
|
||||
RenderState.checkError(getClass.getName + ".compileOrDraw: glNewList")
|
||||
}
|
||||
|
||||
for (((line, color), i) <- currentBuffer.data.buffer.zip(currentBuffer.data.color).zipWithIndex) {
|
||||
MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, currentBuffer.data.format)
|
||||
}
|
||||
renderer.drawBuffer(currentBuffer.data)
|
||||
|
||||
RenderState.checkError(getClass.getName + ".compileOrDraw: drawString")
|
||||
|
||||
|
@ -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 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()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import java.awt.Font
|
||||
import java.io.InputStream
|
||||
|
||||
import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture
|
||||
import li.cil.oc.util.RenderState
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.opengl._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* Font renderer that dynamically generates lookup textures by rendering a font
|
||||
* to it. It's pretty broken right now, and font rendering looks crappy as hell.
|
||||
*/
|
||||
class DynamicFontRenderer(val font: Font) extends TextureFontRenderer {
|
||||
def this(name: String) = this(new Font(name, Font.PLAIN, 11))
|
||||
|
||||
def this(stream: InputStream) = this(Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(11f))
|
||||
|
||||
private val charRenderer = new DynamicCharRenderer(font)
|
||||
|
||||
private val textures = mutable.ArrayBuffer(new DynamicFontRenderer.CharTexture(this))
|
||||
|
||||
private val charMap = mutable.Map.empty[Char, DynamicFontRenderer.CharIcon]
|
||||
|
||||
private val fbo = GL30.glGenFramebuffers()
|
||||
|
||||
private val rbo = GL30.glGenRenderbuffers()
|
||||
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo)
|
||||
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, rbo)
|
||||
|
||||
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL11.GL_RGBA8, charWidth, charHeight)
|
||||
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_RENDERBUFFER, rbo)
|
||||
|
||||
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0)
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
||||
|
||||
var activeTexture: CharTexture = textures(0)
|
||||
|
||||
generateChars(basicChars.toCharArray)
|
||||
|
||||
RenderState.checkError(getClass.getName + ".<init>: glGenFramebuffers")
|
||||
|
||||
override protected def charWidth = charRenderer.charWidth.toInt
|
||||
|
||||
override protected def charHeight = charRenderer.charHeight.toInt
|
||||
|
||||
override protected def textureCount = textures.length
|
||||
|
||||
override protected def bindTexture(index: Int) {
|
||||
activeTexture = textures(index)
|
||||
activeTexture.bind()
|
||||
RenderState.checkError(getClass.getName + ".bindTexture")
|
||||
}
|
||||
|
||||
override protected def generateChar(char: Char) {
|
||||
charMap.getOrElseUpdate(char, createCharIcon(char))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private def createCharIcon(char: Char): DynamicFontRenderer.CharIcon = {
|
||||
if (!font.canDisplay(char)) {
|
||||
if (char == '?') null
|
||||
else charMap.getOrElseUpdate('?', createCharIcon('?'))
|
||||
}
|
||||
else {
|
||||
if (textures.last.isFull) {
|
||||
textures += new DynamicFontRenderer.CharTexture(this)
|
||||
textures.last.bind()
|
||||
}
|
||||
textures.last.add(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DynamicFontRenderer {
|
||||
private val size = 256
|
||||
|
||||
class CharTexture(val owner: DynamicFontRenderer) {
|
||||
private val id = GL11.glGenTextures()
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id)
|
||||
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.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(size * size * 4))
|
||||
|
||||
RenderState.checkError(getClass.getName + ".<init>: create texture")
|
||||
|
||||
// Some padding to avoid bleeding.
|
||||
private val cellWidth = owner.charWidth + 2
|
||||
private val cellHeight = owner.charHeight + 2
|
||||
private val cols = size / cellWidth
|
||||
private val rows = size / cellHeight
|
||||
private val uStep = cellWidth / size.toFloat
|
||||
private val vStep = cellHeight / size.toFloat
|
||||
private val pad = 1f / size
|
||||
private val capacity = cols * rows
|
||||
|
||||
private var chars = 0
|
||||
|
||||
def bind() {
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, id)
|
||||
}
|
||||
|
||||
def isFull = chars >= capacity
|
||||
|
||||
def add(char: Char) = {
|
||||
val x = chars % cols
|
||||
val y = chars / cols
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST)
|
||||
GL11.glDepthMask(false)
|
||||
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, owner.fbo)
|
||||
GL11.glClearColor(0, 0, 0, 0)
|
||||
GL20.glDrawBuffers(GL30.GL_COLOR_ATTACHMENT0)
|
||||
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT)
|
||||
|
||||
GL11.glViewport(0, 0, owner.charWidth, owner.charHeight)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION)
|
||||
GL11.glPushMatrix()
|
||||
GL11.glLoadIdentity()
|
||||
|
||||
GL11.glOrtho(0, owner.charWidth, owner.charHeight, 0, 0, 1)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_MODELVIEW)
|
||||
GL11.glPushMatrix()
|
||||
GL11.glLoadIdentity()
|
||||
GL11.glTranslatef(0, 0, -0.5f)
|
||||
|
||||
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)
|
||||
|
||||
GL11.glMatrixMode(GL11.GL_PROJECTION)
|
||||
GL11.glPopMatrix()
|
||||
GL11.glMatrixMode(GL11.GL_MODELVIEW)
|
||||
GL11.glPopMatrix()
|
||||
|
||||
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0)
|
||||
|
||||
chars += 1
|
||||
|
||||
new CharIcon(this, 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)
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import li.cil.oc.client.Textures
|
||||
import li.cil.oc.{OpenComputers, Settings}
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
||||
import scala.io.Source
|
||||
|
||||
/**
|
||||
* Font renderer using a user specified texture file, meaning the list of
|
||||
* supported characters is fixed. But at least this one works.
|
||||
*/
|
||||
class StaticFontRenderer extends TextureFontRenderer {
|
||||
protected val (chars, charWidth, charHeight) = try {
|
||||
val lines = Source.fromInputStream(Minecraft.getMinecraft.getResourceManager.getResource(new ResourceLocation(Settings.resourceDomain, "textures/font/chars.txt")).getInputStream)("UTF-8").getLines()
|
||||
val chars = lines.next()
|
||||
val (w, h) = if (lines.hasNext) {
|
||||
val size = lines.next().split(" ", 2)
|
||||
(size(0).toInt, size(1).toInt)
|
||||
} else (10, 18)
|
||||
(chars, w, h)
|
||||
}
|
||||
catch {
|
||||
case t: Throwable =>
|
||||
OpenComputers.log.log(Level.WARN, "Failed reading font metadata, using defaults.", t)
|
||||
(basicChars, 10, 18)
|
||||
}
|
||||
|
||||
private val cols = 256 / charWidth
|
||||
private val uStep = charWidth / 256.0
|
||||
private val uSize = uStep
|
||||
private val vStep = (charHeight + 1) / 256.0
|
||||
private val vSize = charHeight / 256.0
|
||||
private val s = Settings.get.fontCharScale
|
||||
private val dw = charWidth * s - charWidth
|
||||
private val dh = charHeight * s - charHeight
|
||||
|
||||
override protected def textureCount = 1
|
||||
|
||||
override protected def bindTexture(index: Int) {
|
||||
if (Settings.get.textAntiAlias) {
|
||||
Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.fontAntiAliased)
|
||||
}
|
||||
else {
|
||||
Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.fontAliased)
|
||||
}
|
||||
}
|
||||
|
||||
override protected def drawChar(tx: Float, ty: Float, char: Char) {
|
||||
val index = 1 + (chars.indexOf(char) match {
|
||||
case -1 => chars.indexOf('?')
|
||||
case i => i
|
||||
})
|
||||
val x = (index - 1) % cols
|
||||
val y = (index - 1) / cols
|
||||
val u = x * uStep
|
||||
val v = y * vStep
|
||||
GL11.glTexCoord2d(u, v + vSize)
|
||||
GL11.glVertex3d(tx - dw, ty + charHeight * s, 0)
|
||||
GL11.glTexCoord2d(u + uSize, v + vSize)
|
||||
GL11.glVertex3d(tx + charWidth * s, ty + charHeight * s, 0)
|
||||
GL11.glTexCoord2d(u + uSize, v)
|
||||
GL11.glVertex3d(tx + charWidth * s, ty - dh, 0)
|
||||
GL11.glTexCoord2d(u, v)
|
||||
GL11.glVertex3d(tx - dw, ty - dh, 0)
|
||||
}
|
||||
|
||||
override protected def generateChar(char: Char) {}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package li.cil.oc.client.renderer.font
|
||||
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.util.{TextBuffer, RenderState, PackedColor}
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
||||
/**
|
||||
* Base class for texture based font rendering.
|
||||
*
|
||||
* Provides common logic for the static one (using an existing texture) and the
|
||||
* dynamic one (generating textures on the fly from a font).
|
||||
*/
|
||||
abstract class TextureFontRenderer {
|
||||
protected final val basicChars = """☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■"""
|
||||
|
||||
def charRenderWidth = charWidth / 2
|
||||
|
||||
def charRenderHeight = charHeight / 2
|
||||
|
||||
/**
|
||||
* If drawString() is called inside display lists this should be called
|
||||
* beforehand, outside the display list, to ensure no characters have to
|
||||
* be generated inside the draw call.
|
||||
*/
|
||||
def generateChars(chars: Array[Char]) {
|
||||
for (char <- chars) {
|
||||
generateChar(char)
|
||||
}
|
||||
}
|
||||
|
||||
def drawBuffer(buffer: TextBuffer) {
|
||||
val format = buffer.format
|
||||
|
||||
GL11.glPushMatrix()
|
||||
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS)
|
||||
|
||||
GL11.glScalef(0.5f, 0.5f, 1)
|
||||
|
||||
GL11.glDepthMask(false)
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D)
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawBuffer: configure state")
|
||||
|
||||
// Background first. We try to merge adjacent backgrounds of the same
|
||||
// color to reduce the number of quads we have to draw.
|
||||
GL11.glBegin(GL11.GL_QUADS)
|
||||
for (y <- 0 until buffer.height) {
|
||||
val color = buffer.color(y)
|
||||
var cbg = 0x000000
|
||||
var x = 0
|
||||
var width = 0
|
||||
for (col <- color.map(PackedColor.unpackBackground(_, format))) {
|
||||
if (col != cbg) {
|
||||
drawQuad(cbg, x, y, width)
|
||||
cbg = col
|
||||
x += width
|
||||
width = 0
|
||||
}
|
||||
width = width + 1
|
||||
}
|
||||
drawQuad(cbg, x, y, width)
|
||||
}
|
||||
GL11.glEnd()
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawBuffer: background")
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D)
|
||||
|
||||
if (Settings.get.textLinearFiltering) {
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR)
|
||||
}
|
||||
|
||||
// Foreground second. We only have to flush when the color changes, so
|
||||
// unless every char has a different color this should be quite efficient.
|
||||
for (y <- 0 until buffer.height) {
|
||||
val line = buffer.buffer(y)
|
||||
val color = buffer.color(y)
|
||||
val ty = y * charHeight
|
||||
for (i <- 0 until textureCount) {
|
||||
bindTexture(i)
|
||||
GL11.glBegin(GL11.GL_QUADS)
|
||||
var cfg = -1
|
||||
var tx = 0f
|
||||
for (n <- 0 until line.length) {
|
||||
val ch = line(n)
|
||||
val col = PackedColor.unpackForeground(color(n), format)
|
||||
// Check if color changed.
|
||||
if (col != cfg) {
|
||||
cfg = col
|
||||
GL11.glColor3ub(
|
||||
((cfg & 0xFF0000) >> 16).toByte,
|
||||
((cfg & 0x00FF00) >> 8).toByte,
|
||||
((cfg & 0x0000FF) >> 0).toByte)
|
||||
}
|
||||
// Don't render whitespace.
|
||||
if (ch != ' ') {
|
||||
drawChar(tx, ty, ch)
|
||||
}
|
||||
tx += charWidth
|
||||
}
|
||||
GL11.glEnd()
|
||||
}
|
||||
}
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawBuffer: foreground")
|
||||
|
||||
GL11.glPopAttrib()
|
||||
GL11.glPopMatrix()
|
||||
|
||||
RenderState.checkError(getClass.getName + ".drawBuffer: leaving")
|
||||
}
|
||||
|
||||
protected def charWidth: Int
|
||||
|
||||
protected def charHeight: Int
|
||||
|
||||
protected def textureCount: Int
|
||||
|
||||
protected def bindTexture(index: Int)
|
||||
|
||||
protected def generateChar(char: Char)
|
||||
|
||||
protected def drawChar(tx: Float, ty: Float, char: Char)
|
||||
|
||||
private def drawQuad(color: Int, x: Int, y: Int, width: Int) = if (color != 0 && width > 0) {
|
||||
val x0 = x * charWidth
|
||||
val x1 = (x + width) * charWidth
|
||||
val y0 = y * charHeight
|
||||
val y1 = (y + 1) * charHeight
|
||||
GL11.glColor3ub(((color >> 16) & 0xFF).toByte, ((color >> 8) & 0xFF).toByte, (color & 0xFF).toByte)
|
||||
GL11.glVertex3d(x0, y1, 0)
|
||||
GL11.glVertex3d(x1, y1, 0)
|
||||
GL11.glVertex3d(x1, y0, 0)
|
||||
GL11.glVertex3d(x0, y0, 0)
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ import li.cil.oc.common.tileentity.Screen
|
||||
import li.cil.oc.util.RenderState
|
||||
import li.cil.oc.util.mods.BuildCraft
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
|
||||
import net.minecraft.client.renderer.Tessellator
|
||||
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
|
||||
import net.minecraft.tileentity.TileEntity
|
||||
import net.minecraftforge.common.util.ForgeDirection
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
@ -8,7 +8,7 @@ import li.cil.oc.api.component.TextBuffer.ColorDepth
|
||||
import li.cil.oc.api.driver.Container
|
||||
import li.cil.oc.api.network._
|
||||
import li.cil.oc.client.{PacketSender => ClientPacketSender, ComponentTracker => ClientComponentTracker}
|
||||
import li.cil.oc.client.renderer.{MonospaceFontRenderer, TextBufferRenderCache}
|
||||
import li.cil.oc.client.renderer.TextBufferRenderCache
|
||||
import li.cil.oc.common.tileentity
|
||||
import li.cil.oc.server.{PacketSender => ServerPacketSender, ComponentTracker => ServerComponentTracker}
|
||||
import li.cil.oc.server.component.Keyboard
|
||||
@ -293,10 +293,10 @@ class TextBuffer(val owner: Container) extends ManagedComponent with api.compone
|
||||
override def renderText() = relativeLitArea != 0 && proxy.render()
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def renderWidth = MonospaceFontRenderer.fontWidth * data.width
|
||||
override def renderWidth = TextBufferRenderCache.renderer.charRenderWidth * data.width
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def renderHeight = MonospaceFontRenderer.fontHeight * data.height
|
||||
override def renderHeight = TextBufferRenderCache.renderer.charRenderHeight * data.height
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def setRenderingEnabled(enabled: Boolean) = isRendering = enabled
|
||||
|
Loading…
x
Reference in New Issue
Block a user