Refactored text rendering a little to allow reusing it for status text display of drones.

This commit is contained in:
Florian Nücke 2014-12-15 03:59:15 +01:00
parent 6a4a03801e
commit a80a2b0f2b
10 changed files with 177 additions and 84 deletions

View File

@ -5,10 +5,14 @@ import java.util
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.{PacketSender => ClientPacketSender}
import li.cil.oc.common.container
import li.cil.oc.common.entity
import li.cil.oc.util.PackedColor
import li.cil.oc.util.RenderState
import li.cil.oc.util.TextBuffer
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiButton
import net.minecraft.client.renderer.Tessellator
@ -16,16 +20,27 @@ import net.minecraft.entity.player.InventoryPlayer
import net.minecraft.inventory.Slot
import org.lwjgl.opengl.GL11
class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends DynamicGuiContainer(new container.Drone(playerInventory, drone)) {
class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends DynamicGuiContainer(new container.Drone(playerInventory, drone)) with traits.DisplayBuffer {
xSize = 176
ySize = 148
protected var powerButton: ImageButton = _
private val bufferWidth = 80
private val bufferHeight = 32
private val bufferX = 10
private val bufferY = 10
private val buffer = new TextBuffer(20, 2, PackedColor.SingleBitFormat)
private val bufferRenderer = new TextBufferRenderData {
private var _dirty = true
override def dirty = _dirty
override def dirty_=(value: Boolean) = _dirty = value
override def data = buffer
}
override protected val bufferX = 9
override protected val bufferY = 9
override protected val bufferColumns = 80
override protected val bufferRows = 16
private val inventoryX = 97
private val inventoryY = 7
@ -46,6 +61,9 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D
override def drawScreen(mouseX: Int, mouseY: Int, dt: Float) {
powerButton.toggled = drone.isRunning
bufferRenderer.dirty = drone.statusText.lines.zipWithIndex.map {
case (line, i) => buffer.set(0, i, line, vertical = false)
}.contains(true)
super.drawScreen(mouseX, mouseY, dt)
}
@ -55,15 +73,30 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D
add(buttonList, powerButton)
}
override protected def drawBuffer() {
GL11.glTranslatef(bufferX, bufferY, 0)
RenderState.disableLighting()
RenderState.makeItBlend()
GL11.glScaled(scale, scale, 1)
GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT)
GL11.glDepthMask(false)
GL11.glColor3f(0.5f, 0.5f, 1f)
TextBufferRenderCache.render(bufferRenderer)
GL11.glPopAttrib()
}
override protected def changeSize(w: Double, h: Double, recompile: Boolean) = 2.0
override protected def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) {
drawBufferLayer()
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS) // Me lazy... prevents NEI render glitch.
if (func_146978_c(power.x, power.y, power.width, power.height, mouseX, mouseY)) {
val tooltip = new java.util.ArrayList[String]
val format = Localization.Computer.Power + ": %d%% (%d/%d)"
// tooltip.add(format.format(
// ((drone.globalBuffer / drone.globalBufferSize) * 100).toInt,
// drone.globalBuffer.toInt,
// drone.globalBufferSize.toInt))
tooltip.add(format.format(
drone.globalBuffer * 100 / math.max(drone.globalBufferSize, 1),
drone.globalBuffer,
drone.globalBufferSize))
copiedDrawHoveringText(tooltip, mouseX - guiLeft, mouseY - guiTop, fontRendererObj)
}
if (powerButton.func_146115_a) {
@ -78,8 +111,7 @@ class Drone(playerInventory: InventoryPlayer, val drone: entity.Drone) extends D
GL11.glColor3f(1, 1, 1) // Required under Linux.
mc.renderEngine.bindTexture(Textures.guiDrone)
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize)
// power.level = robot.globalBuffer / robot.globalBufferSize
power.level = 0.5
power.level = drone.globalBuffer.toDouble / math.max(drone.globalBufferSize.toDouble, 1.0)
drawWidgets()
if (drone.inventory.getSizeInventory > 0) {
drawSelection()

View File

@ -23,7 +23,7 @@ import org.lwjgl.input.Keyboard
import org.lwjgl.input.Mouse
import org.lwjgl.opengl.GL11
class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) extends DynamicGuiContainer(new container.Robot(playerInventory, robot)) with TextBuffer {
class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) extends DynamicGuiContainer(new container.Robot(playerInventory, robot)) with traits.InputBuffer {
override protected val buffer = robot.components.collect {
case Some(buffer: api.component.TextBuffer) => buffer
}.headOption.orNull
@ -55,13 +55,13 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
private val maxBufferWidth = 240.0
private val maxBufferHeight = 140.0
private def bufferWidth = math.min(maxBufferWidth, TextBufferRenderCache.renderer.charRenderWidth * Settings.screenResolutionsByTier(0)._1)
private def bufferRenderWidth = math.min(maxBufferWidth, TextBufferRenderCache.renderer.charRenderWidth * Settings.screenResolutionsByTier(0)._1)
private def bufferHeight = math.min(maxBufferHeight, TextBufferRenderCache.renderer.charRenderHeight * Settings.screenResolutionsByTier(0)._2)
private def bufferRenderHeight = math.min(maxBufferHeight, TextBufferRenderCache.renderer.charRenderHeight * Settings.screenResolutionsByTier(0)._2)
override protected def bufferX = (8 + (maxBufferWidth - bufferWidth) / 2).toInt
override protected def bufferX = (8 + (maxBufferWidth - bufferRenderWidth) / 2).toInt
override protected def bufferY = (8 + (maxBufferHeight - bufferHeight) / 2).toInt
override protected def bufferY = (8 + (maxBufferHeight - bufferRenderHeight) / 2).toInt
private val inventoryX = 169
private val inventoryY = 155 - deltaY
@ -113,8 +113,8 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
BufferRenderer.drawBackground()
GL11.glPopMatrix()
RenderState.makeItBlend()
val scaleX = bufferWidth / buffer.renderWidth
val scaleY = bufferHeight / buffer.renderHeight
val scaleX = bufferRenderWidth / buffer.renderWidth
val scaleY = bufferRenderHeight / buffer.renderHeight
val scale = math.min(scaleX, scaleY)
if (scaleX > scale) {
GL11.glTranslated(buffer.renderWidth * (scaleX - scale) / 2, 0, 0)
@ -260,10 +260,10 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
override protected def changeSize(w: Double, h: Double, recompile: Boolean) = {
val bw = w * TextBufferRenderCache.renderer.charRenderWidth
val bh = h * TextBufferRenderCache.renderer.charRenderHeight
val scaleX = math.min(bufferWidth / bw, 1)
val scaleY = math.min(bufferHeight / bh, 1)
val scaleX = math.min(bufferRenderWidth / bw, 1)
val scaleY = math.min(bufferRenderHeight / bh, 1)
if (recompile) {
BufferRenderer.compileBackground(bufferWidth.toInt, bufferHeight.toInt, forRobot = true)
BufferRenderer.compileBackground(bufferRenderWidth.toInt, bufferRenderHeight.toInt, forRobot = true)
}
math.min(scaleX, scaleY)
}

View File

@ -7,7 +7,7 @@ import li.cil.oc.util.RenderState
import org.lwjgl.input.Mouse
import org.lwjgl.opengl.GL11
class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val hasKeyboardCallback: () => Boolean, val hasPower: () => Boolean) extends TextBuffer {
class Screen(val buffer: api.component.TextBuffer, val hasMouse: Boolean, val hasKeyboardCallback: () => Boolean, val hasPower: () => Boolean) extends traits.InputBuffer {
override protected def hasKeyboard = hasKeyboardCallback()
override protected def bufferX = 8 + x

View File

@ -0,0 +1,50 @@
package li.cil.oc.client.gui.traits
import li.cil.oc.client.renderer.gui.BufferRenderer
import li.cil.oc.util.RenderState
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiScreen
import org.lwjgl.opengl.GL11
trait DisplayBuffer extends GuiScreen {
protected def bufferX: Int
protected def bufferY: Int
protected def bufferColumns: Int
protected def bufferRows: Int
protected var guiSizeChanged = false
protected var currentWidth, currentHeight = -1
protected var scale = 0.0
override def initGui() = {
super.initGui()
BufferRenderer.init(Minecraft.getMinecraft.renderEngine)
guiSizeChanged = true
}
protected def drawBufferLayer() {
val oldWidth = currentWidth
val oldHeight = currentHeight
currentWidth = bufferColumns
currentHeight = bufferRows
scale = changeSize(currentWidth, currentHeight, guiSizeChanged || oldWidth != currentWidth || oldHeight != currentHeight)
RenderState.checkError(getClass.getName + ".drawBufferLayer: entering (aka: wasntme)")
GL11.glPushMatrix()
RenderState.disableLighting()
drawBuffer()
GL11.glPopMatrix()
RenderState.checkError(getClass.getName + ".drawBufferLayer: buffer layer")
}
protected def drawBuffer()
protected def changeSize(w: Double, h: Double, recompile: Boolean): Double
}

View File

@ -1,9 +1,8 @@
package li.cil.oc.client.gui
package li.cil.oc.client.gui.traits
import li.cil.oc.api
import li.cil.oc.api.component.TextBuffer
import li.cil.oc.client.KeyBindings
import li.cil.oc.client.Textures
import li.cil.oc.client.renderer.gui.BufferRenderer
import li.cil.oc.integration.util.NEI
import li.cil.oc.util.RenderState
import net.minecraft.client.Minecraft
@ -14,64 +13,28 @@ import org.lwjgl.opengl.GL11
import scala.collection.mutable
trait TextBuffer extends GuiScreen {
protected def buffer: api.component.TextBuffer
trait InputBuffer extends DisplayBuffer {
protected def buffer: TextBuffer
override protected def bufferColumns = if (buffer == null) 0 else buffer.getWidth
override protected def bufferRows = if (buffer == null) 0 else buffer.getHeight
protected def hasKeyboard: Boolean
private val pressedKeys = mutable.Map.empty[Int, Char]
protected def bufferX: Int
protected def bufferY: Int
protected var guiSizeChanged = false
protected var currentWidth, currentHeight = -1
private var showKeyboardMissing = 0L
protected var scale = 0.0
override def doesGuiPauseGame = false
override def initGui() = {
super.initGui()
BufferRenderer.init(Minecraft.getMinecraft.renderEngine)
Keyboard.enableRepeatEvents(true)
guiSizeChanged = true
}
override def onGuiClosed() = {
super.onGuiClosed()
if (buffer != null) for ((code, char) <- pressedKeys) {
buffer.keyUp(char, code, null)
}
Keyboard.enableRepeatEvents(false)
}
protected def drawBufferLayer() {
if (buffer == null) return
val oldWidth = currentWidth
val oldHeight = currentHeight
if (buffer != null) {
currentWidth = buffer.getWidth
currentHeight = buffer.getHeight
}
else {
currentWidth = 0
currentHeight = 0
}
scale = changeSize(currentWidth, currentHeight, guiSizeChanged || oldWidth != currentWidth || oldHeight != currentHeight)
RenderState.checkError(getClass.getName + ".drawBufferLayer: entering (aka: wasntme)")
GL11.glPushMatrix()
RenderState.disableLighting()
drawBuffer()
GL11.glPopMatrix()
RenderState.checkError(getClass.getName + ".drawBufferLayer: buffer layer")
override protected def drawBufferLayer() {
super.drawBufferLayer()
if (System.currentTimeMillis() - showKeyboardMissing < 1000) {
Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.guiKeyboardMissing)
@ -86,12 +49,18 @@ trait TextBuffer extends GuiScreen {
t.addVertexWithUV(x, y, 0, 0, 0)
t.draw()
GL11.glEnable(GL11.GL_DEPTH_TEST)
RenderState.checkError(getClass.getName + ".drawBufferLayer: keyboard icon")
}
}
RenderState.checkError(getClass.getName + ".drawBufferLayer: leaving")
override def onGuiClosed() = {
super.onGuiClosed()
if (buffer != null) for ((code, char) <- pressedKeys) {
buffer.keyUp(char, code, null)
}
Keyboard.enableRepeatEvents(false)
}
protected def drawBuffer()
override def handleKeyboardInput() {
super.handleKeyboardInput()
@ -135,8 +104,6 @@ trait TextBuffer extends GuiScreen {
}
}
protected def changeSize(w: Double, h: Double, recompile: Boolean): Double
private def ignoreRepeat(char: Char, code: Int) = {
code == Keyboard.KEY_LCONTROL ||
code == Keyboard.KEY_RCONTROL ||

View File

@ -9,6 +9,7 @@ import com.google.common.cache.RemovalNotification
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
import li.cil.oc.Settings
import li.cil.oc.client.renderer.font.TextBufferRenderData
import li.cil.oc.common.component.TextBuffer
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.GLAllocation
@ -23,23 +24,23 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
private val cache = com.google.common.cache.CacheBuilder.newBuilder().
expireAfterAccess(2, TimeUnit.SECONDS).
removalListener(this).
asInstanceOf[CacheBuilder[TextBuffer, Int]].
build[TextBuffer, Int]()
asInstanceOf[CacheBuilder[TextBufferRenderData, Int]].
build[TextBufferRenderData, Int]()
// To allow access in cache entry init.
private var currentBuffer: TextBuffer = _
private var currentBuffer: TextBufferRenderData = _
// ----------------------------------------------------------------------- //
// Rendering
// ----------------------------------------------------------------------- //
def render(buffer: TextBuffer) {
def render(buffer: TextBufferRenderData) {
currentBuffer = buffer
compileOrDraw(cache.get(currentBuffer, this))
}
private def compileOrDraw(list: Int) = {
if (currentBuffer.proxy.dirty) {
if (currentBuffer.dirty) {
RenderState.checkError(getClass.getName + ".compileOrDraw: entering (aka: wasntme)")
for (line <- currentBuffer.data.buffer) {
@ -48,7 +49,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
val doCompile = !RenderState.compilingDisplayList
if (doCompile) {
currentBuffer.proxy.dirty = false
currentBuffer.dirty = false
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
RenderState.checkError(getClass.getName + ".compileOrDraw: glNewList")
@ -84,7 +85,7 @@ object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEnti
RenderState.checkError(getClass.getName + ".call: entering (aka: wasntme)")
val list = GLAllocation.generateDisplayLists(1)
currentBuffer.proxy.dirty = true // Force compilation.
currentBuffer.dirty = true // Force compilation.
RenderState.checkError(getClass.getName + ".call: leaving")

View File

@ -0,0 +1,11 @@
package li.cil.oc.client.renderer.font
import li.cil.oc.util.TextBuffer
trait TextBufferRenderData {
def dirty: Boolean
def dirty_=(value: Boolean): Unit
def data: TextBuffer
}

View File

@ -15,6 +15,7 @@ import li.cil.oc.api.machine.Context
import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.renderer.font.TextBufferRenderData
import li.cil.oc.client.{ComponentTracker => ClientComponentTracker}
import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common._
@ -513,9 +514,17 @@ object TextBuffer {
}
class ClientProxy(val owner: TextBuffer) extends Proxy {
val renderer = new TextBufferRenderData {
override def dirty = ClientProxy.this.dirty
override def dirty_=(value: Boolean) = ClientProxy.this.dirty = value
override def data = owner.data
}
override def render() = {
val wasDirty = dirty
TextBufferRenderCache.render(owner)
TextBufferRenderCache.render(renderer)
wasDirty
}

View File

@ -44,6 +44,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
var bodyAngle = math.random.toFloat * 90
var angularVelocity = 0f
var nextAngularVelocityChange = 0
var lastEnergyUpdate = 0
// Logic stuff, components, machine and such.
val info = new ItemUtils.MicrocontrollerData()
@ -141,12 +142,21 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
// ----------------------------------------------------------------------- //
override def entityInit() {
// Running or not.
dataWatcher.addObject(2, byte2Byte(0: Byte))
// Target position.
dataWatcher.addObject(3, float2Float(0f))
dataWatcher.addObject(4, float2Float(0f))
dataWatcher.addObject(5, float2Float(0f))
// Max acceleration.
dataWatcher.addObject(6, float2Float(0f))
// Selected inventory slot.
dataWatcher.addObject(7, byte2Byte(0: Byte))
// Current and maximum energy.
dataWatcher.addObject(8, int2Integer(0))
dataWatcher.addObject(9, int2Integer(100))
// Status text.
dataWatcher.addObject(10, "Hello\nWorld!")
}
def isRunning = dataWatcher.getWatchableObjectByte(2) != 0
@ -155,6 +165,9 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
def targetZ = dataWatcher.getWatchableObjectFloat(5)
def targetAcceleration = dataWatcher.getWatchableObjectFloat(6)
def selectedSlot = dataWatcher.getWatchableObjectByte(7) & 0xFF
def globalBuffer = dataWatcher.getWatchableObjectInt(8)
def globalBufferSize = dataWatcher.getWatchableObjectInt(9)
def statusText = dataWatcher.getWatchableObjectString(10)
private def setRunning(value: Boolean) = dataWatcher.updateObject(2, byte2Byte(if (value) 1: Byte else 0: Byte))
// Round target values to low accuracy to avoid floating point errors accumulating.
@ -163,6 +176,9 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
def targetZ_=(value: Float): Unit = dataWatcher.updateObject(5, float2Float(math.round(value * 5) / 5f))
def targetAcceleration_=(value: Float): Unit = dataWatcher.updateObject(6, float2Float(math.max(0, math.min(maxAcceleration, value))))
def selectedSlot_=(value: Int) = dataWatcher.updateObject(7, byte2Byte(value.toByte))
private def globalBuffer_=(value: Int) = dataWatcher.updateObject(8, int2Integer(value))
private def globalBufferSize_=(value: Int) = dataWatcher.updateObject(9, int2Integer(value))
def statusText_=(value: String) = dataWatcher.updateObject(10, Option(value).map(_.lines.map(_.take(10)).take(2).mkString("\n")).getOrElse(""))
@SideOnly(Side.CLIENT)
override def setPositionAndRotation2(x: Double, y: Double, z: Double, yaw: Float, pitch: Float, data: Int) {
@ -204,6 +220,11 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
machine.update()
components.updateComponents()
setRunning(machine.isRunning)
if (math.abs(lastEnergyUpdate - globalBuffer) > 100) {
globalBuffer = machine.node.asInstanceOf[Connector].globalBuffer.toInt
globalBufferSize = machine.node.asInstanceOf[Connector].globalBufferSize.toInt
}
}
else if (isRunning) {
// Client side update; occasionally update wing pitch and rotation to
@ -326,6 +347,8 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
targetY = nbt.getFloat("targetY")
targetZ = nbt.getFloat("targetZ")
targetAcceleration = nbt.getFloat("targetAcceleration")
selectedSlot = nbt.getByte("selectedSlot") & 0xFF
statusText = nbt.getString("statusText")
}
override def writeEntityToNBT(nbt: NBTTagCompound): Unit = {
@ -341,5 +364,7 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
nbt.setFloat("targetY", targetY)
nbt.setFloat("targetZ", targetZ)
nbt.setFloat("targetAcceleration", targetAcceleration)
nbt.setByte("selectedSlot", selectedSlot.toByte)
nbt.setString("statusText", statusText)
}
}

View File

@ -1,7 +1,5 @@
package li.cil.oc.server.component.robot
import java.util.UUID
import com.mojang.authlib.GameProfile
import cpw.mods.fml.common.ObfuscationReflectionHelper
import cpw.mods.fml.common.eventhandler.Event