[Editor] Enable displaying map
This commit is contained in:
@@ -34,4 +34,5 @@ compileTestKotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||||
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
|
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
|
||||||
|
implementation "org.joml:joml:${jomlVersion}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
package com.bartlomiejpluta.base.editor
|
package com.bartlomiejpluta.base.editor
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.view.main.MainView
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
|
|
||||||
class MyView : View() {
|
class EditorApp : App(MainView::class)
|
||||||
override val root = vbox {
|
|
||||||
button("Press me")
|
|
||||||
label("Waiting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditorApp : App(MyView::class)
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
launch<EditorApp>(args)
|
launch<EditorApp>(args)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.controller.map
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.controller.tileset.TileSetController
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.GameMap
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.tileset.TileSet
|
||||||
|
import tornadofx.Controller
|
||||||
|
|
||||||
|
class MapController : Controller() {
|
||||||
|
private val tileSetController: TileSetController by inject()
|
||||||
|
|
||||||
|
val map = GameMap(tileSetController.tileset, 20, 20)
|
||||||
|
.createTileLayer(0)
|
||||||
|
.createTileLayer(3, 5)
|
||||||
|
.createTileLayer(3, 5)
|
||||||
|
.createTileLayer(3, 5)
|
||||||
|
.createTileLayer(3, 5)
|
||||||
|
.createTileLayer(3, 5)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.controller.tileset
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.tileset.TileSet
|
||||||
|
import tornadofx.Controller
|
||||||
|
|
||||||
|
class TileSetController : Controller() {
|
||||||
|
val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.model.map.layer
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.view.render.Renderable
|
||||||
|
|
||||||
|
interface Layer : Renderable
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.model.map.layer
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.tileset.Tile
|
||||||
|
import javafx.scene.canvas.GraphicsContext
|
||||||
|
|
||||||
|
class TileLayer(private val layer: Array<Array<Tile?>>) : Layer {
|
||||||
|
|
||||||
|
fun setTile(row: Int, column: Int, tile: Tile?) = apply { layer[row][column] = tile }
|
||||||
|
|
||||||
|
override fun render(gc: GraphicsContext) {
|
||||||
|
for ((row, columns) in layer.withIndex()) {
|
||||||
|
for ((column, tile) in columns.withIndex()) {
|
||||||
|
if (tile != null) {
|
||||||
|
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.model.map
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.layer.Layer
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.layer.TileLayer
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.tileset.Tile
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.tileset.TileSet
|
||||||
|
import com.bartlomiejpluta.base.editor.view.render.Renderable
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
import javafx.scene.canvas.GraphicsContext
|
||||||
|
|
||||||
|
class Grid(private val tileSet: TileSet, private val rows: Int, private val columns: Int) : Renderable {
|
||||||
|
private var tileWidth = tileSet.tileWidth.toDouble()
|
||||||
|
private var tileHeight = tileSet.tileHeight.toDouble()
|
||||||
|
private var mapWidth = columns * tileWidth
|
||||||
|
private var mapHeight = rows * tileHeight
|
||||||
|
|
||||||
|
override fun render(gc: GraphicsContext) {
|
||||||
|
gc.lineWidth = LINE_WIDTH
|
||||||
|
|
||||||
|
for (row in 0 until rows) {
|
||||||
|
gc.strokeLine(0.0, row * tileHeight, mapWidth, row * tileHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (column in 0 until columns) {
|
||||||
|
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, mapHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val LINE_WIDTH = 1.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GameMap(private val tileSet: TileSet, private val rows: Int, private val columns: Int) : Renderable {
|
||||||
|
val layers = mutableListOf<Layer>()
|
||||||
|
private val grid = Grid(tileSet, rows, columns)
|
||||||
|
|
||||||
|
val width = columns * tileSet.tileWidth
|
||||||
|
|
||||||
|
val height = columns * tileSet.tileWidth
|
||||||
|
|
||||||
|
fun createTileLayer(tile: Int) = createTileLayer().apply {
|
||||||
|
val layerId = layers.size - 1
|
||||||
|
for (row in 0 until rows) {
|
||||||
|
for (column in 0 until columns) {
|
||||||
|
setTile(layerId, row, column, tile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTileLayer(tileRow: Int, tileColumn: Int) = createTileLayer().apply {
|
||||||
|
val layerId = layers.size - 1
|
||||||
|
for (row in 0 until rows) {
|
||||||
|
for (column in 0 until columns) {
|
||||||
|
setTile(layerId, row, column, tileRow, tileColumn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createTileLayer() = apply { layers.add(TileLayer(Array(rows) { Array(columns) { null } })) }
|
||||||
|
|
||||||
|
fun setTile(layer: Int, row: Int, column: Int, tile: Int) = apply {
|
||||||
|
(layers[layer] as TileLayer).setTile(row, column, tileSet.getTile(tile))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTile(layer: Int, row: Int, column: Int, tileRow: Int, tileColumn: Int) = apply {
|
||||||
|
(layers[layer] as TileLayer).setTile(row, column, tileSet.getTile(tileRow, tileColumn))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(gc: GraphicsContext) {
|
||||||
|
layers.forEach { it.render(gc) }
|
||||||
|
grid.render(gc)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.model.map.tileset
|
||||||
|
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
|
||||||
|
import javafx.scene.image.PixelReader
|
||||||
|
|
||||||
|
import javafx.scene.image.WritableImage
|
||||||
|
import javafx.scene.image.PixelWriter
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
|
||||||
|
|
||||||
|
class Tile(
|
||||||
|
tileSet: TileSet,
|
||||||
|
row: Int,
|
||||||
|
column: Int,
|
||||||
|
val image: Image,
|
||||||
|
) {
|
||||||
|
val id = row * tileSet.columns + column
|
||||||
|
|
||||||
|
private fun cloneImage(image: Image): Image {
|
||||||
|
val height = image.height.toInt()
|
||||||
|
val width = image.width.toInt()
|
||||||
|
val pixelReader = image.pixelReader
|
||||||
|
val writableImage = WritableImage(width, height)
|
||||||
|
val pixelWriter = writableImage.pixelWriter
|
||||||
|
|
||||||
|
for (y in 0 until height) {
|
||||||
|
for (x in 0 until width) {
|
||||||
|
val color = pixelReader.getColor(x, y)
|
||||||
|
pixelWriter.setColor(x, y, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writableImage
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scale(image: Image, factor: Int): Image {
|
||||||
|
val width = image.width.toInt()
|
||||||
|
val height = image.height.toInt()
|
||||||
|
val output = WritableImage(width * factor, height * factor)
|
||||||
|
|
||||||
|
val reader: PixelReader = image.pixelReader
|
||||||
|
val writer = output.pixelWriter
|
||||||
|
|
||||||
|
for (y in 0 until height) {
|
||||||
|
for (x in 0 until width) {
|
||||||
|
val argb = reader.getArgb(x, y)
|
||||||
|
for (dy in 0 until factor) {
|
||||||
|
for (dx in 0 until factor) {
|
||||||
|
writer.setArgb(x * factor + dx, y * factor + dy, argb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.model.map.tileset
|
||||||
|
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
import javafx.scene.image.PixelFormat
|
||||||
|
import javafx.scene.image.WritableImage
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
|
||||||
|
class TileSet(private val image: Image, val rows: Int, val columns: Int) {
|
||||||
|
val tileWidth = image.width.toInt() / columns
|
||||||
|
|
||||||
|
val tileHeight = image.height.toInt() / rows
|
||||||
|
|
||||||
|
private val tiles: Array<Array<Tile>> =
|
||||||
|
Array(rows) { row -> Array(columns) { column -> cropTile(row, column) } }
|
||||||
|
|
||||||
|
private fun cropTile(row: Int, column: Int): Tile {
|
||||||
|
val reader = image.pixelReader
|
||||||
|
val buffer = ByteBuffer.allocate(tileWidth * tileHeight * 4)
|
||||||
|
reader.getPixels(column * tileWidth, row * tileHeight, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
|
||||||
|
val tile = WritableImage(tileWidth, tileHeight)
|
||||||
|
val writer = tile.pixelWriter
|
||||||
|
writer.setPixels(0, 0, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
|
||||||
|
|
||||||
|
return Tile(this, row, column, tile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTile(row: Int, column: Int) = tiles[row][column]
|
||||||
|
|
||||||
|
fun getTile(id: Int) = tiles[id / rows][id % columns]
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.view.component.map
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.GameMap
|
||||||
|
import com.bartlomiejpluta.base.editor.view.render.Renderer
|
||||||
|
import javafx.scene.canvas.Canvas
|
||||||
|
|
||||||
|
class MapPane(map: GameMap) : Canvas() {
|
||||||
|
private val renderer = Renderer(graphicsContext2D, map)
|
||||||
|
|
||||||
|
init {
|
||||||
|
width = map.width.toDouble()
|
||||||
|
height = map.height.toDouble()
|
||||||
|
renderer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.view.fragment
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.model.map.GameMap
|
||||||
|
import com.bartlomiejpluta.base.editor.view.component.map.MapPane
|
||||||
|
import tornadofx.Fragment
|
||||||
|
import tornadofx.group
|
||||||
|
import tornadofx.plusAssign
|
||||||
|
import tornadofx.scrollpane
|
||||||
|
|
||||||
|
class MapFragment : Fragment() {
|
||||||
|
val map: GameMap by param()
|
||||||
|
val pane = MapPane(map)
|
||||||
|
|
||||||
|
override val root = scrollpane {
|
||||||
|
prefWidth = 300.0
|
||||||
|
prefHeight = 300.0
|
||||||
|
|
||||||
|
group {
|
||||||
|
group {
|
||||||
|
this += pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt
Executable file
14
editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.view.main
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.controller.map.MapController
|
||||||
|
import com.bartlomiejpluta.base.editor.view.fragment.MapFragment
|
||||||
|
import tornadofx.View
|
||||||
|
import tornadofx.borderpane
|
||||||
|
|
||||||
|
class MainView : View() {
|
||||||
|
private val mapController: MapController by inject()
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
center = find<MapFragment>(mapOf(MapFragment::map to mapController.map)).root
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.view.render
|
||||||
|
|
||||||
|
import javafx.scene.canvas.GraphicsContext
|
||||||
|
|
||||||
|
interface Renderable {
|
||||||
|
fun render(gc: GraphicsContext)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.view.render
|
||||||
|
|
||||||
|
import javafx.animation.AnimationTimer
|
||||||
|
import javafx.scene.canvas.GraphicsContext
|
||||||
|
|
||||||
|
class Renderer(
|
||||||
|
private val gc: GraphicsContext,
|
||||||
|
private val renderable: Renderable
|
||||||
|
) : AnimationTimer() {
|
||||||
|
private var previous = System.nanoTime()
|
||||||
|
|
||||||
|
override fun handle(now: Long) {
|
||||||
|
val dt = (now - previous) / 1000000000.0
|
||||||
|
previous = now
|
||||||
|
gc.isImageSmoothing = false
|
||||||
|
|
||||||
|
render()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun render() {
|
||||||
|
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height);
|
||||||
|
|
||||||
|
renderable.render(gc)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,4 +3,4 @@ springBootVersion=2.4.2
|
|||||||
springDependencyManagementVersion=1.0.11.RELEASE
|
springDependencyManagementVersion=1.0.11.RELEASE
|
||||||
jomlVersion=1.10.0
|
jomlVersion=1.10.0
|
||||||
guavaVersion=29.0-jre
|
guavaVersion=29.0-jre
|
||||||
tornadoFxVersion=2.0.0-SNAPSHOT
|
tornadoFxVersion=2.0.0-SNAPSHOT
|
||||||
Reference in New Issue
Block a user