more color screen stuff; made advanced screen 4 bit, pro screen 8 bit (which is actually pretty much for a pseudo computer in a game with 16x16 textures...); depth can be dynamically changed; colors are always provided as 32bit rgb from the lua side to make things easier, so it's ok to call these even on a color-less gpu+screen; added vertical gap between chars in char texture to avoid bleeding when interpolating due to downscaling; made some programs use colors (sh, lua and ls); screw scanlines, aliasing is too much of a pain

This commit is contained in:
Florian Nücke 2013-11-11 00:51:23 +01:00
parent 4ee76b146d
commit 0c8347df62
20 changed files with 384 additions and 156 deletions

View File

@ -13,7 +13,19 @@ for i = 1, #dirs do
if not list then
print(reason)
else
local function setColor(c)
if component.gpu.getForeground() ~= c then
component.gpu.setForeground(c)
end
end
for f in list do
if f:sub(-1) == "/" then
setColor(0x99CCFF)
elseif f:sub(-4) == ".lua" then
setColor(0x00FF00)
else
setColor(0xFFFFFF)
end
if options.a or f:sub(1, 1) ~= "." then
if options.l then
print(f, fs.size(fs.concat(path, f)))
@ -22,6 +34,7 @@ for i = 1, #dirs do
end
end
end
setColor(0xFFFFFF)
if not options.l then
print()
end

View File

@ -2,7 +2,9 @@ print("Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio")
local history = {}
local env = setmetatable({}, {__index=_ENV})
while term.isAvailable() do
local foreground = component.gpu.setForeground(0x00FF00)
term.write("lua> ")
component.gpu.setForeground(foreground)
local command = term.read(history)
if command == nil then -- eof
return

View File

@ -16,7 +16,9 @@ while true do
end
end
while term.isAvailable() do
local foreground = component.gpu.setForeground(0xFF0000)
term.write("# ")
component.gpu.setForeground(foreground)
local command = term.read(history)
if not command then
print("exit")

View File

@ -4,8 +4,6 @@ local function onComponentAvailable(_, componentType)
then
local gpu = component.primary("gpu")
gpu.bind(component.primary("screen").address)
local maxX, maxY = gpu.maxResolution()
gpu.setResolution(maxX, maxY)
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,6 +1,7 @@
package li.cil.oc
import java.io.File
import li.cil.oc.util.PackedColor
object Config {
val resourceDomain = "opencomputers"
@ -10,6 +11,7 @@ object Config {
// ----------------------------------------------------------------------- //
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
val screenDepthsByTier = Array(PackedColor.Depth.OneBit, PackedColor.Depth.FourBit, PackedColor.Depth.EightBit)
// ----------------------------------------------------------------------- //
@ -39,6 +41,7 @@ object Config {
var maxScreenTextRenderDistance = 10.0
var screenTextFadeStartDistance = 8.0
var textLinearFiltering = false
// ----------------------------------------------------------------------- //
@ -97,6 +100,15 @@ object Config {
"instantly disappear when moving away from the screen displaying it.").
getDouble(screenTextFadeStartDistance)
textLinearFiltering = config.get("client", "textLinearFiltering", textLinearFiltering, "" +
"Whether to apply linear filtering for text displayed on screens when the\n" +
"screen has to be scaled down - i.e. the text is rendered at a resolution\n" +
"lower than their native one, e.g. when the GUI scale is less than one or\n" +
"when looking at a far away screen. This leads to smoother text for scaled\n" +
"down text but results in characters not perfectly connecting anymore (for\n" +
"example for box drawing characters. Look it up on Wikipedia.)").
getBoolean(textLinearFiltering)
// --------------------------------------------------------------------- //
config.getCategory("power").

View File

@ -5,6 +5,7 @@ import li.cil.oc.common.PacketType
import li.cil.oc.common.tileentity.{PowerDistributor, Computer, Rotatable, Screen}
import li.cil.oc.common.{PacketHandler => CommonPacketHandler}
import li.cil.oc.server.component.Redstone
import li.cil.oc.util.PackedColor
import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.tileentity.TileEntity
@ -25,7 +26,9 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.RedstoneStateResponse => onRedstoneStateResponse(p)
case PacketType.RotatableStateResponse => onRotatableStateResponse(p)
case PacketType.ScreenBufferResponse => onScreenBufferResponse(p)
case PacketType.ScreenColorChange => onScreenColorChange(p)
case PacketType.ScreenCopy => onScreenCopy(p)
case PacketType.ScreenDepthChange => onScreenDepthChange(p)
case PacketType.ScreenFill => onScreenFill(p)
case PacketType.ScreenResolutionChange => onScreenResolutionChange(p)
case PacketType.ScreenSet => onScreenSet(p)
@ -74,12 +77,31 @@ class PacketHandler extends CommonPacketHandler {
def onScreenBufferResponse(p: PacketParser) =
p.readTileEntity[Screen]() match {
case Some(t) =>
val screen = t.instance
val w = p.readInt()
val h = p.readInt()
t.instance.resolution = (w, h)
screen.resolution = (w, h)
p.readUTF.split('\n').zipWithIndex.foreach {
case (line, i) => t.instance.set(0, i, line)
case (line, i) => screen.set(0, i, line)
}
screen.depth = PackedColor.Depth(p.readInt())
screen.foreground = p.readInt()
screen.background = p.readInt()
for (row <- 0 until h) {
val rowColor = screen.colors(row)
for (col <- 0 until w) {
rowColor(col) = p.readShort()
}
}
case _ => // Invalid packet.
}
def onScreenColorChange(p: PacketParser) =
p.readTileEntity[Screen]() match {
case Some(t) => {
t.instance.foreground = p.readInt()
t.instance.background = p.readInt()
}
case _ => // Invalid packet.
}
@ -97,6 +119,14 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet.
}
def onScreenDepthChange(p: PacketParser) =
p.readTileEntity[Screen]() match {
case Some(t) => {
t.instance.depth = PackedColor.Depth(p.readInt())
}
case _ => // Invalid packet.
}
def onScreenFill(p: PacketParser) =
p.readTileEntity[Screen]() match {
case Some(t) => {

View File

@ -4,6 +4,7 @@ import li.cil.oc.Config
import li.cil.oc.client.PacketSender
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.common.tileentity
import li.cil.oc.util.PackedColor
import net.minecraft.client.gui.{GuiScreen => MCGuiScreen}
import net.minecraft.client.renderer.GLAllocation
import net.minecraft.client.renderer.Tessellator
@ -12,7 +13,6 @@ import net.minecraft.util.ResourceLocation
import org.lwjgl.input.Keyboard
import org.lwjgl.opengl.GL11
import scala.collection.mutable
import li.cil.oc.util.PackedColor
/**
* This GUI shows the buffer of a single screen.
@ -184,9 +184,11 @@ object Screen {
GL11.glEndList()
}
private[gui] def compileText(scale: Double, lines: Array[Array[Char]], colors:Array[Array[Int]], depth: PackedColor.Depth.Value) =
private[gui] def compileText(scale: Double, lines: Array[Array[Char]], colors: Array[Array[Short]], depth: PackedColor.Depth.Value) =
if (textureManager.isDefined) {
GL11.glNewList(displayLists.get + 1, GL11.GL_COMPILE)
GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT)
GL11.glDepthMask(false)
GL11.glTranslatef(margin + innerMargin, margin + innerMargin, 0)
GL11.glScaled(scale, scale, 1)
@ -194,6 +196,7 @@ object Screen {
case ((line, color), i) => MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, depth)
}
GL11.glPopAttrib()
GL11.glEndList()
}

View File

@ -1,6 +1,6 @@
package li.cil.oc.client.renderer
import li.cil.oc.util.{RenderState, PackedColor}
import li.cil.oc.util.PackedColor
import li.cil.oc.{OpenComputers, Config}
import net.minecraft.client.renderer.GLAllocation
import net.minecraft.client.renderer.Tessellator
@ -10,7 +10,7 @@ import org.lwjgl.opengl.GL11
import scala.io.Source
object MonospaceFontRenderer {
private val font = new ResourceLocation(Config.resourceDomain, "textures/font/chars.png")
val font = new ResourceLocation(Config.resourceDomain, "textures/font/chars.png")
private val chars = Source.fromInputStream(MonospaceFontRenderer.getClass.getResourceAsStream("/assets/" + Config.resourceDomain + "/textures/font/chars.txt")).mkString
@ -21,7 +21,7 @@ object MonospaceFontRenderer {
val (fontWidth, fontHeight) = (5, 9)
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Int], depth: PackedColor.Depth.Value) = instance match {
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Short], depth: PackedColor.Depth.Value) = instance match {
case None => OpenComputers.log.warning("Trying to render string with uninitialized MonospaceFontRenderer.")
case Some(renderer) => renderer.drawString(x, y, value, color, depth)
}
@ -36,7 +36,9 @@ object MonospaceFontRenderer {
private val (charWidth, charHeight) = (MonospaceFontRenderer.fontWidth * 2, MonospaceFontRenderer.fontHeight * 2)
private val cols = 256 / charWidth
private val uStep = charWidth / 256.0
private val vStep = charHeight / 256.0
private val uSize = uStep
private val vStep = (charHeight + 1) / 256.0
private val vSize = charHeight / 256.0
// Set up the display lists.
{
@ -49,9 +51,9 @@ object MonospaceFontRenderer {
val v = y * vStep
GL11.glNewList(charLists + index, GL11.GL_COMPILE)
t.startDrawingQuads()
t.addVertexWithUV(0, charHeight, 0, u, v + vStep)
t.addVertexWithUV(charWidth, charHeight, 0, u + uStep, v + vStep)
t.addVertexWithUV(charWidth, 0, 0, u + uStep, v)
t.addVertexWithUV(0, charHeight, 0, u, v + vSize)
t.addVertexWithUV(charWidth, charHeight, 0, u + uSize, v + vSize)
t.addVertexWithUV(charWidth, 0, 0, u + uSize, v)
t.addVertexWithUV(0, 0, 0, u, v)
t.draw()
GL11.glTranslatef(charWidth, 0, 0)
@ -63,35 +65,39 @@ object MonospaceFontRenderer {
GL11.glEndList()
}
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Int], depth: PackedColor.Depth.Value) = {
def drawString(x: Int, y: Int, value: Array[Char], color: Array[Short], depth: PackedColor.Depth.Value) = {
if (color.length != value.length) throw new IllegalArgumentException("Color count must match char count.")
textureManager.bindTexture(MonospaceFontRenderer.font)
GL11.glPushMatrix()
GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT)
GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_TEXTURE_BIT)
GL11.glTranslatef(x, y, 0)
GL11.glScalef(0.5f, 0.5f, 1)
GL11.glDepthMask(false)
RenderState.makeItBlend()
// 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(_, depth))) {
if (col != cbg) {
draw(cbg, width)
draw(cbg, offset, width)
cbg = col
offset += width
width = 0
}
width = width + 1
}
draw(cbg, width)
draw(cbg, offset, width)
if (Config.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.
var cfg = 0x000000
GL11.glColor3f(0, 0, 0)
var cfg = -1
for ((ch, col) <- value.zip(color.map(PackedColor.unpackForeground(_, depth)))) {
val index = 1 + chars.indexOf(ch) match {
case -1 => chars.indexOf('?')
@ -102,9 +108,9 @@ object MonospaceFontRenderer {
flush()
cfg = col
GL11.glColor3ub(
(cfg & 0xFF0000 >> 16).toByte,
(cfg & 0x00FF00 >> 8).toByte,
(cfg & 0x0000FF).toByte)
((cfg & 0xFF0000) >> 16).toByte,
((cfg & 0x00FF00) >> 8).toByte,
((cfg & 0x0000FF) >> 0).toByte)
}
listBuffer.put(charLists + index)
if (listBuffer.remaining == 0)
@ -116,18 +122,23 @@ object MonospaceFontRenderer {
GL11.glPopMatrix()
}
private def draw(color: Int, width: Int) = if (color != 0 && width > 0) {
private val bgu1 = 254.0 / 256.0
private val bgu2 = 255.0 / 256.0
private val bgv1 = 255.0 / 256.0
private val bgv2 = 256.0 / 256.0
private def draw(color: Int, offset: Int, width: Int) = if (color != 0 && width > 0) {
val t = Tessellator.instance
t.startDrawingQuads()
t.setColorOpaque_I(color)
t.addVertexWithUV(0, charHeight, 0, 0, vStep)
t.addVertexWithUV(charWidth, charHeight, 0, width * uStep, vStep)
t.addVertexWithUV(charWidth, 0, 0, width * uStep, 0)
t.addVertexWithUV(0, 0, 0, 0, 0)
t.addVertexWithUV(charWidth * offset, charHeight, 0, bgu1, bgv2)
t.addVertexWithUV(charWidth * (offset + width), charHeight, 0, bgu2, bgv2)
t.addVertexWithUV(charWidth * (offset + width), 0, 0, bgu2, bgv1)
t.addVertexWithUV(charWidth * offset, 0, 0, bgu1, bgv1)
t.draw()
}
private def flush() = {
private def flush() = if (listBuffer.position > 0) {
listBuffer.flip()
GL11.glCallLists(listBuffer)
listBuffer.clear()

View File

@ -34,7 +34,9 @@ object PacketType extends Enumeration {
val ScreenBufferResponse = Value("ScreenBufferResponse")
/** These are sent from the server to the client for partial updates. */
val ScreenColorChange = Value("ScreenColorChange")
val ScreenCopy = Value("ScreenCopy")
val ScreenDepthChange = Value("ScreenDepthChange")
val ScreenFill = Value("ScreenFill")
val ScreenResolutionChange = Value("ScreenResolutionChange")
val ScreenSet = Value("ScreenSet")

View File

@ -7,7 +7,7 @@ import li.cil.oc.util.{PackedColor, TextBuffer}
import li.cil.oc.{Config, util, api}
import net.minecraft.nbt.NBTTagCompound
class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) extends Persistable {
class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int), val maxDepth: PackedColor.Depth.Value) extends Persistable {
private val buffer = new TextBuffer(maxResolution, PackedColor.Depth.OneBit)
// ----------------------------------------------------------------------- //
@ -20,12 +20,48 @@ class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) exten
def depth = buffer.depth
def depth_=(value: PackedColor.Depth.Value) = {
if (value > maxDepth)
throw new IllegalArgumentException("unsupported depth")
if (buffer.depth = value) {
owner.onScreenDepthChange(value)
true
}
else false
}
def foreground = buffer.foreground
def foreground_=(value: Int) = {
if (buffer.foreground != value) {
val result = buffer.foreground
buffer.foreground = value
owner.onScreenColorChange(foreground, background)
result
}
else value
}
def background = buffer.background
def background_=(value: Int) = {
if (buffer.background != value) {
val result = buffer.background
buffer.background = value
owner.onScreenColorChange(foreground, background)
result
}
else value
}
def resolution = buffer.size
def resolution_=(value: (Int, Int)) = {
val (w, h) = value
val (mw, mh) = maxResolution
if (w <= mw && h <= mh && (buffer.size = value)) {
if (w < 1 || w > mw || h < 1 || h > mh)
throw new IllegalArgumentException("unsupported resolution")
if (buffer.size = value) {
owner.onScreenResolutionChange(w, h)
true
}
@ -58,12 +94,12 @@ class Screen(val owner: Screen.Environment, val maxResolution: (Int, Int)) exten
// ----------------------------------------------------------------------- //
override def load(nbt: NBTTagCompound) = {
buffer.readFromNBT(nbt.getCompoundTag("oc.screen"))
buffer.load(nbt.getCompoundTag("oc.screen"))
}
override def save(nbt: NBTTagCompound) = {
val screenNbt = new NBTTagCompound
buffer.writeToNBT(screenNbt)
buffer.save(screenNbt)
nbt.setCompoundTag("oc.screen", screenNbt)
}
@ -81,7 +117,7 @@ object Screen {
withConnector(Config.bufferScreen * (tier + 1)).
create()
final val instance = new component.Screen(this, Config.screenResolutionsByTier(tier))
final val instance = new component.Screen(this, Config.screenResolutionsByTier(tier), Config.screenDepthsByTier(tier))
protected def tier: Int
@ -101,8 +137,12 @@ object Screen {
// ----------------------------------------------------------------------- //
def onScreenColorChange(foreground: Int, background: Int) {}
def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {}
def onScreenDepthChange(depth: PackedColor.Depth.Value) {}
def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {}
def onScreenResolutionChange(w: Int, h: Int) {

View File

@ -8,6 +8,8 @@ class GraphicsCard(val parent: Delegator, val tier: Int) extends Delegate {
val maxResolution = Config.screenResolutionsByTier(tier)
val maxDepth = Config.screenDepthsByTier(tier)
override def registerIcons(iconRegister: IconRegister) {
super.registerIcons(iconRegister)

View File

@ -7,11 +7,12 @@ import li.cil.oc.client.gui
import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common.component.Screen.{Environment => ScreenEnvironment}
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.PackedColor
import net.minecraft.client.Minecraft
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.AxisAlignedBB
import net.minecraftforge.common.ForgeDirection
import scala.collection.mutable
import net.minecraft.client.Minecraft
class ScreenTier1 extends Screen {
protected def tier = 0
@ -225,6 +226,46 @@ abstract class Screen extends Rotatable with ScreenEnvironment {
// ----------------------------------------------------------------------- //
override def onScreenColorChange(foreground: Int, background: Int) {
super.onScreenColorChange(foreground, background)
if (!worldObj.isRemote) {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenColorChange(this, foreground, background)
}
}
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = {
super.onScreenCopy(col, row, w, h, tx, ty)
if (worldObj.isRemote) {
currentGui.foreach(_.updateText())
hasChanged = true
}
else {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenCopy(this, col, row, w, h, tx, ty)
}
}
override def onScreenDepthChange(depth: PackedColor.Depth.Value) {
super.onScreenDepthChange(depth)
if (!worldObj.isRemote) {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenDepthChange(this, depth)
}
}
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) = {
super.onScreenFill(col, row, w, h, c)
if (worldObj.isRemote) {
currentGui.foreach(_.updateText())
hasChanged = true
}
else {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenFill(this, col, row, w, h, c)
}
}
override def onScreenResolutionChange(w: Int, h: Int) = {
super.onScreenResolutionChange(w, h)
if (worldObj.isRemote) {
@ -248,28 +289,4 @@ abstract class Screen extends Rotatable with ScreenEnvironment {
ServerPacketSender.sendScreenSet(this, col, row, s)
}
}
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) = {
super.onScreenFill(col, row, w, h, c)
if (worldObj.isRemote) {
currentGui.foreach(_.updateText())
hasChanged = true
}
else {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenFill(this, col, row, w, h, c)
}
}
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = {
super.onScreenCopy(col, row, w, h, tx, ty)
if (worldObj.isRemote) {
currentGui.foreach(_.updateText())
hasChanged = true
}
else {
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
ServerPacketSender.sendScreenCopy(this, col, row, w, h, tx, ty)
}
}
}

View File

@ -52,9 +52,7 @@ class PacketHandler extends CommonPacketHandler {
def onScreenBufferRequest(p: PacketParser) =
p.readTileEntity[tileentity.Screen]() match {
case Some(t) =>
val (w, h) = t.instance.resolution
PacketSender.sendScreenBufferState(t, w, h, t.instance.text, Option(p.player))
case Some(t) => PacketSender.sendScreenBufferState(t, Option(p.player))
case _ => // Invalid packet.
}

View File

@ -3,8 +3,10 @@ package li.cil.oc.server
import cpw.mods.fml.common.network.Player
import li.cil.oc.common.PacketBuilder
import li.cil.oc.common.PacketType
import li.cil.oc.common.component.Screen
import li.cil.oc.common.tileentity.{PowerDistributor, Rotatable}
import li.cil.oc.server.component.Redstone
import li.cil.oc.util.PackedColor
import net.minecraft.tileentity.TileEntity
import net.minecraftforge.common.ForgeDirection
@ -70,13 +72,20 @@ object PacketSender {
}
}
def sendScreenBufferState(t: TileEntity, w: Int, h: Int, text: String, player: Option[Player] = None) {
def sendScreenBufferState(t: TileEntity with Screen.Environment, player: Option[Player] = None) {
val pb = new PacketBuilder(PacketType.ScreenBufferResponse)
pb.writeTileEntity(t)
val screen = t.instance
val (w, h) = screen.resolution
pb.writeInt(w)
pb.writeInt(h)
pb.writeUTF(text)
pb.writeUTF(screen.text)
pb.writeInt(screen.depth.id)
pb.writeInt(screen.foreground)
pb.writeInt(screen.background)
for (cs <- screen.colors) for (c <- cs) pb.writeShort(c)
player match {
case Some(p) => pb.sendToPlayer(p)
@ -84,6 +93,16 @@ object PacketSender {
}
}
def sendScreenColorChange(t: TileEntity, foreground: Int, background: Int) {
val pb = new PacketBuilder(PacketType.ScreenColorChange)
pb.writeTileEntity(t)
pb.writeInt(foreground)
pb.writeInt(background)
pb.sendToAllPlayers()
}
def sendScreenCopy(t: TileEntity, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
val pb = new PacketBuilder(PacketType.ScreenCopy)
@ -98,6 +117,15 @@ object PacketSender {
pb.sendToAllPlayers()
}
def sendScreenDepthChange(t: TileEntity, value: PackedColor.Depth.Value) {
val pb = new PacketBuilder(PacketType.ScreenDepthChange)
pb.writeTileEntity(t)
pb.writeInt(value.id)
pb.sendToAllPlayers()
}
def sendScreenFill(t: TileEntity, col: Int, row: Int, w: Int, h: Int, c: Char) {
val pb = new PacketBuilder(PacketType.ScreenFill)

View File

@ -3,10 +3,11 @@ package li.cil.oc.server.component
import li.cil.oc.api
import li.cil.oc.api.network._
import li.cil.oc.common.component.Screen
import li.cil.oc.util.PackedColor
import net.minecraft.nbt.NBTTagCompound
import scala.Some
class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
class GraphicsCard(val maxResolution: (Int, Int), val maxDepth: PackedColor.Depth.Value) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Neighbors).
withComponent("gpu").
create()
@ -15,7 +16,7 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
private var screenInstance: Option[Screen] = None
private def screen(f: (Screen) => Array[AnyRef]) = {
private def screen(f: (Screen) => Array[AnyRef]) = this.synchronized {
if (screenInstance.isEmpty && screenAddress.isDefined) {
Option(node.network.node(screenAddress.get)) match {
case Some(node: Node) if node.host.isInstanceOf[Screen.Environment] =>
@ -38,18 +39,73 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
// ----------------------------------------------------------------------- //
@LuaCallback("bind")
def bind(context: Context, args: Arguments): Array[AnyRef] = {
def bind(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
val address = args.checkString(0)
node.network.node(address) match {
case null => Array(Unit, "invalid address")
case node: Node if node.host.isInstanceOf[Screen.Environment] =>
screenAddress = Option(address)
screenInstance = None
result(true)
screen(s => {
val (gmw, gmh) = maxResolution
val (smw, smh) = s.maxResolution
s.resolution = (gmw min smw, gmh min smh)
s.depth = PackedColor.Depth(maxDepth.id min s.maxDepth.id)
s.foreground = 0xFFFFFF
s.background = 0x000000
result(true)
})
case _ => Array(Unit, "not a screen")
}
}
@LuaCallback(value = "getBackground", direct = true)
def getBackground(context: Context, args: Arguments): Array[AnyRef] =
screen(s => result(s.background))
@LuaCallback("setBackground")
def setBackground(context: Context, args: Arguments): Array[AnyRef] = {
val color = args.checkInteger(0)
screen(s => result(s.background = color))
}
@LuaCallback(value = "getForeground", direct = true)
def getForeground(context: Context, args: Arguments): Array[AnyRef] =
screen(s => result(s.foreground))
@LuaCallback("setForeground")
def setForeground(context: Context, args: Arguments): Array[AnyRef] = {
val color = args.checkInteger(0)
screen(s => result(s.foreground = color))
}
@LuaCallback(value = "getDepth", direct = true)
def getDepth(context: Context, args: Arguments): Array[AnyRef] =
screen(s => result(s.depth match {
case PackedColor.Depth.OneBit => 1
case PackedColor.Depth.FourBit => 4
case PackedColor.Depth.EightBit => 8
}))
@LuaCallback("setDepth")
def setDepth(context: Context, args: Arguments): Array[AnyRef] = {
val depth = args.checkInteger(0)
screen(s => result(s.depth = depth match {
case 1 => PackedColor.Depth.OneBit
case 4 if maxDepth >= PackedColor.Depth.FourBit => PackedColor.Depth.FourBit
case 8 if maxDepth >= PackedColor.Depth.EightBit => PackedColor.Depth.EightBit
case _ => throw new IllegalArgumentException("unsupported depth")
}))
}
@LuaCallback(value = "maxDepth", direct = true)
def maxDepth(context: Context, args: Arguments): Array[AnyRef] =
screen(s => result(PackedColor.Depth(maxDepth.id min s.maxDepth.id) match {
case PackedColor.Depth.OneBit => 1
case PackedColor.Depth.FourBit => 4
case PackedColor.Depth.EightBit => 8
}))
@LuaCallback(value = "getResolution", direct = true)
def getResolution(context: Context, args: Arguments): Array[AnyRef] =
screen(s => {
@ -62,10 +118,8 @@ class GraphicsCard(val maxResolution: (Int, Int)) extends ManagedComponent {
val w = args.checkInteger(0)
val h = args.checkInteger(1)
val (mw, mh) = maxResolution
if (w > 0 && h > 0 && w <= mw && h <= mh)
screen(s => result(s.resolution = (w, h)))
else
Array(Unit, "unsupported resolution")
if (w > 0 && h > 0 && w <= mw && h <= mh) screen(s => result(s.resolution = (w, h)))
else throw new IllegalArgumentException("unsupported resolution")
}
@LuaCallback(value = "maxResolution", direct = true)

View File

@ -12,7 +12,7 @@ object GraphicsCard extends Item {
override def createEnvironment(item: ItemStack, container: AnyRef) =
Items.multi.subItem(item) match {
case Some(gpu: common.item.GraphicsCard) =>
new component.GraphicsCard(gpu.maxResolution)
new component.GraphicsCard(gpu.maxResolution, gpu.maxDepth)
case _ => null
}

View File

@ -3,7 +3,7 @@ package li.cil.oc.util
object PackedColor {
object Depth extends Enumeration {
val OneBit, EightBit, SixteenBit = Value
val OneBit, FourBit, EightBit = Value
}
private val rMask32 = 0xFF0000
@ -13,88 +13,65 @@ object PackedColor {
private val gShift32 = 8
private val bShift32 = 0
// 7 6 5 4 3 2 1 0
// r r r g g g b b : 3.3.2
private val rMask8 = Integer.parseInt("11100000", 2)
private val gMask8 = Integer.parseInt("00011100", 2)
private val bMask8 = Integer.parseInt("00000011", 2)
private val rShift8 = 5
private val gShift8 = 2
private val bShift8 = 0
private val rScale8 = 255.0 / 0x7
private val gScale8 = 255.0 / 0x7
private val bScale8 = 255.0 / 0x3
private abstract class ColorFormat {
def inflate(value: Int): Int
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
// r r r r r g g g g g g b b b b b : 5.6.5
private val rMask16 = Integer.parseInt("1111100000000000", 2)
private val gMask16 = Integer.parseInt("0000011111100000", 2)
private val bMask16 = Integer.parseInt("0000000000011111", 2)
private val rShift16 = 11
private val gShift16 = 5
private val bShift16 = 0
private val rScale16 = 255.0 / 0x1F
private val gScale16 = 255.0 / 0x3F
private val bScale16 = 255.0 / 0x1F
private def extractFrom1Bit(c: Short) = if (c == 0) 0x000000 else 0xFFFFFF
private def compressTo1Bit(c: Int) = (if (c == 0) 0 else 1).toShort
private def extractFrom8Bit(c: Short) = {
val r = ((((c & rMask8) >>> rShift8) * rScale8).toInt << rShift32) & rMask32
val g = ((((c & gMask8) >>> gShift8) * gScale8).toInt << gShift32) & gMask32
val b = ((((c & bMask8) >>> bShift8) * bScale8).toInt << bShift32) & bMask32
r | g | b
def deflate(value: Int): Int
}
private def compressTo8Bit(c: Int) = {
val r = ((((c & rMask32) >>> rShift32) / rScale8).toInt << rShift8) & rMask8
val g = ((((c & gMask32) >>> gShift32) / gScale8).toInt << gShift8) & gMask8
val b = ((((c & bMask32) >>> bShift32) / bScale8).toInt << bShift8) & bMask8
(r | g | b).toShort
private class SingleBitFormat extends ColorFormat {
def inflate(value: Int) = if (value == 0) 0x000000 else 0xFFFFFF
def deflate(value: Int) = if (value == 0) 0 else 1
}
private def extractFrom16Bit(c: Short) = {
val r = ((((c & rMask16) >>> rShift16) * rScale16).toInt << rShift32) & rMask32
val g = ((((c & gMask16) >>> gShift16) * gScale16).toInt << gShift32) & gMask32
val b = ((((c & bMask16) >>> bShift16) * bScale16).toInt << bShift32) & bMask32
r | g | b
}
private class MultiBitFormat(rBits: Int, gBits: Int, bBits: Int) extends ColorFormat {
def mask(nBits: Int) = 0xFFFFFFFF >>> (32 - nBits)
private def compressTo16Bit(c: Int) = {
val r = ((((c & rMask32) >>> rShift32) / rScale16).toInt << rShift16) & rMask16
val g = ((((c & gMask32) >>> gShift32) / gScale16).toInt << gShift16) & gMask16
val b = ((((c & bMask32) >>> bShift32) / bScale16).toInt << bShift16) & bMask16
(r | g | b).toShort
}
private val bShift = 0
private val gShift = bBits
private val rShift = gShift + gBits
// Colors are packed: 0xFFFFBBBB (F = foreground, B = background)
private val fgShift = 16
private val bgShift = 0
private val bMask = mask(bBits) << bShift
private val gMask = mask(gBits) << gShift
private val rMask = mask(rBits) << rShift
def pack(foreground: Int, background: Int, depth: Depth.Value) =
depth match {
case Depth.OneBit => (compressTo1Bit(foreground) << fgShift) | (compressTo1Bit(background) << bgShift)
case Depth.EightBit => (compressTo8Bit(foreground) << fgShift) | (compressTo8Bit(background) << bgShift)
case Depth.SixteenBit => (compressTo16Bit(foreground) << fgShift) | (compressTo16Bit(background) << bgShift)
private val bScale = 255.0 / ((1 << bBits) - 1)
private val gScale = 255.0 / ((1 << gBits) - 1)
private val rScale = 255.0 / ((1 << rBits) - 1)
def inflate(value: Int) = {
val r = ((((value & rMask) >>> rShift) * rScale).toInt << rShift32) & rMask32
val g = ((((value & gMask) >>> gShift) * gScale).toInt << gShift32) & gMask32
val b = ((((value & bMask) >>> bShift) * bScale).toInt << bShift32) & bMask32
r | g | b
}
def unpackForeground(color: Int, depth: Depth.Value) = {
val c = (color >>> fgShift).toShort
depth match {
case Depth.OneBit => extractFrom1Bit(c)
case Depth.EightBit => extractFrom8Bit(c)
case Depth.SixteenBit => extractFrom16Bit(c)
def deflate(value: Int) = {
val r = ((((value & rMask32) >>> rShift32) / rScale).toInt << rShift) & rMask
val g = ((((value & gMask32) >>> gShift32) / gScale).toInt << gShift) & gMask
val b = ((((value & bMask32) >>> bShift32) / bScale).toInt << bShift) & bMask
r | g | b
}
}
def unpackBackground(color: Int, depth: Depth.Value) = {
val c = (color >>> bgShift).toShort
depth match {
case Depth.OneBit => extractFrom1Bit(c)
case Depth.EightBit => extractFrom8Bit(c)
case Depth.SixteenBit => extractFrom16Bit(c)
}
private val formats = Map(
Depth.OneBit -> new SingleBitFormat(),
Depth.FourBit -> new MultiBitFormat(1, 2, 1),
Depth.EightBit -> new MultiBitFormat(3, 3, 2))
// Colors are packed: 0xFFBB (F = foreground, B = background)
private val fgShift = 8
private val bgMask = 0x000000FF
def pack(foreground: Int, background: Int, depth: Depth.Value) = {
val format = formats(depth)
((format.deflate(foreground) << fgShift) | format.deflate(background)).toShort
}
def unpackForeground(color: Short, depth: Depth.Value) =
formats(depth).inflate((color & 0xFFFF) >>> fgShift)
def unpackBackground(color: Short, depth: Depth.Value) =
formats(depth).inflate(color & bgMask)
}

View File

@ -24,7 +24,7 @@ object RenderState {
def makeItBlend() {
GL11.glEnable(GL11.GL_BLEND)
GL11.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_COLOR)
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
GL11.glDepthFunc(GL11.GL_LEQUAL)
}

View File

@ -1,6 +1,6 @@
package li.cil.oc.util
import net.minecraft.nbt.{NBTTagIntArray, NBTTagCompound, NBTTagList, NBTTagString}
import net.minecraft.nbt._
/**
* This stores chars in a 2D-Array and provides some manipulation functions.
@ -10,27 +10,49 @@ import net.minecraft.nbt.{NBTTagIntArray, NBTTagCompound, NBTTagList, NBTTagStri
* relatively fast updates, given a smart algorithm (using copy()/fill()
* instead of set()ing everything).
*/
class TextBuffer(var width: Int, var height: Int, val depth: PackedColor.Depth.Value) {
class TextBuffer(var width: Int, var height: Int, initialDepth: PackedColor.Depth.Value) extends Persistable {
def this(size: (Int, Int), depth: PackedColor.Depth.Value) = this(size._1, size._2, depth)
private var depth_ = initialDepth
private var foreground_ = 0xFFFFFF
private var background_ = 0x000000
private var packed = PackedColor.pack(foreground_, background_, depth)
private var packed = PackedColor.pack(foreground_, background_, depth_)
def foreground = foreground_
def foreground_=(value: Int) = {
foreground_ = value
packed = PackedColor.pack(foreground_, background_, depth)
packed = PackedColor.pack(foreground_, background_, depth_)
}
def background = background_
def background_=(value: Int) = {
background_ = value
packed = PackedColor.pack(foreground_, background_, depth)
packed = PackedColor.pack(foreground_, background_, depth_)
}
def depth = depth_
def depth_=(value: PackedColor.Depth.Value) = {
if (depth != value) {
for (row <- 0 until height) {
val rowColor = color(row)
for (col <- 0 until width) {
val packed = rowColor(col)
val fg = PackedColor.unpackForeground(packed, depth_)
val bg = PackedColor.unpackBackground(packed, depth_)
rowColor(col) = PackedColor.pack(fg, bg, value)
}
}
depth_ = value
packed = PackedColor.pack(foreground_, background_, depth_)
true
}
else false
}
var color = Array.fill(height, width)(packed)
@ -143,33 +165,50 @@ class TextBuffer(var width: Int, var height: Int, val depth: PackedColor.Depth.V
changed
}
def readFromNBT(nbt: NBTTagCompound): Unit = {
override def load(nbt: NBTTagCompound): Unit = {
val w = nbt.getInteger("width")
val h = nbt.getInteger("height")
size = (w, h)
val b = nbt.getTagList("buffer")
for (i <- 0 until (h min b.tagCount)) {
val line = b.tagAt(i).asInstanceOf[NBTTagString].data
set(0, i, line)
}
depth_ = PackedColor.Depth(nbt.getInteger("depth"))
foreground = nbt.getInteger("foreground")
background = nbt.getInteger("background")
val c = nbt.getTagList("color")
for (i <- 0 until (h min c.tagCount)) {
val line = c.tagAt(i).asInstanceOf[NBTTagIntArray].intArray
Array.copy(line, 0, color(i), 0, line.length min width)
for (i <- 0 until h) {
val rowColor = color(i)
for (j <- 0 until w) {
rowColor(j) = c.tagAt(j + i * w).asInstanceOf[NBTTagShort].data
}
}
}
def writeToNBT(nbt: NBTTagCompound): Unit = {
override def save(nbt: NBTTagCompound): Unit = {
nbt.setInteger("width", width)
nbt.setInteger("height", height)
val b = new NBTTagList()
for (i <- 0 until height) {
b.appendTag(new NBTTagString(null, String.valueOf(buffer(i))))
}
nbt.setTag("buffer", b)
nbt.setInteger("depth", depth_.id)
nbt.setInteger("foreground", foreground_)
nbt.setInteger("background", background_)
val c = new NBTTagList()
for (i <- 0 until height) {
c.appendTag(new NBTTagIntArray(null, color(i)))
val rowColor = color(i)
for (j <- 0 until width) {
c.appendTag(new NBTTagShort(null, rowColor(j)))
}
}
nbt.setTag("color", c)
}