[Editor] Make the Auto Tile working
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
package com.bartlomiejpluta.base.editor.autotile.asset
|
package com.bartlomiejpluta.base.editor.autotile.asset
|
||||||
|
|
||||||
import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset
|
import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset
|
||||||
|
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
|
||||||
import com.bartlomiejpluta.base.editor.project.model.Project
|
import com.bartlomiejpluta.base.editor.project.model.Project
|
||||||
|
|
||||||
class AutoTileAsset(project: Project, uid: String, source: String, name: String) :
|
class AutoTileAsset(project: Project, uid: String, source: String, name: String) :
|
||||||
GraphicAsset(project.autoTilesDirectoryProperty, uid, source, name, 6, 4)
|
GraphicAsset(project.autoTilesDirectoryProperty, uid, source, name, AutoTile.ROWS, AutoTile.COLUMNS)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.bartlomiejpluta.base.editor.autotile.asset
|
package com.bartlomiejpluta.base.editor.autotile.asset
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
|
||||||
import javafx.beans.property.SimpleIntegerProperty
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
@@ -11,10 +12,10 @@ class AutoTileAssetData {
|
|||||||
val nameProperty = SimpleStringProperty()
|
val nameProperty = SimpleStringProperty()
|
||||||
var name by nameProperty
|
var name by nameProperty
|
||||||
|
|
||||||
val rowsProperty = SimpleIntegerProperty(6)
|
val rowsProperty = SimpleIntegerProperty(AutoTile.ROWS)
|
||||||
var rows by rowsProperty
|
var rows by rowsProperty
|
||||||
|
|
||||||
val columnsProperty = SimpleIntegerProperty(4)
|
val columnsProperty = SimpleIntegerProperty(AutoTile.COLUMNS)
|
||||||
var columns by columnsProperty
|
var columns by columnsProperty
|
||||||
|
|
||||||
val tileWidthProperty = SimpleIntegerProperty(1)
|
val tileWidthProperty = SimpleIntegerProperty(1)
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package com.bartlomiejpluta.base.editor.autotile.model
|
package com.bartlomiejpluta.base.editor.autotile.model
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
|
||||||
|
import com.bartlomiejpluta.base.editor.util.ImageUtil
|
||||||
import javafx.beans.property.ReadOnlyStringWrapper
|
import javafx.beans.property.ReadOnlyStringWrapper
|
||||||
import javafx.beans.property.SimpleIntegerProperty
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import javafx.scene.image.Image
|
import javafx.scene.image.Image
|
||||||
|
import tornadofx.div
|
||||||
import tornadofx.getValue
|
import tornadofx.getValue
|
||||||
|
|
||||||
|
// Algorithm source: https://love2d.org/forums/viewtopic.php?t=7826
|
||||||
class AutoTile(uid: String, name: String, image: Image) {
|
class AutoTile(uid: String, name: String, image: Image) {
|
||||||
val uidProperty = ReadOnlyStringWrapper(uid)
|
val uidProperty = ReadOnlyStringWrapper(uid)
|
||||||
val uid by uidProperty
|
val uid by uidProperty
|
||||||
@@ -17,10 +21,10 @@ class AutoTile(uid: String, name: String, image: Image) {
|
|||||||
val imageProperty = SimpleObjectProperty(image)
|
val imageProperty = SimpleObjectProperty(image)
|
||||||
val image by imageProperty
|
val image by imageProperty
|
||||||
|
|
||||||
val rowsProperty = SimpleIntegerProperty(6)
|
val rowsProperty = SimpleIntegerProperty(ROWS)
|
||||||
val rows by rowsProperty
|
val rows by rowsProperty
|
||||||
|
|
||||||
val columnsProperty = SimpleIntegerProperty(4)
|
val columnsProperty = SimpleIntegerProperty(COLUMNS)
|
||||||
val columns by columnsProperty
|
val columns by columnsProperty
|
||||||
|
|
||||||
val tileWidthProperty = SimpleIntegerProperty(image.width.toInt() / columns)
|
val tileWidthProperty = SimpleIntegerProperty(image.width.toInt() / columns)
|
||||||
@@ -34,4 +38,121 @@ class AutoTile(uid: String, name: String, image: Image) {
|
|||||||
|
|
||||||
val heightProperty = SimpleIntegerProperty(tileHeight * rows)
|
val heightProperty = SimpleIntegerProperty(tileHeight * rows)
|
||||||
val height by heightProperty
|
val height by heightProperty
|
||||||
|
|
||||||
|
val halfWidthProperty = tileWidthProperty.div(2)
|
||||||
|
val halfWidth by halfWidthProperty
|
||||||
|
|
||||||
|
val halfHeightProperty = tileWidthProperty.div(2)
|
||||||
|
val halfHeight by halfHeightProperty
|
||||||
|
|
||||||
|
val islandSubTiles: Array<Image>
|
||||||
|
val topLeftSubTiles: Array<Image>
|
||||||
|
val topRightSubTiles: Array<Image>
|
||||||
|
val bottomLeftSubTiles: Array<Image>
|
||||||
|
val bottomRightSubTiles: Array<Image>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val islandTile = cropTile(0, 0)
|
||||||
|
val crossTile = cropTile(1, 0)
|
||||||
|
val topLeftCornerTile = cropTile(0, 1)
|
||||||
|
val topRightCornerTile = cropTile(1, 1)
|
||||||
|
val bottomLeftCornerTile = cropTile(0, 2)
|
||||||
|
val bottomRightCornerTile = cropTile(1, 2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indexes:
|
||||||
|
* 0 - No connected tiles
|
||||||
|
* 1 - Left tile is connected
|
||||||
|
* 2 - Right tile is connected
|
||||||
|
* 3 - Left and Right tiles are connected
|
||||||
|
* 4 - Left, Right, and Center tiles are connected.
|
||||||
|
*/
|
||||||
|
val (tl3, tr3, bl3, br3) = cutSubTiles(crossTile)
|
||||||
|
val (tl0, tr2, bl1, br4) = cutSubTiles(topLeftCornerTile)
|
||||||
|
val (tl1, tr0, bl4, br2) = cutSubTiles(topRightCornerTile)
|
||||||
|
val (tl2, tr4, bl0, br1) = cutSubTiles(bottomLeftCornerTile)
|
||||||
|
val (tl4, tr1, bl2, br0) = cutSubTiles(bottomRightCornerTile)
|
||||||
|
|
||||||
|
islandSubTiles = cutSubTiles(islandTile)
|
||||||
|
topLeftSubTiles = arrayOf(tl0, tl1, tl2, tl3, tl4)
|
||||||
|
topRightSubTiles = arrayOf(tr0, tr1, tr2, tr3, tr4)
|
||||||
|
bottomLeftSubTiles = arrayOf(bl0, bl1, bl2, bl3, bl4)
|
||||||
|
bottomRightSubTiles = arrayOf(br0, br1, br2, br3, br4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTile(layer: AutoTileLayer, row: Int, column: Int): Array<Image> {
|
||||||
|
var topLeft = 0
|
||||||
|
var topRight = 0
|
||||||
|
var bottomLeft = 0
|
||||||
|
var bottomRight = 0
|
||||||
|
|
||||||
|
// Top
|
||||||
|
if (row > 0 && layer.layer[row - 1][column]) {
|
||||||
|
topLeft += 2
|
||||||
|
topRight += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
if (row < layer.rows - 1 && layer.layer[row + 1][column]) {
|
||||||
|
bottomLeft += 1
|
||||||
|
bottomRight += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left
|
||||||
|
if (column > 0 && layer.layer[row][column - 1]) {
|
||||||
|
topLeft += 1
|
||||||
|
bottomLeft += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
if (column < layer.columns - 1 && layer.layer[row][column + 1]) {
|
||||||
|
topRight += 2
|
||||||
|
bottomRight += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top left
|
||||||
|
if (row > 0 && column > 0 && layer.layer[row - 1][column - 1] && topLeft == 3) {
|
||||||
|
topLeft = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top right
|
||||||
|
if (row > 0 && column < layer.columns - 1 && layer.layer[row - 1][column + 1] && topRight == 3) {
|
||||||
|
topRight = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom left
|
||||||
|
if (row < layer.rows - 1 && column > 0 && layer.layer[row + 1][column - 1] && bottomLeft == 3) {
|
||||||
|
bottomLeft = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom right
|
||||||
|
if (row < layer.rows - 1 && column < layer.columns - 1 && layer.layer[row + 1][column + 1] && bottomRight == 3) {
|
||||||
|
bottomRight = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topLeft == 0 && topRight == 0 && bottomLeft == 0 && bottomRight == 0) {
|
||||||
|
return islandSubTiles
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrayOf(topLeftSubTiles[topLeft], topRightSubTiles[topRight], bottomLeftSubTiles[bottomLeft], bottomRightSubTiles[bottomRight])
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cropTile(column: Int, row: Int) =
|
||||||
|
ImageUtil.cropImage(image, column * tileWidth, row * tileHeight, tileWidth, tileHeight)
|
||||||
|
|
||||||
|
private fun cutSubTiles(tile: Image): Array<Image> {
|
||||||
|
val halfWidth = tileWidth / 2
|
||||||
|
val halfHeight = tileHeight / 2
|
||||||
|
val topLeft = ImageUtil.cropImage(tile, 0, 0, halfWidth, halfHeight)
|
||||||
|
val topRight = ImageUtil.cropImage(tile, halfWidth, 0, halfWidth, halfHeight)
|
||||||
|
val bottomLeft = ImageUtil.cropImage(tile, 0, halfHeight, halfWidth, halfHeight)
|
||||||
|
val bottomRight = ImageUtil.cropImage(tile, halfWidth, halfHeight, halfWidth, halfHeight)
|
||||||
|
|
||||||
|
return arrayOf(topLeft, topRight, bottomLeft, bottomRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ROWS = 3
|
||||||
|
const val COLUMNS = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.bartlomiejpluta.base.editor.map.canvas
|
package com.bartlomiejpluta.base.editor.map.canvas
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
|
||||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.ImageLayerMode
|
import com.bartlomiejpluta.base.editor.map.model.enumeration.ImageLayerMode
|
||||||
import com.bartlomiejpluta.base.editor.map.model.layer.*
|
import com.bartlomiejpluta.base.editor.map.model.layer.*
|
||||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||||
@@ -129,31 +130,21 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
|||||||
private fun renderAutoTileLayer(gc: GraphicsContext, layer: AutoTileLayer) {
|
private fun renderAutoTileLayer(gc: GraphicsContext, layer: AutoTileLayer) {
|
||||||
for ((row, columns) in layer.layer.withIndex()) {
|
for ((row, columns) in layer.layer.withIndex()) {
|
||||||
for ((column, tile) in columns.withIndex()) {
|
for ((column, tile) in columns.withIndex()) {
|
||||||
|
if(tile) {
|
||||||
if (tile) {
|
renderAutoTile(gc, layer.autoTile, layer, column, row)
|
||||||
gc.fill = Color.BLACK
|
|
||||||
gc.fillOval(column.toDouble() * tileWidth, row.toDouble() * tileHeight, tileWidth, tileHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
var total = 0
|
|
||||||
for (x in max(column - 1, 0) .. min(column + 1, map.rows - 1)) {
|
|
||||||
for (y in max(row - 1, 0) .. min(row + 1, map.columns - 1)) {
|
|
||||||
if (layer.layer[y][x]) {
|
|
||||||
total += 2.0.pow(i).toInt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++i
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.textAlign = TextAlignment.CENTER
|
private fun renderAutoTile(gc: GraphicsContext, autoTile: AutoTile, layer: AutoTileLayer, column: Int, row: Int) {
|
||||||
gc.fill = Color.WHITE
|
val (topLeft, topRight, bottomLeft, bottomRight) = autoTile.getTile(layer, row, column)
|
||||||
gc.stroke = Color.WHITE
|
val x = column * tileWidth
|
||||||
gc.globalAlpha = 1.0
|
val y = row * tileHeight
|
||||||
gc.fillText(total.toString(), column.toDouble() * tileWidth + tileWidth * 0.5 , row.toDouble() * tileHeight + 20)
|
gc.drawImage(topLeft, x, y)
|
||||||
}
|
gc.drawImage(topRight, x + tileWidth/2, y)
|
||||||
}
|
gc.drawImage(bottomLeft, x, y + tileHeight/2)
|
||||||
|
gc.drawImage(bottomRight, x + tileWidth/2, y + tileHeight/2)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderObjectLayer(gc: GraphicsContext, objectLayer: ObjectLayer) {
|
private fun renderObjectLayer(gc: GraphicsContext, objectLayer: ObjectLayer) {
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ class AutoTileLayer(
|
|||||||
var layer = layer
|
var layer = layer
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
var rows = rows
|
||||||
|
private set
|
||||||
|
|
||||||
|
var columns = columns
|
||||||
|
private set
|
||||||
|
|
||||||
val autoTileAssetProperty = autoTileAsset.toProperty()
|
val autoTileAssetProperty = autoTileAsset.toProperty()
|
||||||
var autoTileAsset by autoTileAssetProperty
|
var autoTileAsset by autoTileAssetProperty
|
||||||
|
|
||||||
@@ -30,6 +36,9 @@ class AutoTileLayer(
|
|||||||
override val nameProperty = SimpleStringProperty(name)
|
override val nameProperty = SimpleStringProperty(name)
|
||||||
|
|
||||||
override fun resize(rows: Int, columns: Int) {
|
override fun resize(rows: Int, columns: Int) {
|
||||||
|
this.rows = rows
|
||||||
|
this.columns = columns
|
||||||
|
|
||||||
layer = Array(rows) { row ->
|
layer = Array(rows) { row ->
|
||||||
Array(columns) { column ->
|
Array(columns) { column ->
|
||||||
layer.getOrNull(row)?.getOrNull(column) ?: false
|
layer.getOrNull(row)?.getOrNull(column) ?: false
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.util
|
||||||
|
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
import javafx.scene.image.PixelFormat
|
||||||
|
import javafx.scene.image.WritableImage
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
object ImageUtil {
|
||||||
|
fun cropImage(image: Image, x: Int, y: Int, w: Int, h: Int): Image {
|
||||||
|
val reader = image.pixelReader
|
||||||
|
val buffer = ByteBuffer.allocate(w * h * 4)
|
||||||
|
reader.getPixels(x, y, w, h, PixelFormat.getByteBgraInstance(), buffer, 4 * w)
|
||||||
|
val output = WritableImage(w, h)
|
||||||
|
val writer = output.pixelWriter
|
||||||
|
writer.setPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), buffer, 4 * w)
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user