diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 1e98ef47..768c48da 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -144,17 +144,17 @@ private fun loop() { var y = 0 for (tile in Starbound.tilesAccess.values) { - chunk[x, y + 1] = tile - chunk[x++, y] = tile - chunk[x, y + 1] = tile - chunk[x++, y] = tile - chunk[x, y + 1] = tile - chunk[x++, y] = tile - chunk[x, y + 1] = tile - chunk[x++, y] = tile - chunk[x, y + 1] = tile - chunk[x++, y] = tile - chunk[x, y + 1] = tile + chunk.foreground[x, y + 1] = tile + chunk.foreground[x++, y] = tile + chunk.foreground[x, y + 1] = tile + chunk.foreground[x++, y] = tile + chunk.foreground[x, y + 1] = tile + chunk.foreground[x++, y] = tile + chunk.foreground[x, y + 1] = tile + chunk.foreground[x++, y] = tile + chunk.foreground[x, y + 1] = tile + chunk.foreground[x++, y] = tile + chunk.foreground[x, y + 1] = tile if (x >= 32) { x = 0 diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt index 4e76e033..60551dc9 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/TileDefinition.kt @@ -9,8 +9,7 @@ import org.apache.logging.log4j.LogManager import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.math.Vector2i import ru.dbotthepony.kstarbound.util.Color -import ru.dbotthepony.kstarbound.world.IChunk -import ru.dbotthepony.kstarbound.world.ITileAccess +import ru.dbotthepony.kstarbound.world.ITileGetter import java.io.File data class TileDefinition( @@ -181,7 +180,7 @@ sealed class RenderRule(params: Map) { val matchHue = params["matchHue"] as? Boolean ?: false val inverse = params["inverse"] as? Boolean ?: false - abstract fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean + abstract fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean companion object { fun factory(name: String, params: Map): RenderRule { @@ -221,7 +220,7 @@ sealed class RenderRule(params: Map) { } class RenderRuleEqualsSelf(params: Map) : RenderRule(params) { - override fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + override fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { val otherTile = getter[thisPos + offsetPos] ?: return inverse if (inverse) @@ -232,13 +231,13 @@ class RenderRuleEqualsSelf(params: Map) : RenderRule(params) { } class RenderRuleShadows(params: Map) : RenderRule(params) { - override fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + override fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { return false // TODO } } class RenderRuleConnects(params: Map) : RenderRule(params) { - override fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + override fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { if (inverse) return getter[thisPos + offsetPos] == null @@ -247,13 +246,13 @@ class RenderRuleConnects(params: Map) : RenderRule(params) { } class AlwaysPassingRenderRule(params: Map) : RenderRule(params) { - override fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + override fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { return inverse } } class AlwaysFailingRenderRule(params: Map) : RenderRule(params) { - override fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + override fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { return !inverse } } @@ -274,7 +273,7 @@ data class TileRenderRule( val join: RenderRuleCombination, val pieces: List ) { - fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { + fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i, offsetPos: Vector2i): Boolean { if (join == RenderRuleCombination.ANY) { for (piece in pieces) { if (piece.test(getter, thisRef, thisPos, offsetPos)) { @@ -338,7 +337,7 @@ data class TileRenderMatchPositioned( /** * Состояние [condition] на [thisPos] с [offset] */ - fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i): Boolean { + fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i): Boolean { return condition.test(getter, thisRef, thisPos, offset) } @@ -373,7 +372,7 @@ data class TileRenderMatchPiece( * * [subMatches] стоит итерировать только если это вернуло true */ - fun test(getter: ITileAccess, thisRef: TileDefinition, thisPos: Vector2i): Boolean { + fun test(getter: ITileGetter, thisRef: TileDefinition, thisPos: Vector2i): Boolean { for (matcher in matchAllPoints) { if (!matcher.test(getter, thisRef, thisPos)) { return false diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt index cd603729..7eb3d4de 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/render/ChunkRenderer.kt @@ -32,10 +32,10 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable layers.clear() // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) - for ((pos, tile) in chunk.posToTile) { + for ((pos, tile) in chunk.foreground.posToTile) { if (tile != null) { val renderer = state.tileRenderers.get(tile.def.materialName) - renderer.tesselate(chunk, layers, pos) + renderer.tesselate(chunk.foreground, layers, pos) } } } @@ -49,7 +49,7 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable unloadUnused() // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) - for ((pos, tile) in chunk.posToTile) { + for ((pos, tile) in chunk.foreground.posToTile) { if (tile != null) { state.tileRenderers.get(tile.def.materialName) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt index e3899210..e2e7073c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/render/TileRenderer.kt @@ -1,16 +1,14 @@ package ru.dbotthepony.kstarbound.render import org.apache.logging.log4j.LogManager -import org.lwjgl.glfw.GLFW.glfwGetTime import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.TileDefinition import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece import ru.dbotthepony.kstarbound.defs.TileRenderPiece import ru.dbotthepony.kstarbound.gl.* -import ru.dbotthepony.kstarbound.math.Matrix4f import ru.dbotthepony.kstarbound.math.Vector2i -import ru.dbotthepony.kstarbound.world.IChunk +import ru.dbotthepony.kstarbound.world.ITileChunk import kotlin.collections.HashMap data class TileLayer( @@ -113,7 +111,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { val bakedProgramState = state.tileRenderers.simpleProgram(texture) // private var notifiedDepth = false - private fun tesselateAt(piece: TileRenderPiece, getter: IChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { + private fun tesselateAt(piece: TileRenderPiece, getter: ITileChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO) { val fx = pos.x.toFloat() val fy = pos.y.toFloat() @@ -157,7 +155,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { } } - private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: IChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder): TileRenderTesselateResult { + private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder): TileRenderTesselateResult { if (matchPiece.test(getter, tile, pos)) { for (renderPiece in matchPiece.pieces) { if (renderPiece.piece.texture != null) { @@ -196,7 +194,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) { * * Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos] */ - fun tesselate(getter: IChunk, layers: TileLayerList, pos: Vector2i) { + fun tesselate(getter: ITileChunk, layers: TileLayerList, pos: Vector2i) { // если у нас нет renderTemplate // то мы просто не можем его отрисовать tile.render.renderTemplate ?: return diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index f3e66b5c..b0f63666 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -3,12 +3,27 @@ package ru.dbotthepony.kstarbound.world import ru.dbotthepony.kstarbound.defs.TileDefinition import ru.dbotthepony.kstarbound.math.Vector2i +/** + * Представляет из себя класс, который содержит состояние тайла на заданной позиции + */ data class ChunkTile(val def: TileDefinition) { var color = -1 var forceVariant = -1 } -interface ITileAccess { +interface ITileMap { + /** + * Относительная проверка находится ли координата вне границ чанка + */ + fun isOutside(x: Int, y: Int): Boolean { + return x !in 0 until CHUNK_SIZE || y !in 0 until CHUNK_SIZE + } +} + +/** + * Предоставляет интерфейс для доступа к тайлам в чанке + */ +interface ITileGetter : ITileMap { /** * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка */ @@ -18,35 +33,57 @@ interface ITileAccess { * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка */ operator fun get(pos: Vector2i) = get(pos.x, pos.y) + + /** + * Возвращает итератор пар + * + * Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка + */ + val posToTile: Iterator> get() { + return object : Iterator> { + private var x = 0 + private var y = 0 + + private fun idx() = x + CHUNK_SIZE * y + + override fun hasNext(): Boolean { + return idx() < CHUNK_SIZE * CHUNK_SIZE + } + + override fun next(): Pair { + if (!hasNext()) { + throw IllegalStateException("Already iterated everything!") + } + + val tile = this@ITileGetter[x, y] + val pos = Vector2i(x, y) + + x++ + + if (x >= CHUNK_SIZE) { + y++ + x = 0 + } + + return pos to tile + } + } + } } -interface IChunk : ITileAccess { +/** + * Интерфейс предоставляет из себя описание класса, который имеет координаты чанка + */ +interface IChunkPositionable : ITileMap { val pos: ChunkPos - /** - * Возвращает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка - */ - fun getBackground(x: Int, y: Int): ChunkTile? - - /** - * Возвращает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка - */ - fun getBackground(pos: Vector2i) = getBackground(pos.x, pos.y) - - /** - * Относительная проверка находится ли координата вне границ чагка - */ - fun isOutside(x: Int, y: Int): Boolean { - return x !in 0 until CHUNK_SIZE || y !in 0 until CHUNK_SIZE - } - /** * Возвращает псевдослучайное Long для заданной позиции * * Для использования в рендерах и прочих вещах, которым нужно стабильное число на основе своей позиции */ fun randomLongFor(x: Int, y: Int): Long { - var long = x * 738548L + y * 2191293543L + var long = (x or (pos.x shl CHUNK_SHIFT)) * 738548L + (y or (pos.y shl CHUNK_SHIFT)) * 2191293543L long = long xor 8339437585692L long = (long ushr 4) or (long shl 52) long *= 7848344324L @@ -76,45 +113,12 @@ interface IChunk : ITileAccess { * Для использования в рендерах и прочих вещах, которым нужно стабильное число на основе своей позиции */ fun randomDoubleFor(pos: Vector2i) = randomDoubleFor(pos.x, pos.y) - - /** - * Возвращает итератор пар - * - * Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка - */ - val posToTile: Iterator> get() { - return object : Iterator> { - private var x = 0 - private var y = 0 - - private fun idx() = x + CHUNK_SIZE * y - - override fun hasNext(): Boolean { - return idx() < CHUNK_SIZE * CHUNK_SIZE - } - - override fun next(): Pair { - if (!hasNext()) { - throw IllegalStateException("Already iterated everything!") - } - - val tile = this@IChunk[x, y] - val pos = Vector2i(x, y) - - x++ - - if (x >= CHUNK_SIZE) { - y++ - x = 0 - } - - return pos to tile - } - } - } } -interface IChunkSetter { +/** + * Предоставляет интерфейс по установке тайлов в чанке + */ +interface ITileSetter : ITileMap { /** * Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка */ @@ -123,19 +127,11 @@ interface IChunkSetter { * Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка */ operator fun set(pos: Vector2i, tile: TileDefinition?) = set(pos.x, pos.y, tile) - - /** - * Устанавливает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка - */ - fun setBackground(x: Int, y: Int, tile: TileDefinition?): ChunkTile? - - /** - * Устанавливает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка - */ - fun setBackground(pos: Vector2i, tile: TileDefinition?) = setBackground(pos.x, pos.y, tile) } -interface IMutableChunk : IChunk, IChunkSetter +interface ITileGetterSetter : ITileGetter, ITileSetter +interface ITileChunk : ITileGetter, IChunkPositionable +interface IMutableTileChunk : ITileChunk, ITileSetter const val CHUNK_SHIFT = 6 const val CHUNK_SIZE = 1 shl CHUNK_SHIFT // 64 @@ -148,75 +144,53 @@ data class ChunkPos(val x: Int, val y: Int) { val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SHIFT) - 1, ((y + 1) shl CHUNK_SHIFT) - 1) } -open class Chunk(val world: World, override val pos: ChunkPos) : IMutableChunk { - /** - * Хранит тайлы как x + y * CHUNK_SIZE - */ - private val tiles = arrayOfNulls(CHUNK_SIZE * CHUNK_SIZE) +open class Chunk(val world: World, val pos: ChunkPos) { + inner class ChunkTileLayer : IMutableTileChunk { + override val pos: ChunkPos + get() = this@Chunk.pos - /** - * Хранит фоновые тайлы как x + y * CHUNK_SIZE - */ - private val backgroundTiles = arrayOfNulls(CHUNK_SIZE * CHUNK_SIZE) + /** + * Хранит тайлы как x + y * CHUNK_SIZE + */ + private val tiles = arrayOfNulls(CHUNK_SIZE * CHUNK_SIZE) - override operator fun get(x: Int, y: Int): ChunkTile? { - if (isOutside(x, y)) - return null + override operator fun get(x: Int, y: Int): ChunkTile? { + if (isOutside(x, y)) + return null - return tiles[x or (y shl CHUNK_SHIFT)] + return tiles[x or (y shl CHUNK_SHIFT)] + } + + operator fun set(x: Int, y: Int, tile: ChunkTile?) { + if (isOutside(x, y)) + throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range") + + tiles[x or (y shl CHUNK_SHIFT)] = tile + } + + override operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { + if (isOutside(x, y)) + throw IndexOutOfBoundsException("Trying to set tile ${tile?.materialName} at $x $y, but that is outside of chunk's range") + + val chunkTile = if (tile != null) ChunkTile(tile) else null + this[x, y] = chunkTile + return chunkTile + } + + override fun randomLongFor(x: Int, y: Int): Long { + return super.randomLongFor(x, y) xor world.seed + } } - protected operator fun set(x: Int, y: Int, tile: ChunkTile?) { - if (isOutside(x, y)) - throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range") - - tiles[x or (y shl CHUNK_SHIFT)] = tile - } - - override operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { - if (isOutside(x, y)) - throw IndexOutOfBoundsException("Trying to set tile ${tile?.materialName} at $x $y, but that is outside of chunk's range") - - val chunkTile = if (tile != null) ChunkTile(tile) else null - this[x, y] = chunkTile - return chunkTile - } - - override fun getBackground(x: Int, y: Int): ChunkTile? { - if (isOutside(x, y)) - return null - - return backgroundTiles[x or (y shl CHUNK_SHIFT)] - } - - protected fun setBackground(x: Int, y: Int, tile: ChunkTile?) { - if (isOutside(x, y)) - throw IndexOutOfBoundsException("Trying to set background tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range") - - backgroundTiles[x or (y shl CHUNK_SHIFT)] = tile - } - - override fun setBackground(x: Int, y: Int, tile: TileDefinition?): ChunkTile? { - if (isOutside(x, y)) - throw IndexOutOfBoundsException("Trying to set background tile ${tile?.materialName} at $x $y, but that is outside of chunk's range") - - val chunkTile = if (tile != null) ChunkTile(tile) else null - setBackground(x, y, chunkTile) - return chunkTile - } - - override fun randomLongFor(x: Int, y: Int): Long { - return super.randomLongFor(x, y) xor world.seed - } + val foreground = ChunkTileLayer() + val background = ChunkTileLayer() companion object { - val EMPTY = object : IMutableChunk { + val EMPTY = object : IMutableTileChunk { override val pos = ChunkPos(0, 0) override fun get(x: Int, y: Int): ChunkTile? = null override fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile? = null - override fun getBackground(x: Int, y: Int): ChunkTile? = null - override fun setBackground(x: Int, y: Int, tile: TileDefinition?): ChunkTile? = null } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 2a2cfb78..16a838e3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -43,12 +43,22 @@ class World(val seed: Long = 0L) { fun getOrMakeChunk(pos: Vector2i) = getOrMakeChunk(ChunkPos(pos)) fun getTile(pos: Vector2i): ChunkTile? { - return getChunk(pos)?.get(pos.x, pos.y) + return getChunk(pos)?.foreground?.get(pos.x, pos.y) } fun setTile(pos: Vector2i, tile: TileDefinition?): Chunk { val chunk = getOrMakeChunk(pos) - chunk[pos.x and CHUNK_SIZE_FF, pos.y and CHUNK_SIZE_FF] = tile + chunk.foreground[pos.x and CHUNK_SIZE_FF, pos.y and CHUNK_SIZE_FF] = tile + return chunk + } + + fun getBackgroundTile(pos: Vector2i): ChunkTile? { + return getChunk(pos)?.background?.get(pos.x, pos.y) + } + + fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): Chunk { + val chunk = getOrMakeChunk(pos) + chunk.background[pos.x and CHUNK_SIZE_FF, pos.y and CHUNK_SIZE_FF] = tile return chunk } }