Улучшено дерево наследия чанков

This commit is contained in:
DBotThePony 2022-02-03 12:31:46 +07:00
parent 76e5357b32
commit 3962eec095
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 140 additions and 159 deletions

View File

@ -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

View File

@ -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<String, Any>) {
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<String, Any>): RenderRule {
@ -221,7 +220,7 @@ sealed class RenderRule(params: Map<String, Any>) {
}
class RenderRuleEqualsSelf(params: Map<String, Any>) : 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<String, Any>) : RenderRule(params) {
}
class RenderRuleShadows(params: Map<String, Any>) : 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<String, Any>) : 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<String, Any>) : RenderRule(params) {
}
class AlwaysPassingRenderRule(params: Map<String, Any>) : 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<String, Any>) : 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<RenderRule>
) {
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

View File

@ -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)
}

View File

@ -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

View File

@ -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)
/**
* Возвращает итератор пар <Vector2i, Тайл?>
*
* Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка
*/
val posToTile: Iterator<Pair<Vector2i, ChunkTile?>> get() {
return object : Iterator<Pair<Vector2i, ChunkTile?>> {
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<Vector2i, ChunkTile?> {
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)
/**
* Возвращает итератор пар <Vector2i, Тайл?>
*
* Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка
*/
val posToTile: Iterator<Pair<Vector2i, ChunkTile?>> get() {
return object : Iterator<Pair<Vector2i, ChunkTile?>> {
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<Vector2i, ChunkTile?> {
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<ChunkTile>(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<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE)
/**
* Хранит тайлы как x + y * CHUNK_SIZE
*/
private val tiles = arrayOfNulls<ChunkTile>(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
}
}
}

View File

@ -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
}
}