More streamlining

This commit is contained in:
DBotThePony 2022-09-11 18:50:19 +07:00
parent 96c88aa725
commit ee21636529
Signed by: DBot
GPG Key ID: DCC23B5715498507
13 changed files with 387 additions and 278 deletions

View File

@ -68,11 +68,11 @@ fun main() {
for (y in 0 .. 31) { for (y in 0 .. 31) {
for (x in 0 .. 31) { for (x in 0 .. 31) {
val materialID = reader.readShort() val materialID = reader.readUnsignedShort()
val getMat = Starbound.tilesAccessID[materialID.toInt()] val getMat = Starbound.tilesAccessID[materialID]
if (getMat != null) { if (getMat != null) {
chunk.foreground[x, y] = getMat chunk.foreground[x, y].material = getMat
hitTile = true hitTile = true
} }
@ -84,22 +84,22 @@ fun main() {
val modifier = reader.readUnsignedShort() val modifier = reader.readUnsignedShort()
val getModifier = Starbound.tileModifiersByIDAccess[modifier] val getModifier = Starbound.tileModifiersByIDAccess[modifier]
chunk.foreground[x, y]?.color = colorVariant chunk.foreground[x, y].color = colorVariant
chunk.foreground[x, y]?.setHueShift(colorShift) chunk.foreground[x, y].setHueShift(colorShift)
if (getModifier != null && getMat != null) { if (getModifier != null && getMat != null) {
chunk.foreground[x, y]?.modifier = getModifier chunk.foreground[x, y].modifier = getModifier
} }
val modifierHueShift = reader.readUnsignedByte() val modifierHueShift = reader.readUnsignedByte()
chunk.foreground[x, y]?.setModifierHueShift(modifierHueShift) chunk.foreground[x, y].setModifierHueShift(modifierHueShift)
val materialID2 = reader.readShort() val materialID2 = reader.readUnsignedShort()
val getMat2 = Starbound.tilesAccessID[materialID2.toInt()] val getMat2 = Starbound.tilesAccessID[materialID2]
if (getMat2 != null) { if (getMat2 != null) {
chunk.background[x, y] = getMat2 chunk.background[x, y].material = getMat2
hitTile = true hitTile = true
} }
@ -113,15 +113,15 @@ fun main() {
val getModifier2 = Starbound.tileModifiersByIDAccess[modifier2] val getModifier2 = Starbound.tileModifiersByIDAccess[modifier2]
if (getModifier2 != null && getMat2 != null) { if (getModifier2 != null && getMat2 != null) {
chunk.background[x, y]?.modifier = getModifier2 chunk.background[x, y].modifier = getModifier2
} }
chunk.background[x, y]?.color = colorVariant2 chunk.background[x, y].color = colorVariant2
chunk.background[x, y]?.setHueShift(colorShift2) chunk.background[x, y].setHueShift(colorShift2)
val modifierHueShift2 = reader.readUnsignedByte() val modifierHueShift2 = reader.readUnsignedByte()
chunk.background[x, y]?.setModifierHueShift(modifierHueShift2) chunk.background[x, y].setModifierHueShift(modifierHueShift2)
val liquid = reader.readUnsignedByte() val liquid = reader.readUnsignedByte()
val liquidLevel = reader.readFloat() val liquidLevel = reader.readFloat()

View File

@ -50,28 +50,31 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
layers.clear() layers.clear()
for ((pos, tile) in view.posToTile) { for ((pos, tile) in view.posToTile) {
if (tile != null) { val material = tile.material
state.tileRenderers.getTileRenderer(tile.def.materialName).tesselate(tile, view, layers, pos, background = isBackground)
val modifier = tile.modifier if (material != null) {
state.tileRenderers.getTileRenderer(material.materialName).tesselate(tile, view, layers, pos, background = isBackground)
}
if (modifier != null) { val modifier = tile.modifier
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
} if (modifier != null) {
state.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, pos, background = isBackground, isModifier = true)
} }
} }
} }
fun loadRenderers(view: ITileChunk) { fun loadRenderers(view: ITileChunk) {
for ((_, tile) in view.posToTile) { for ((_, tile) in view.posToTile) {
if (tile != null) { val material = tile.material
state.tileRenderers.getTileRenderer(tile.def.materialName) val modifier = tile.modifier
val modifier = tile.modifier if (material != null) {
state.tileRenderers.getTileRenderer(material.materialName)
}
if (modifier != null) { if (modifier != null) {
state.tileRenderers.getModifierRenderer(modifier.modName) state.tileRenderers.getModifierRenderer(modifier.modName)
}
} }
} }
} }

View File

@ -10,8 +10,8 @@ import ru.dbotthepony.kstarbound.client.gl.*
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.* import ru.dbotthepony.kstarbound.client.gl.vertex.*
import ru.dbotthepony.kstarbound.defs.tile.* import ru.dbotthepony.kstarbound.defs.tile.*
import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kstarbound.world.ITileChunk import ru.dbotthepony.kstarbound.world.ITileChunk
import ru.dbotthepony.kstarbound.world.ITileState
import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
import kotlin.collections.HashMap import kotlin.collections.HashMap
@ -171,14 +171,14 @@ private enum class TileRenderTesselateResult {
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, VertexType.QUADS) private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, VertexType.QUADS)
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
override fun test(thisTile: TileState?, otherTile: TileState?): Boolean { override fun test(thisTile: ITileState, otherTile: ITileState): Boolean {
return otherTile?.def == definition && thisTile?.hueShift == otherTile.hueShift return otherTile.material == definition && thisTile.hueShift == otherTile.hueShift
} }
} }
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester { private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
override fun test(thisTile: TileState?, otherTile: TileState?): Boolean { override fun test(thisTile: ITileState, otherTile: ITileState): Boolean {
return otherTile?.modifier == definition return otherTile.modifier == definition
} }
} }
@ -196,7 +196,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
val bakedBackgroundProgramState = state.tileRenderers.background(texture) val bakedBackgroundProgramState = state.tileRenderers.background(texture)
// private var notifiedDepth = false // private var notifiedDepth = false
private fun tesselateAt(self: TileState, piece: RenderPiece, getter: ITileChunk, builder: AbstractVertexBuilder<*>, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) { private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileChunk, builder: AbstractVertexBuilder<*>, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
val fx = pos.x.toFloat() val fx = pos.x.toFloat()
val fy = pos.y.toFloat() val fy = pos.y.toFloat()
@ -238,7 +238,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
} }
private fun tesselatePiece( private fun tesselatePiece(
self: TileState, self: ITileState,
matchPiece: RenderMatch, matchPiece: RenderMatch,
getter: ITileChunk, getter: ITileChunk,
layers: TileLayerList, layers: TileLayerList,
@ -289,7 +289,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
* *
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf] * Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
*/ */
fun tesselate(self: TileState, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) { fun tesselate(self: ITileState, getter: ITileChunk, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) {
// если у нас нет renderTemplate // если у нас нет renderTemplate
// то мы просто не можем его отрисовать // то мы просто не можем его отрисовать
val template = def.renderTemplate val template = def.renderTemplate

View File

@ -12,8 +12,8 @@ import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.io.EnumAdapter import ru.dbotthepony.kstarbound.io.EnumAdapter
import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter import ru.dbotthepony.kstarbound.io.KConcreteTypeAdapter
import ru.dbotthepony.kstarbound.util.WriteOnce import ru.dbotthepony.kstarbound.util.WriteOnce
import ru.dbotthepony.kstarbound.world.TileState
import ru.dbotthepony.kstarbound.world.ITileGetter import ru.dbotthepony.kstarbound.world.ITileGetter
import ru.dbotthepony.kstarbound.world.ITileState
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -38,7 +38,7 @@ data class RenderPiece(
} }
fun interface EqualityRuleTester { fun interface EqualityRuleTester {
fun test(thisTile: TileState?, otherTile: TileState?): Boolean fun test(thisTile: ITileState, otherTile: ITileState): Boolean
} }
data class RenderRuleList( data class RenderRuleList(
@ -57,7 +57,7 @@ data class RenderRuleList(
private fun doTest(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i, offsetPos: Vector2i): Boolean { private fun doTest(getter: ITileGetter, equalityTester: EqualityRuleTester, thisPos: Vector2i, offsetPos: Vector2i): Boolean {
return when (type) { return when (type) {
"EqualsSelf" -> equalityTester.test(getter[thisPos], getter[thisPos + offsetPos]) "EqualsSelf" -> equalityTester.test(getter[thisPos], getter[thisPos + offsetPos])
"Connects" -> getter[thisPos + offsetPos] != null "Connects" -> getter[thisPos + offsetPos].material != null
else -> { else -> {
if (LOGGED.add(type)) { if (LOGGED.add(type)) {

View File

@ -0,0 +1,67 @@
package ru.dbotthepony.kstarbound.util
import java.util.stream.Stream
import java.util.stream.StreamSupport
import kotlin.reflect.KClass
class NotNullTwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int, initializer: (Int, Int) -> T) {
data class Entry<out T>(
val x: Int,
val y: Int,
val value: T,
)
private val memory: Array<T> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T>
init {
for (x in 0 until width) {
for (y in 0 until height) {
memory[x + y * width] = initializer.invoke(x, y)
}
}
}
fun isOutside(x: Int, y: Int): Boolean {
return (x !in 0 until width) || (y !in 0 until height)
}
operator fun get(x: Int, y: Int): T {
if (x !in 0 until width) {
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
}
if (y !in 0 until height) {
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
}
return memory[x + y * width]
}
operator fun set(x: Int, y: Int, value: T): T {
if (x !in 0 until width) {
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
}
if (y !in 0 until height) {
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
}
val old = memory[x + y * width]
memory[x + y * width] = value
return old
}
fun stream(): Stream<out T> {
return StreamSupport.stream(ArraySpliterator(memory), false)
}
fun indexedStream(): Stream<out Entry<T>> {
return StreamSupport.stream(IndexedArraySpliterator(memory), false).map {
val x = it.index % width
val y = (it.index - x) / width
Entry(x, y, it.value)
}
}
}
inline fun <reified T : Any> NotNullTwoDimensionalArray(width: Int, height: Int, noinline initializer: (Int, Int) -> T) = NotNullTwoDimensionalArray(T::class, width, height, initializer)

View File

@ -13,6 +13,10 @@ class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, pri
private val memory: Array<T?> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T?> private val memory: Array<T?> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T?>
fun isOutside(x: Int, y: Int): Boolean {
return (x !in 0 until width) || (y !in 0 until height)
}
operator fun get(x: Int, y: Int): T? { operator fun get(x: Int, y: Int): T? {
if (x !in 0 until width) { if (x !in 0 until width) {
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width") throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")

View File

@ -8,126 +8,18 @@ import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TILE_COLOR_VARIANTS import ru.dbotthepony.kstarbound.defs.tile.TILE_COLOR_VARIANTS
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.util.NotNullTwoDimensionalArray
import ru.dbotthepony.kstarbound.util.TwoDimensionalArray import ru.dbotthepony.kstarbound.util.TwoDimensionalArray
import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderDepthFirst
import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderSizeFirst import ru.dbotthepony.kstarbound.world.phys.RectTileFlooderSizeFirst
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashSet import kotlin.collections.HashSet
/**
* Представляет из себя класс, который содержит состояние тайла на заданной позиции
*/
class TileState(val chunk: Chunk<*, *>.TileLayer, val def: TileDefinition) {
var color = 0
set(value) {
if (value != field) {
if (!def.renderParameters.multiColored) {
throw IllegalStateException("${def.materialName} can't be colored")
}
if (value !in 0 until TILE_COLOR_VARIANTS) {
throw IndexOutOfBoundsException("Tile variant $value is out of possible range 0 to $TILE_COLOR_VARIANTS")
}
field = value
chunk.incChangeset()
}
}
/**
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
*/
fun setHueShift(value: Int) {
if (value < 0) {
hueShift = 0f
} else if (value > 255) {
hueShift = 360f
} else {
hueShift = (value / 255f) * 360f
}
}
/**
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
*/
fun setModifierHueShift(value: Int) {
if (value < 0) {
modifierHueShift = 0f
} else if (value > 255) {
modifierHueShift = 360f
} else {
modifierHueShift = (value / 255f) * 360f
}
}
var hueShift = 0f
set(value) {
var newValue = value % 360f
if (newValue < 0f) {
newValue += 360f
}
if (newValue != field) {
field = newValue
chunk.incChangeset()
}
}
var modifierHueShift = 0f
set(value) {
var newValue = value % 360f
if (newValue < 0f) {
newValue += 360f
}
if (newValue != field) {
field = newValue
chunk.incChangeset()
}
}
var modifier: MaterialModifier? = null
set(value) {
if (value != field) {
field = value
chunk.incChangeset()
}
}
override fun equals(other: Any?): Boolean {
return other is TileState &&
other.color == color &&
other.modifier === modifier &&
other.modifierHueShift == modifierHueShift &&
other.hueShift == hueShift &&
other.def === def
}
override fun toString(): String {
return "ChunkTile[$chunk, material = ${def.materialName}, color = $color, modifier = ${modifier?.modName}]"
}
override fun hashCode(): Int {
var result = chunk.hashCode()
result = 31 * result + def.hashCode()
result = 31 * result + color
result = 31 * result + (modifier?.hashCode() ?: 0)
return result
}
}
data class LiquidState(val chunk: Chunk<*, *>, val def: LiquidDefinition) {
var pressure: Float = 0f
var level: Float = 1f
var isInfinite: Boolean = false
}
private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double { private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double {
if (point.x > 0.0) { if (point.x > 0.0) {
return point.dot(axis) return point.dot(axis)
@ -148,14 +40,22 @@ private fun ccwSortScore(point: Vector2d, axis: Vector2d): Double {
*/ */
abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType, This>>(val world: WorldType, val pos: ChunkPos) { abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType, This>>(val world: WorldType, val pos: ChunkPos) {
/** /**
* Возвращает счётчик изменений чанка * Возвращает общий счётчик изменений чанка
*/ */
var changeset = 0 var changeset = 0
private set private set
fun incChangeset() { /**
changeset++ * Возвращает счётчик изменений чанка по тайлам
} */
var tileChangeset = 0
private set
/**
* Возвращает счётчик изменений чанка по жидкостям
*/
var liquidChangeset = 0
private set
val left get() = pos.left val left get() = pos.left
val right get() = pos.right val right get() = pos.right
@ -182,18 +82,153 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
foreground.bakeCollisions() foreground.bakeCollisions()
} }
inner class LiquidState(val def: LiquidDefinition) {
var pressure: Float = 0f
set(value) {
if (value != field) {
field = value
liquidChangeset++
changeset++
}
}
var level: Float = 1f
set(value) {
if (value != field) {
field = value
liquidChangeset++
changeset++
}
}
var isInfinite: Boolean = false
set(value) {
if (value != field) {
field = value
liquidChangeset++
changeset++
}
}
}
inner class TileLayer : IMutableTileChunk { inner class TileLayer : IMutableTileChunk {
inner class TileState(def: TileDefinition? = null) : IMutableTileState {
override var material: TileDefinition? = def
set(value) {
if (value !== field) {
field = value
color = 0
hueShift = 0f
collisionChangeset++
this@TileLayer.changeset++
this@Chunk.changeset++
this@Chunk.tileChangeset++
markPhysicsDirty()
}
}
override var color = 0
set(value) {
val material = material
if (material == null) {
field = 0
return
}
if (value != field) {
if (!material.renderParameters.multiColored) {
throw IllegalStateException("${material.materialName} can't be colored")
}
if (value !in 0 until TILE_COLOR_VARIANTS) {
throw IndexOutOfBoundsException("Tile variant $value is out of possible range 0 to $TILE_COLOR_VARIANTS")
}
field = value
this@TileLayer.changeset++
this@Chunk.changeset++
this@Chunk.tileChangeset++
}
}
override var hueShift = 0f
set(value) {
var newValue = value % 360f
if (newValue < 0f) {
newValue += 360f
}
if (newValue != field) {
field = newValue
this@TileLayer.changeset++
this@Chunk.changeset++
this@Chunk.tileChangeset++
}
}
override var modifierHueShift = 0f
set(value) {
var newValue = value % 360f
if (newValue < 0f) {
newValue += 360f
}
if (newValue != field) {
field = newValue
this@TileLayer.changeset++
this@Chunk.changeset++
this@Chunk.tileChangeset++
}
}
override var modifier: MaterialModifier? = null
set(value) {
if (value != field) {
field = value
modifierHueShift = 0f
this@TileLayer.changeset++
this@Chunk.changeset++
this@Chunk.tileChangeset++
}
}
override fun equals(other: Any?): Boolean {
return other is Chunk<*, *>.TileLayer.TileState &&
other.color == color &&
other.modifier === modifier &&
other.modifierHueShift == modifierHueShift &&
other.hueShift == hueShift &&
other.material === material
}
override fun toString(): String {
return "TileState[${this@TileLayer}, material = ${material?.materialName}, color = $color, modifier = ${modifier?.modName}]"
}
override fun hashCode(): Int {
var result = this@TileLayer.hashCode()
result = 31 * result + material.hashCode()
result = 31 * result + color
result = 31 * result + (modifier?.hashCode() ?: 0)
return result
}
}
/** /**
* Возвращает счётчик изменений этого слоя * Возвращает счётчик изменений этого слоя
*/ */
var changeset = 0 var changeset = 0
private set private set
fun incChangeset() {
changeset++
this@Chunk.changeset++
}
private val collisionCache = ArrayList<AABB>() private val collisionCache = ArrayList<AABB>()
private val collisionCacheView = Collections.unmodifiableCollection(collisionCache) private val collisionCacheView = Collections.unmodifiableCollection(collisionCache)
private var collisionChangeset = -1 private var collisionChangeset = -1
@ -223,7 +258,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
for (y in 0 .. CHUNK_SIZE_FF) { for (y in 0 .. CHUNK_SIZE_FF) {
for (x in 0..CHUNK_SIZE_FF) { for (x in 0..CHUNK_SIZE_FF) {
if (!seen[x or (y shl CHUNK_SHIFT)] && tiles[x or (y shl CHUNK_SHIFT)] != null) { if (!seen[x or (y shl CHUNK_SHIFT)] && tiles[x, y].material != null) {
val depthFirst = RectTileFlooderDepthFirst(tiles, seen, x, y) val depthFirst = RectTileFlooderDepthFirst(tiles, seen, x, y)
val sizeFirst = RectTileFlooderSizeFirst(tiles, seen, x, y) val sizeFirst = RectTileFlooderSizeFirst(tiles, seen, x, y)
val xSpanDepth = depthFirst.maxs.x - depthFirst.mins.x val xSpanDepth = depthFirst.maxs.x - depthFirst.mins.x
@ -375,33 +410,10 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
/** /**
* Хранит тайлы как x + y * CHUNK_SIZE * Хранит тайлы как x + y * CHUNK_SIZE
*/ */
private val tiles = arrayOfNulls<TileState>(CHUNK_SIZE * CHUNK_SIZE) private val tiles: NotNullTwoDimensionalArray<TileState> = NotNullTwoDimensionalArray(CHUNK_SIZE, CHUNK_SIZE) { _, _ -> TileState() }
override operator fun get(x: Int, y: Int): TileState? { override operator fun get(x: Int, y: Int): TileState {
if (isOutside(x, y)) return tiles[x, y]
return null
return tiles[x or (y shl CHUNK_SHIFT)]
}
operator fun set(x: Int, y: Int, tile: TileState?) {
if (isOutside(x, y))
throw IndexOutOfBoundsException("Trying to set tile ${tile?.def?.materialName} at $x $y, but that is outside of chunk's range")
changeset++
tiles[x or (y shl CHUNK_SHIFT)] = tile
markPhysicsDirty()
}
override operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState? {
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) TileState(this, tile) else null
this[x, y] = chunkTile
changeset++
markPhysicsDirty()
return chunkTile
} }
override fun randomLongFor(x: Int, y: Int): Long { override fun randomLongFor(x: Int, y: Int): Long {
@ -418,11 +430,20 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
fun setLiquid(x: Int, y: Int, value: LiquidDefinition?): LiquidState? { fun setLiquid(x: Int, y: Int, value: LiquidDefinition?): LiquidState? {
if (value == null) { if (value == null) {
return liquidStates.set(x, y, null) val old = liquidStates.set(x, y, null)
if (old != null) {
changeset++
liquidChangeset++
}
return old
} }
val state = LiquidState(this, value) val state = LiquidState(value)
liquidStates[x, y] = state liquidStates[x, y] = state
changeset++
liquidChangeset++
return state return state
} }
@ -475,13 +496,6 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
} }
companion object { companion object {
val EMPTY = object : IMutableTileChunk {
override val pos = ChunkPos(0, 0)
override fun get(x: Int, y: Int): TileState? = null
override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? = null
}
private val aabbBase = AABB( private val aabbBase = AABB(
Vector2d.ZERO, Vector2d.ZERO,
Vector2d(CHUNK_SIZE.toDouble(), CHUNK_SIZE.toDouble()), Vector2d(CHUNK_SIZE.toDouble(), CHUNK_SIZE.toDouble()),

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.kstarbound.world package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
@ -10,6 +11,48 @@ const val CHUNK_SIZE_FF = CHUNK_SIZE - 1
const val CHUNK_SIZEf = CHUNK_SIZE.toFloat() const val CHUNK_SIZEf = CHUNK_SIZE.toFloat()
const val CHUNK_SIZEd = CHUNK_SIZE.toDouble() const val CHUNK_SIZEd = CHUNK_SIZE.toDouble()
interface ITileState {
val material: TileDefinition?
val modifier: MaterialModifier?
val color: Int
val hueShift: Float
val modifierHueShift: Float
}
interface IMutableTileState : ITileState {
override var material: TileDefinition?
override var modifier: MaterialModifier?
override var color: Int
override var hueShift: Float
override var modifierHueShift: Float
/**
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
*/
fun setHueShift(value: Int) {
if (value < 0) {
hueShift = 0f
} else if (value > 255) {
hueShift = 360f
} else {
hueShift = (value / 255f) * 360f
}
}
/**
* Выставляет hue shift как байтовое значение в диапазоне 0 .. 255
*/
fun setModifierHueShift(value: Int) {
if (value < 0) {
modifierHueShift = 0f
} else if (value > 255) {
modifierHueShift = 360f
} else {
modifierHueShift = (value / 255f) * 360f
}
}
}
interface ITileMap { interface ITileMap {
/** /**
* Относительная проверка находится ли координата вне границ чанка * Относительная проверка находится ли координата вне границ чанка
@ -26,7 +69,7 @@ interface ITileGetter : ITileMap {
/** /**
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/ */
operator fun get(x: Int, y: Int): TileState? operator fun get(x: Int, y: Int): ITileState
/** /**
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка * Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
@ -38,8 +81,8 @@ interface ITileGetter : ITileMap {
* *
* Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка * Вектор имеет ОТНОСИТЕЛЬНЫЕ значения внутри самого чанка
*/ */
val posToTile: Iterator<Pair<Vector2i, TileState?>> get() { val posToTile: Iterator<Pair<Vector2i, ITileState>> get() {
return object : Iterator<Pair<Vector2i, TileState?>> { return object : Iterator<Pair<Vector2i, ITileState>> {
private var x = 0 private var x = 0
private var y = 0 private var y = 0
@ -49,7 +92,7 @@ interface ITileGetter : ITileMap {
return idx() < CHUNK_SIZE * CHUNK_SIZE return idx() < CHUNK_SIZE * CHUNK_SIZE
} }
override fun next(): Pair<Vector2i, TileState?> { override fun next(): Pair<Vector2i, ITileState> {
if (!hasNext()) { if (!hasNext()) {
throw IllegalStateException("Already iterated everything!") throw IllegalStateException("Already iterated everything!")
} }
@ -114,20 +157,38 @@ interface IChunkPositionable : ITileMap {
fun randomDoubleFor(pos: Vector2i) = randomDoubleFor(pos.x, pos.y) fun randomDoubleFor(pos: Vector2i) = randomDoubleFor(pos.x, pos.y)
} }
/** interface ITileChunk : ITileGetter, IChunkPositionable
* Предоставляет интерфейс по установке тайлов в чанке
*/ interface IMutableTileChunk : ITileChunk {
interface ITileSetter : ITileMap { override fun get(x: Int, y: Int): IMutableTileState
/** override fun get(pos: Vector2i): IMutableTileState {
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка return get(pos.x, pos.y)
*/ }
operator fun set(x: Int, y: Int, tile: TileDefinition?): TileState?
/**
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
operator fun set(pos: Vector2i, tile: TileDefinition?) = set(pos.x, pos.y, tile)
} }
interface ITileGetterSetter : ITileGetter, ITileSetter object EmptyTileState : IMutableTileState {
interface ITileChunk : ITileGetter, IChunkPositionable override var material: TileDefinition?
interface IMutableTileChunk : ITileChunk, ITileSetter get() = null
set(value) {}
override var modifier: MaterialModifier?
get() = null
set(value) {}
override var color: Int
get() = 0
set(value) {}
override var hueShift: Float
get() = 0f
set(value) {}
override var modifierHueShift: Float
get() = 0f
set(value) {}
}
object EmptyTileChunk : IMutableTileChunk {
override val pos: ChunkPos
get() = ChunkPos.ZERO
override fun get(x: Int, y: Int): IMutableTileState {
return EmptyTileState
}
}

View File

@ -22,38 +22,38 @@ open class TileView(
open val bottomLeft: ITileChunk?, open val bottomLeft: ITileChunk?,
open val bottomRight: ITileChunk?, open val bottomRight: ITileChunk?,
) : ITileChunk { ) : ITileChunk {
override fun get(x: Int, y: Int): TileState? { override fun get(x: Int, y: Int): ITileState {
if (x in 0 ..CHUNK_SIZE_FF) { if (x in 0 ..CHUNK_SIZE_FF) {
if (y in 0 ..CHUNK_SIZE_FF) { if (y in 0 ..CHUNK_SIZE_FF) {
return center[x, y] return center[x, y]
} }
if (y < 0) { if (y < 0) {
return bottom?.get(x, y + CHUNK_SIZE) return bottom?.get(x, y + CHUNK_SIZE) ?: EmptyTileState
} else { } else {
return top?.get(x, y - CHUNK_SIZE) return top?.get(x, y - CHUNK_SIZE) ?: EmptyTileState
} }
} }
if (x < 0) { if (x < 0) {
if (y in 0 ..CHUNK_SIZE_FF) { if (y in 0 ..CHUNK_SIZE_FF) {
return left?.get(x + CHUNK_SIZE, y) return left?.get(x + CHUNK_SIZE, y) ?: EmptyTileState
} }
if (y < 0) { if (y < 0) {
return bottomLeft?.get(x + CHUNK_SIZE, y + CHUNK_SIZE) return bottomLeft?.get(x + CHUNK_SIZE, y + CHUNK_SIZE) ?: EmptyTileState
} else { } else {
return topLeft?.get(x + CHUNK_SIZE, y - CHUNK_SIZE) return topLeft?.get(x + CHUNK_SIZE, y - CHUNK_SIZE) ?: EmptyTileState
} }
} else { } else {
if (y in 0 ..CHUNK_SIZE_FF) { if (y in 0 ..CHUNK_SIZE_FF) {
return right?.get(x - CHUNK_SIZE, y) return right?.get(x - CHUNK_SIZE, y) ?: EmptyTileState
} }
if (y < 0) { if (y < 0) {
return bottomRight?.get(x - CHUNK_SIZE, y + CHUNK_SIZE) return bottomRight?.get(x - CHUNK_SIZE, y + CHUNK_SIZE) ?: EmptyTileState
} else { } else {
return topRight?.get(x - CHUNK_SIZE, y - CHUNK_SIZE) return topRight?.get(x - CHUNK_SIZE, y - CHUNK_SIZE) ?: EmptyTileState
} }
} }
} }
@ -75,39 +75,7 @@ class MutableTileView(
override val bottomLeft: IMutableTileChunk?, override val bottomLeft: IMutableTileChunk?,
override val bottomRight: IMutableTileChunk?, override val bottomRight: IMutableTileChunk?,
) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk { ) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
override fun set(x: Int, y: Int, tile: TileDefinition?): TileState? { override fun get(x: Int, y: Int): IMutableTileState {
if (x in 0 .. CHUNK_SIZE_FF) { return super<TileView>.get(x, y) as IMutableTileState
if (y in 0 .. CHUNK_SIZE_FF) {
return center.set(x, y, tile)
}
if (y < 0) {
return bottom?.set(x, y + CHUNK_SIZE, tile)
} else {
return top?.set(x, y - CHUNK_SIZE, tile)
}
}
if (x < 0) {
if (y in 0 .. CHUNK_SIZE_FF) {
return left?.set(x + CHUNK_SIZE, y, tile)
}
if (y < 0) {
return bottomLeft?.set(x + CHUNK_SIZE, y + CHUNK_SIZE, tile)
} else {
return topLeft?.set(x + CHUNK_SIZE, y - CHUNK_SIZE, tile)
}
} else {
if (y in 0 .. CHUNK_SIZE_FF) {
return right?.set(x - CHUNK_SIZE, y, tile)
}
if (y < 0) {
return bottomRight?.set(x - CHUNK_SIZE, y + CHUNK_SIZE, tile)
} else {
return topRight?.set(x - CHUNK_SIZE, y - CHUNK_SIZE, tile)
}
}
} }
} }

View File

@ -323,26 +323,14 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
) )
} }
fun getTile(pos: Vector2i): TileState? { fun getTile(pos: Vector2i): ITileState? {
return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
} }
fun setTile(pos: Vector2i, tile: TileDefinition?): ChunkType { fun getBackgroundTile(pos: Vector2i): ITileState? {
val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos))
chunk.foreground[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
return chunk
}
fun getBackgroundTile(pos: Vector2i): TileState? {
return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)) return get(ChunkPos.fromTilePosition(pos))?.background?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
} }
fun setBackgroundTile(pos: Vector2i, tile: TileDefinition?): ChunkType {
val chunk = computeIfAbsent(ChunkPos.fromTilePosition(pos))
chunk.background[ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y)] = tile
return chunk
}
/** /**
* Возвращает все чанки, которые пересекаются с заданным [boundingBox] * Возвращает все чанки, которые пересекаются с заданным [boundingBox]
*/ */

View File

@ -1,12 +1,14 @@
package ru.dbotthepony.kstarbound.world.phys package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.util.NotNullTwoDimensionalArray
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.TileState import ru.dbotthepony.kstarbound.world.Chunk
import ru.dbotthepony.kstarbound.world.ITileState
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
class RectTileFlooderDepthFirst( class RectTileFlooderDepthFirst(
private val tiles: Array<TileState?>, private val tiles: NotNullTwoDimensionalArray<out ITileState>,
private val seen: BooleanArray, private val seen: BooleanArray,
rootx: Int, rootx: Int,
rooty: Int rooty: Int
@ -23,7 +25,7 @@ class RectTileFlooderDepthFirst(
return false return false
} }
return !seen[x or (y shl CHUNK_SHIFT)] && tiles[x or (y shl CHUNK_SHIFT)] != null return !seen[x or (y shl CHUNK_SHIFT)] && tiles[x, y].material != null
} }
init { init {

View File

@ -1,12 +1,13 @@
package ru.dbotthepony.kstarbound.world.phys package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.util.NotNullTwoDimensionalArray
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.TileState import ru.dbotthepony.kstarbound.world.ITileState
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
class RectTileFlooderSizeFirst( class RectTileFlooderSizeFirst(
private val tiles: Array<TileState?>, private val tiles: NotNullTwoDimensionalArray<out ITileState>,
private val seen: BooleanArray, private val seen: BooleanArray,
private val rootx: Int, private val rootx: Int,
private val rooty: Int private val rooty: Int
@ -23,7 +24,7 @@ class RectTileFlooderSizeFirst(
return false return false
} }
return !seen[x or (y shl CHUNK_SHIFT)] && tiles[x or (y shl CHUNK_SHIFT)] != null return !seen[x or (y shl CHUNK_SHIFT)] && tiles[x, y].material != null
} }
private var widthPositive = 0 private var widthPositive = 0

View File

@ -1,8 +1,9 @@
package ru.dbotthepony.kstarbound.world.phys package ru.dbotthepony.kstarbound.world.phys
import ru.dbotthepony.kstarbound.util.NotNullTwoDimensionalArray
import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT import ru.dbotthepony.kstarbound.world.CHUNK_SHIFT
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.TileState import ru.dbotthepony.kstarbound.world.ITileState
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
private data class TileExposure( private data class TileExposure(
@ -14,7 +15,7 @@ private data class TileExposure(
) )
private class TileFlooder( private class TileFlooder(
private val tiles: Array<TileState?>, private val tiles: NotNullTwoDimensionalArray<out ITileState>,
private val seen: BooleanArray, private val seen: BooleanArray,
rootx: Int, rootx: Int,
rooty: Int rooty: Int
@ -33,7 +34,7 @@ private class TileFlooder(
return false return false
} }
return tiles[x or (y shl CHUNK_SHIFT)] != null return tiles[x, y].material != null
} }
private fun visit(x: Int, y: Int) { private fun visit(x: Int, y: Int) {
@ -71,4 +72,4 @@ private class TileFlooder(
init { init {
visit(rootx, rooty) visit(rootx, rooty)
} }
} }