diff --git a/src/main/resources/ocelot/desktop/shader/general.frag b/src/main/resources/ocelot/desktop/shader/general.frag index 5fc8d2a..a345802 100644 --- a/src/main/resources/ocelot/desktop/shader/general.frag +++ b/src/main/resources/ocelot/desktop/shader/general.frag @@ -8,5 +8,5 @@ out vec4 outColor; uniform sampler2D uTexture; void main() { - outColor = fColor; + outColor = texture(uTexture, fUV) * fColor; } \ No newline at end of file diff --git a/src/main/resources/ocelot/desktop/spritesheet.png b/src/main/resources/ocelot/desktop/spritesheet.png new file mode 100644 index 0000000..09dbe84 Binary files /dev/null and b/src/main/resources/ocelot/desktop/spritesheet.png differ diff --git a/src/main/resources/ocelot/desktop/spritesheet.txt b/src/main/resources/ocelot/desktop/spritesheet.txt new file mode 100644 index 0000000..7d78de6 --- /dev/null +++ b/src/main/resources/ocelot/desktop/spritesheet.txt @@ -0,0 +1,27 @@ +APU0 0 0 16 16 +APU1 16 0 16 16 +APU2 32 0 16 16 +BorderH 96 32 2 8 +BorderV 98 32 8 2 +CPU0 48 0 16 16 +CPU1 64 0 16 16 +CPU2 80 0 16 16 +CornerBL 64 32 8 8 +CornerBR 72 32 8 8 +CornerTL 80 32 8 8 +CornerTR 88 32 8 8 +EEPROM 96 0 16 16 +Empty 106 32 1 1 +GraphicsCard0 112 0 16 16 +GraphicsCard1 0 16 16 16 +GraphicsCard2 16 16 16 16 +HardDiskDrive0 32 16 16 16 +HardDiskDrive1 48 16 16 16 +HardDiskDrive2 64 16 16 16 +InternetCard 80 16 16 16 +Memory0 96 16 16 16 +Memory1 112 16 16 16 +Memory2 0 32 16 16 +Memory3 16 32 16 16 +Memory4 32 32 16 16 +Memory5 48 32 16 16 diff --git a/src/main/scala/ocelot/desktop/graphics/Graphics.scala b/src/main/scala/ocelot/desktop/graphics/Graphics.scala index 38adf71..7651fad 100644 --- a/src/main/scala/ocelot/desktop/graphics/Graphics.scala +++ b/src/main/scala/ocelot/desktop/graphics/Graphics.scala @@ -1,10 +1,10 @@ package ocelot.desktop.graphics -import ocelot.desktop.color.{Color, RGBAColorNorm} +import ocelot.desktop.color.{Color, IntColor, RGBAColorNorm} import ocelot.desktop.geometry.Transform2D import ocelot.desktop.graphics.mesh.{Mesh, MeshInstance} import ocelot.desktop.graphics.render.InstanceRenderer -import ocelot.desktop.util.FontLoader +import ocelot.desktop.util.{FontLoader, Spritesheet} import org.apache.logging.log4j.scala.Logging import org.lwjgl.opengl.GL11 @@ -24,6 +24,10 @@ class Graphics extends Logging { private var _background = RGBAColorNorm(0f, 0f, 0f, 1f) private var _fontSize: Float = 16f + private var _sprite: String = "Empty" + private var spriteRect = Spritesheet.sprites(_sprite) + private val emptySpriteTrans = Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h) + GL11.glEnable(GL11.GL_DEPTH_TEST) GL11.glDepthFunc(GL11.GL_LEQUAL) GL11.glEnable(GL11.GL_BLEND) @@ -74,6 +78,13 @@ class Graphics extends Logging { _fontSize = value } + def sprite: String = _sprite + + def sprite_=(value: String): Unit = { + _sprite = value + spriteRect = Spritesheet.sprites(_sprite) + } + def text(x: Float, y: Float, text: String): Unit = { var ox = x @@ -105,24 +116,34 @@ class Graphics extends Logging { // ^ dirty hack to avoid edge bleeding, somehow works ) - rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height))) + rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height), emptySpriteTrans)) textRenderer.schedule(MeshInstance(_foreground, z + 1, Transform2D.translate(x, y) >> Transform2D.scale(width, height), uvTransform)) z += 2 } + def sprite(name: String, x: Float, y: Float, width: Float, height: Float): Unit = { + flush() + sprite = name + background = RGBAColorNorm(1f, 1f, 1f, 1f) + rect(x, y, width, height) + } + def rect(x: Float, y: Float, width: Float, height: Float): Unit = { - rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height))) + val uvTransform = Transform2D.translate(spriteRect.x, spriteRect.y) >> Transform2D.scale(spriteRect.w, spriteRect.h) + rectRenderer.schedule(MeshInstance(_background, z, Transform2D.translate(x, y) >> Transform2D.scale(width, height), uvTransform)) z += 1 } def flush(): Unit = { + Spritesheet.texture.bind() rectRenderer.flush() fontAtlas.bind() textRenderer.flush() } def commit(): Unit = { + Spritesheet.texture.bind() rectRenderer.commit() fontAtlas.bind() textRenderer.commit() diff --git a/src/main/scala/ocelot/desktop/graphics/Texture.scala b/src/main/scala/ocelot/desktop/graphics/Texture.scala index bd15042..8be0d9b 100644 --- a/src/main/scala/ocelot/desktop/graphics/Texture.scala +++ b/src/main/scala/ocelot/desktop/graphics/Texture.scala @@ -1,9 +1,11 @@ package ocelot.desktop.graphics +import java.awt.image.BufferedImage import java.nio.ByteBuffer import ocelot.desktop.util.{Resource, ResourceManager} import org.apache.logging.log4j.scala.Logging +import org.lwjgl.BufferUtils import org.lwjgl.opengl._ import org.lwjgl.system.MemoryUtil.NULL @@ -21,6 +23,31 @@ class Texture extends Logging with Resource { 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) + def this(image: BufferedImage) { + this() + + val pixels = new Array[Int](image.getWidth * image.getHeight) + image.getRGB(0, 0, image.getWidth, image.getHeight, pixels, 0, image.getWidth) + + val buf = BufferUtils.createByteBuffer(image.getWidth * image.getHeight * 4) + + for (y <- 0 until image.getHeight) { + for (x <- 0 until image.getWidth) { + val pixel = pixels(y * image.getWidth + x) + buf.put(((pixel >> 16) & 0xFF).toByte) + buf.put(((pixel >> 8) & 0xFF).toByte) + buf.put((pixel & 0xFF).toByte) + buf.put(((pixel >> 24) & 0xFF).toByte) + } + } + + buf.flip() + + bind() + GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, image.getWidth, image.getHeight, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf) + GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D) + } + def this(width: Int, height: Int, format: Int, dataType: Int) = { this() bind() diff --git a/src/main/scala/ocelot/desktop/ui/UiHandler.scala b/src/main/scala/ocelot/desktop/ui/UiHandler.scala index c5310e2..2b6b04d 100644 --- a/src/main/scala/ocelot/desktop/ui/UiHandler.scala +++ b/src/main/scala/ocelot/desktop/ui/UiHandler.scala @@ -5,7 +5,7 @@ import java.nio.{ByteBuffer, IntBuffer} import ocelot.desktop.geometry.{Size2D, Vector2D} import ocelot.desktop.graphics.Graphics import ocelot.desktop.ui.widget.RootWidget -import ocelot.desktop.util.{Audio, FPSCalculator, FontLoader} +import ocelot.desktop.util.{Audio, FPSCalculator, FontLoader, Spritesheet} import org.apache.logging.log4j.scala.Logging import org.lwjgl.glfw.{GLFW, GLFWErrorCallback} import org.lwjgl.openal._ @@ -75,6 +75,7 @@ class UiHandler(val root: RootWidget) extends Logging { logger.info(s"OpenGL renderer: ${GL11.glGetString(GL11.GL_RENDERER)}") FontLoader.init() + Spritesheet.load() graphics = new Graphics soundDevice = ALC10.alcOpenDevice(null.asInstanceOf[ByteBuffer]) diff --git a/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala index 5f8ad0b..125b66a 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/ScreenWidget.scala @@ -14,17 +14,17 @@ import scala.collection.mutable class ScreenWidget(screen: Screen) extends RootWidget with Logging { private val fontSize = 16f - var width = 80 - var height = 25 + var width = screen.getWidth + var height = screen.getHeight var background: Int = 0x000000 var foreground: Int = 0xFFFFFF private var data: Array[Cell] = Array.fill(width * height)(Cell(' ', background, foreground)) - override def minimumSize: Option[Size2D] = Some(Size2D(width * fontSize / 2f, height * fontSize)) + override def minimumSize: Option[Size2D] = Some(Size2D(width * fontSize / 2f + 32, height * fontSize + 32)) - override def maximumSize: Option[Size2D] = Some(Size2D(width * fontSize, height * fontSize * 2f)) + override def maximumSize: Option[Size2D] = minimumSize private var lastMousePos = Vector2D(0, 0) private var pressedMouseButtons = new mutable.HashSet[MouseEvent.Button.Value]() @@ -63,7 +63,7 @@ class ScreenWidget(screen: Screen) extends RootWidget with Logging { screen.mouseScroll(lastMousePos.x, lastMousePos.y, event.offset, User("myself")) } - def convertMousePos(p: Vector2D): Vector2D = Vector2D(p.x / fontSize * 2f, p.y / fontSize) + def convertMousePos(p: Vector2D): Vector2D = Vector2D((p.x - 16) / fontSize * 2f, (p.y - 16) / fontSize) override def update(): Unit = { val currentMousePos = convertMousePos(uiHandler.mousePosition) @@ -79,10 +79,14 @@ class ScreenWidget(screen: Screen) extends RootWidget with Logging { override def windowTitle: String = f"Ocelot Desktop [FPS: ${uiHandler.fps}%2.3f]" override def draw(g: Graphics): Unit = { - val tx = size.width / 2 - fontSize * width / 4 - val ty = size.height / 2 - fontSize * height / 2 + val w = math.round(fontSize * width / 2f) + 32 + val h = math.round(fontSize * height) + 32 + + g.clear(0, 0, w, h) + g.background = IntColor(0x333333) + g.sprite = "Empty" + g.rect(0, 0, w, h) - g.clear(0, 0, math.round(fontSize * width / 2f), math.round(fontSize * height)) g.fontSize = fontSize for (y <- 0 until height) { @@ -91,10 +95,19 @@ class ScreenWidget(screen: Screen) extends RootWidget with Logging { val Cell(char, bg, fg) = data(y * width + x) g.background = IntColor(bg) g.foreground = IntColor(fg) - g.char(tx + x * fontSize / 2f, ty + y * fontSize, char) + g.char(x * fontSize / 2f + 16, y * fontSize + 16, char) } } } + + g.sprite("CornerTL", 0, 0, 16,16) + g.sprite("CornerTR", w - 16, 0, 16,16) + g.sprite("CornerBL", 0, h - 16, 16,16) + g.sprite("CornerBR", w - 16, h - 16, 16,16) + g.sprite("BorderH", 16, 0, w - 32, 16) + g.sprite("BorderH", 16, h - 16, w - 32, 16) + g.sprite("BorderV", 0, 16, 16, h - 32) + g.sprite("BorderV", w - 16, 16, 16, h - 32) } def set(x: Int, y: Int, text: String, vertical: Boolean): Unit = { diff --git a/src/main/scala/ocelot/desktop/util/Spritesheet.scala b/src/main/scala/ocelot/desktop/util/Spritesheet.scala new file mode 100644 index 0000000..082e3d0 --- /dev/null +++ b/src/main/scala/ocelot/desktop/util/Spritesheet.scala @@ -0,0 +1,38 @@ +package ocelot.desktop.util + +import javax.imageio.ImageIO +import ocelot.desktop.graphics.Texture +import org.apache.logging.log4j.scala.Logging + +import scala.collection.mutable +import scala.io.Source + +object Spritesheet extends Logging { + val sprites = new mutable.HashMap[String, Rect]() + var texture: Texture = _ + + def load(): Unit = { + logger.info("Loading sprites") + + val imageURL = getClass.getResource("/ocelot/desktop/spritesheet.png") + val image = ImageIO.read(imageURL) + texture = new Texture(image) + + val txt = Source.fromURL(getClass.getResource("/ocelot/desktop/spritesheet.txt")) + + for (line <- txt.getLines) { + val split = line.split("\\s+") + sprites += (split.head -> Rect( + split(1).toFloat / image.getWidth.toFloat, + split(2).toFloat / image.getHeight.toFloat, + split(3).toFloat / image.getWidth.toFloat, + split(4).toFloat / image.getHeight.toFloat + )) + logger.info(s"${split.head} -> ${sprites(split.head)}") + } + + txt.close() + + logger.info(s"Loaded ${sprites.size} sprites") + } +}