Light flood test

This commit is contained in:
DBotThePony 2022-09-16 15:29:39 +07:00
parent 45b3e203ba
commit 96068d483c
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 239 additions and 47 deletions
src/main/kotlin/ru/dbotthepony/kstarbound

View File

@ -42,8 +42,10 @@ fun main() {
var set = 0L
var parse = 0L
for (chunkX in 17 .. 18) {
for (chunkY in 21 .. 21) {
//for (chunkX in 17 .. 18) {
for (chunkX in 0 .. 60) {
// for (chunkY in 21 .. 21) {
for (chunkY in 0 .. 60) {
var t = System.currentTimeMillis()
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
find += System.currentTimeMillis() - t

View File

@ -4,6 +4,7 @@ import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
@ -15,6 +16,8 @@ import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nint.Vector2i
import kotlin.math.roundToInt
class ClientWorld(
val client: StarboundClient,
@ -127,7 +130,7 @@ class ClientWorld(
client.pushScissorRect(x, client.viewportHeight - y, x2 - x, y - y2)
}
client.lightRenderer.renderSoftLight(lightPosition, color, radius = 20f, innerRadius = 1f)
//client.lightRenderer.renderSoftLight(lightPosition, color, radius = 20f, innerRadius = 1f)
if (isScreenspaceRender) {
client.popScissorRect()
@ -137,12 +140,30 @@ class ClientWorld(
val old = client.gl.blendFunc
client.gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC
client.gl.activeTexture = 0
client.gl.texture2D = client.lightRenderer.outputTexture
client.gl.programs.textureQuad.run()
// client.gl.activeTexture = 0
// client.gl.texture2D = client.lightRenderer.outputTexture
// client.gl.programs.textureQuad.run()
client.gl.blendFunc = old
val pos = client.screenToWorld(client.mouseCoordinatesF)
val lightsize = 16
val lightmap = floodLight(
Vector2i(pos.x.roundToInt(), pos.y.roundToInt()), lightsize
)
client.gl.quadWireframe {
for (column in 0 until lightmap.columns) {
for (row in 0 until lightmap.rows) {
if (lightmap[column, row] > 0) {
it.quad(pos.x.roundToInt() + column.toFloat() - lightsize, pos.y.roundToInt() + row.toFloat() - lightsize, pos.x.roundToInt() + column + 1f - lightsize, pos.y.roundToInt() + row + 1f - lightsize)
}
}
}
}
physics.debugDraw()
/*for (renderer in determineRenderers) {

View File

@ -29,9 +29,19 @@ private fun circulate(value: Int, bounds: Int): Int {
class ChunkPos(val x: Int, val y: Int) : Comparable<ChunkPos> {
constructor(pos: IStruct2i) : this(pos.component1(), pos.component2())
val firstBlock get() = Vector2i(x shl CHUNK_SHIFT, y shl CHUNK_SHIFT)
val firstBlock get() = Vector2i(tileX, tileY)
val lastBlock get() = Vector2i(((x + 1) shl CHUNK_SHIFT) - 1, ((y + 1) shl CHUNK_SHIFT) - 1)
/**
* Координата тайла на 0 позиции по оси X внутри чанка в мире
*/
val tileX: Int get() = x shl CHUNK_SHIFT
/**
* Координата тайла на 0 позиции по оси Y внутри чанка в мире
*/
val tileY: Int get() = y shl CHUNK_SHIFT
val top: ChunkPos
get() {
return ChunkPos(x, y + 1)

View File

@ -0,0 +1,43 @@
package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.util.NotNullTwoDimensionalArray
import ru.dbotthepony.kstarbound.util.TwoDimensionalArray
/**
* Предоставляет доступ к чанку и его соседям
*
* Данный вариант отличается от [TileView] тем, что данный класс *копирует* список тайлов из всех чанков, которые ему известны
* в единый массив.
*
* Полезно в ситуациях, когда необходимо максимально быстрое получение данных о тайлах.
*/
class RigidTileView(
view: TileView,
) : ITileChunk {
constructor(
pos: ChunkPos,
center: ITileChunk?,
right: ITileChunk?,
top: ITileChunk?,
topRight: ITileChunk?,
topLeft: ITileChunk?,
left: ITileChunk?,
bottom: ITileChunk?,
bottomLeft: ITileChunk?,
bottomRight: ITileChunk?,
) : this(TileView(pos, center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight))
override val pos: ChunkPos = view.pos
private val memory: NotNullTwoDimensionalArray<ITileState>
init {
memory = NotNullTwoDimensionalArray(CHUNK_SIZE * 3, CHUNK_SIZE * 3) { a, b -> view[a - CHUNK_SIZE, b - CHUNK_SIZE] }
}
override fun get(x: Int, y: Int): ITileState {
return memory[x + CHUNK_SIZE, y + CHUNK_SIZE]
}
}

View File

@ -10,7 +10,8 @@ import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
* с желанием получить тайл из соседнего чанка
*/
open class TileView(
open val center: ITileChunk,
override val pos: ChunkPos,
open val center: ITileChunk?,
open val right: ITileChunk?,
open val top: ITileChunk?,
@ -25,7 +26,7 @@ open class TileView(
override fun get(x: Int, y: Int): ITileState {
if (x in 0 ..CHUNK_SIZE_FF) {
if (y in 0 ..CHUNK_SIZE_FF) {
return center[x, y]
return center?.get(x, y) ?: EmptyTileState
}
if (y < 0) {
@ -57,13 +58,11 @@ open class TileView(
}
}
}
override val pos: ChunkPos
get() = center.pos
}
class MutableTileView(
override val center: IMutableTileChunk,
pos: ChunkPos,
override val center: IMutableTileChunk?,
override val right: IMutableTileChunk?,
override val top: IMutableTileChunk?,
@ -74,7 +73,7 @@ class MutableTileView(
override val bottom: IMutableTileChunk?,
override val bottomLeft: IMutableTileChunk?,
override val bottomRight: IMutableTileChunk?,
) : TileView(center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
) : TileView(pos, center, right, top, topRight, topLeft, left, bottom, bottomLeft, bottomRight), IMutableTileChunk {
override fun get(x: Int, y: Int): IMutableTileState {
return super<TileView>.get(x, y) as IMutableTileState
}

View File

@ -16,6 +16,7 @@ import ru.dbotthepony.kstarbound.util.Timer
import ru.dbotthepony.kstarbound.world.entities.CollisionResolution
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
import ru.dbotthepony.kvector.narray.Int2Dimensional
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.util2d.AABBi
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@ -41,6 +42,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return@cmp a.compareTo(b)
}
//protected val chunkMap = HashMap<ChunkPos, ChunkType>()
/**
* Является ли мир "сферическим"
*
@ -56,6 +59,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val dirtyPhysicsChunks = HashSet<ChunkType>()
protected var lastAccessedChunk: ChunkType? = null
protected var lastAccessedChunkPos: ChunkPos? = null
val physics = B2World(Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION))
@ -226,21 +230,19 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
*/
open operator fun get(pos: ChunkPos): ChunkType? {
if (!isCircular) {
val lastAccessedChunk = lastAccessedChunk
//if (lastAccessedChunkPos == pos) {
// return lastAccessedChunk
//}
if (lastAccessedChunk?.pos == pos) {
return lastAccessedChunk
}
return chunkMap[pos]
//lastAccessedChunkPos = pos
lastAccessedChunk = chunkMap[pos]
return this.lastAccessedChunk
}
@Suppress("Name_Shadowing")
val pos = pos.circular(widthInChunks)
val lastAccessedChunk = lastAccessedChunk
if (lastAccessedChunk?.pos == pos) {
if (lastAccessedChunkPos == pos) {
return lastAccessedChunk
}
@ -270,6 +272,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val chunk = chunkFactory(pos)
lastAccessedChunk = chunk
lastAccessedChunkPos = pos
val orphanedInThisChunk = ArrayList<Entity>()
@ -289,40 +292,48 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
})
}
open fun getForegroundView(pos: ChunkPos): TileView? {
val get = get(pos) ?: return null
val tuple = InstantWorldChunkTuple(this as This, get)
open fun getForegroundView(pos: ChunkPos): TileView {
val tuple = get(pos)?.let { InstantWorldChunkTuple(this as This, it) }
return TileView(
center = tuple.chunk.foreground,
left = tuple.left?.chunk?.foreground,
top = tuple.top?.chunk?.foreground,
topLeft = tuple.topLeft?.chunk?.foreground,
topRight = tuple.topRight?.chunk?.foreground,
right = tuple.right?.chunk?.foreground,
bottom = tuple.bottom?.chunk?.foreground,
bottomLeft = tuple.bottomLeft?.chunk?.foreground,
bottomRight = tuple.bottomRight?.chunk?.foreground,
pos = pos,
center = tuple?.chunk?.foreground,
left = tuple?.left?.chunk?.foreground,
top = tuple?.top?.chunk?.foreground,
topLeft = tuple?.topLeft?.chunk?.foreground,
topRight = tuple?.topRight?.chunk?.foreground,
right = tuple?.right?.chunk?.foreground,
bottom = tuple?.bottom?.chunk?.foreground,
bottomLeft = tuple?.bottomLeft?.chunk?.foreground,
bottomRight = tuple?.bottomRight?.chunk?.foreground,
)
}
open fun getBackgroundView(pos: ChunkPos): TileView? {
val get = get(pos) ?: return null
val tuple = InstantWorldChunkTuple(this as This, get)
open fun getBackgroundView(pos: ChunkPos): TileView {
val tuple = get(pos)?.let { InstantWorldChunkTuple(this as This, it) }
return TileView(
center = tuple.chunk.background,
left = tuple.left?.chunk?.background,
top = tuple.top?.chunk?.background,
topLeft = tuple.topLeft?.chunk?.background,
topRight = tuple.topRight?.chunk?.background,
right = tuple.right?.chunk?.background,
bottom = tuple.bottom?.chunk?.background,
bottomLeft = tuple.bottomLeft?.chunk?.background,
bottomRight = tuple.bottomRight?.chunk?.background,
pos = pos,
center = tuple?.chunk?.background,
left = tuple?.left?.chunk?.background,
top = tuple?.top?.chunk?.background,
topLeft = tuple?.topLeft?.chunk?.background,
topRight = tuple?.topRight?.chunk?.background,
right = tuple?.right?.chunk?.background,
bottom = tuple?.bottom?.chunk?.background,
bottomLeft = tuple?.bottomLeft?.chunk?.background,
bottomRight = tuple?.bottomRight?.chunk?.background,
)
}
open fun getRigidForegroundView(pos: ChunkPos): RigidTileView {
return RigidTileView(getForegroundView(pos))
}
open fun getRigidBackgroundView(pos: ChunkPos): RigidTileView {
return RigidTileView(getBackgroundView(pos))
}
fun getTile(pos: Vector2i): ITileState? {
return get(ChunkPos.fromTilePosition(pos))?.foreground?.get(ChunkPos.normalizeCoordinate(pos.x), ChunkPos.normalizeCoordinate(pos.y))
}
@ -559,4 +570,110 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return true
}
// Свет
private fun floodLightInto(
lightmap: Int2Dimensional,
view: RigidTileView,
thisIntensity: Int,
lightBlockerStrength: Int,
posX: Int,
worldPosX: Int,
posY: Int,
worldPosY: Int,
): Int {
if (lightmap[posX, posY] >= thisIntensity) {
return 1
}
val tile = view[worldPosX, worldPosY]
val newIntensity: Int
if (tile.material?.renderParameters?.lightTransparent == false) {
newIntensity = thisIntensity - lightBlockerStrength
} else {
newIntensity = thisIntensity - 1
}
lightmap[posX, posY] = newIntensity.coerceAtLeast(0)
if (newIntensity > 1) {
var c = 1
c += floodLightInto(
lightmap, view, newIntensity, lightBlockerStrength,
posX + 1,
worldPosX + 1,
posY,
worldPosY,
)
c += floodLightInto(
lightmap, view, newIntensity, lightBlockerStrength,
posX - 1,
worldPosX - 1,
posY,
worldPosY,
)
c += floodLightInto(
lightmap, view, newIntensity, lightBlockerStrength,
posX,
worldPosX,
posY + 1,
worldPosY + 1,
)
c += floodLightInto(
lightmap, view, newIntensity, lightBlockerStrength,
posX,
worldPosX,
posY - 1,
worldPosY - 1,
)
return c
}
return 1
}
/**
* Просчитывает распространение света во все стороны на указанной позиции (в тайлах)
*
* [lightIntensity] - максимальное расстояние, которое может пройти свет из точки своего появления.
* Имеет жёсткое ограничение в [CHUNK_SIZE].
*
* [lightBlockerStrength] - какова стоимость "пробития" тайла насквозь, который не пропускает свет
*/
fun floodLight(
lightPosition: Vector2i,
lightIntensity: Int,
lightBlockerStrength: Int = 4,
): Int2Dimensional {
require(lightIntensity >= 1) { "Invalid light intensity $lightIntensity" }
require(lightBlockerStrength >= 1) { "Invalid light blocker strength $lightBlockerStrength" }
require(lightIntensity <= CHUNK_SIZE) { "Too intensive light! $lightIntensity" }
val lightmap = Int2Dimensional(lightIntensity * 2 + 1, lightIntensity * 2 + 1)
val view = getRigidForegroundView(ChunkPos.fromTilePosition(lightPosition))
val calls = floodLightInto(
lightmap,
view,
lightIntensity,
lightBlockerStrength,
lightIntensity,
lightPosition.x - view.pos.tileX,
lightIntensity,
lightPosition.y - view.pos.tileY,
)
println(calls)
return lightmap
}
}