Кеш GLShaderProgram, загрузка матриц напрямую в видеопамять,

дополнения TileDef
This commit is contained in:
DBotThePony 2022-02-03 11:00:29 +07:00
parent 5953f033f3
commit f0af2d5a8e
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 275 additions and 95 deletions

View File

@ -138,48 +138,27 @@ private fun loop() {
state.blend = true
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
val rock = ChunkTile(Starbound.getTileDefinition("alienrock")!!)
val obsidian = ChunkTile(Starbound.getTileDefinition("obsidian")!!)
val corruptdirt = ChunkTile(Starbound.getTileDefinition("corruptdirt")!!)
val darkwood = ChunkTile(Starbound.getTileDefinition("darkwood")!!)
val chunk = Starbound.world.setTile(Vector2i(2, 2), rock)
chunk[3, 2] = rock
chunk[4, 2] = rock
chunk[4, 3] = rock
chunk[4, 4] = rock
chunk[3, 4] = rock
chunk[5, 4] = rock
chunk[2, 4] = obsidian
chunk[2, 3] = obsidian
chunk[1, 4] = corruptdirt
chunk[1, 3] = corruptdirt
chunk[1, 5] = darkwood
chunk[2, 5] = darkwood
chunk[3, 5] = darkwood
chunk[1, 1] = darkwood
chunk[2, 1] = darkwood
chunk[3, 1] = darkwood
for (x in 0 until 1) {
for (y in 0 until 4) {
//chunk[x, y] = ChunkTile(Starbound.loadTileDefinition("alienrock"))
}
}
val chunk = Starbound.world.getOrMakeChunk(Vector2i(2, 2))
var x = 0
var y = 0
for (tile in Starbound.tilesAccess.values) {
chunk[x++, y] = ChunkTile(tile)
chunk[x++, y] = ChunkTile(tile)
chunk[x++, y] = ChunkTile(tile)
chunk[x++, y] = ChunkTile(tile)
chunk[x++, y] = ChunkTile(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[x++, y] = (tile)
chunk[x, y + 1] = (tile)
if (x >= 24) {
if (x >= 32) {
x = 0
y++
y += 2
}
}
@ -194,37 +173,8 @@ private fun loop() {
state.matrixStack.clear(viewportMatrixGame.toMutableMatrix())
// program.use()
// val time = glfwGetTime()
// program["globalColor"] = Uniform3f(sin(time).toFloat(), cos(time).toFloat(), sin(time).toFloat())
// program["ourTexture"] = 0
// texture.bind()
// vao.bind()
// ebo.bind()
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
// checkForGLError()
//tileRenderer.renderPiece()
//state.shaderVertexTexture.use()
//state.shaderVertexTexture["_texture"] = 0
////state.shaderVertexTexture["_transform"] = Vector3f.FORWARD.rotateAroundThis(PI * glfwGetTime())
////state.shaderVertexTexture["_transform"] = state.matrixStack.push().scale((glfwGetTime() % 1000.0).toFloat(), (glfwGetTime() % 1000.0).toFloat(), (glfwGetTime() % 1000.0).toFloat()).last
////state.shaderVertexTexture["_transform"] = Matrix4f.perspective(((glfwGetTime() * 16.0) % 180.0).toFloat(), 0.1f, 100f)
//state.shaderVertexTexture["_transform"] = state.matrixStack.push().scale(x = 10f, y = 10f).translateWithScale(10f, 10f).last
//texture.bind()
//texture.textureMinFilter = GL_NEAREST
//texture.textureMagFilter = GL_NEAREST
//chunkRenderer.bind()
//glDrawElements(GL_TRIANGLES, chunkRenderer.indexCount, GL_UNSIGNED_INT, 0)
//checkForGLError()
state.matrixStack.push().scale(x = 20f, y = 20f).translateWithScale(0f, 0f)
chunkRenderer.render()
//state.matrixStack.translateWithScale(18f)
//chunkRenderer.render()
glfwSwapBuffers(window) // swap the color buffers

View File

@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.math.Vector2i
import ru.dbotthepony.kstarbound.util.Color
@ -18,6 +19,9 @@ data class TileDefinition(
val itemDrop: String?,
val description: String,
val shortdescription: String,
val blocksLiquidFlow: Boolean,
val soil: Boolean,
val tillableMod: Int,
val racialDescription: ImmutableMap<String, String>,
val footstepSound: String?,
@ -40,6 +44,10 @@ class TileDefinitionBuilder {
var description = "..."
var shortdescription = "..."
var blocksLiquidFlow = true
var soil = false
var tillableMod = 0
val racialDescription = ArrayList<Pair<String, String>>()
var footstepSound: String? = null
@ -62,6 +70,9 @@ class TileDefinitionBuilder {
itemDrop = itemDrop,
description = description,
shortdescription = shortdescription,
blocksLiquidFlow = blocksLiquidFlow,
soil = soil,
tillableMod = tillableMod,
footstepSound = footstepSound,
health = health,
@ -72,6 +83,8 @@ class TileDefinitionBuilder {
}
companion object {
private val LOGGER = LogManager.getLogger()
fun fromJson(input: JsonObject): TileDefinitionBuilder {
val builder = TileDefinitionBuilder()
@ -83,12 +96,19 @@ class TileDefinitionBuilder {
builder.particleColor = Color(input["particleColor"].asJsonArray)
builder.itemDrop = input["itemDrop"]?.asString
builder.description = input["description"]?.asString ?: "..."
builder.shortdescription = input["shortdescription"]?.asString ?: "..."
builder.description = input["description"]?.asString ?: builder.description
builder.shortdescription = input["shortdescription"]?.asString ?: builder.shortdescription
builder.footstepSound = input["footstepSound"]?.asString
builder.blocksLiquidFlow = input["footstepSound"]?.asBoolean ?: builder.blocksLiquidFlow
builder.soil = input["footstepSound"]?.asBoolean ?: builder.soil
builder.health = input["health"].asInt
builder.tillableMod = input["health"]?.asInt ?: builder.tillableMod
builder.category = input["category"].asString
if (input["variants"] != null) {
LOGGER.warn("Tile {} has `variants` ({}) defined as top level property (expected to be under `renderParameters`)", builder.materialName, input["variants"].asString)
}
for (key in input.keySet()) {
if (key.endsWith("Description") && key.length != "Description".length) {
builder.racialDescription.add(key.substring(0, key.length - "Description".length) to input[key].asString)
@ -98,9 +118,9 @@ class TileDefinitionBuilder {
input["renderParameters"]?.asJsonObject?.let {
builder.render.texture = it["texture"].asString
builder.render.variants = it["variants"].asInt
builder.render.lightTransparent = it["lightTransparent"]?.asBoolean ?: false
builder.render.occludesBelow = it["occludesBelow"]?.asBoolean ?: true
builder.render.multiColored = it["multiColored"]?.asBoolean ?: false
builder.render.lightTransparent = it["lightTransparent"]?.asBoolean ?: builder.render.lightTransparent
builder.render.occludesBelow = it["occludesBelow"]?.asBoolean ?: builder.render.occludesBelow
builder.render.multiColored = it["multiColored"]?.asBoolean ?: builder.render.multiColored
builder.render.zLevel = it["zLevel"].asInt
}

View File

@ -1,10 +1,16 @@
package ru.dbotthepony.kstarbound.gl
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import org.lwjgl.opengl.GL41
import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.api.IStruct3f
import ru.dbotthepony.kstarbound.api.IStruct4F
import ru.dbotthepony.kstarbound.math.AbstractMatrix3f
import ru.dbotthepony.kstarbound.math.AbstractMatrix4f
import ru.dbotthepony.kstarbound.math.FloatMatrix
import java.util.*
import kotlin.collections.HashSet
class ShaderLinkException(reason: String) : RuntimeException(reason)
@ -37,11 +43,21 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
if (value.rows == 3 && value.columns == 3) {
// Матрица 3x3
glProgramUniformMatrix3fv(program.pointer, pointer, false, value.toFloatArray())
if (value is AbstractMatrix3f) {
glProgramUniformMatrix3fv(program.pointer, pointer, false, value.toFloatBuffer())
} else {
glProgramUniformMatrix3fv(program.pointer, pointer, false, value.toFloatArray())
}
checkForGLError()
} else if (value.rows == 4 && value.columns == 4) {
// Матрица 4x4
glProgramUniformMatrix4fv(program.pointer, pointer, false, value.toFloatArray())
if (value is AbstractMatrix4f) {
glProgramUniformMatrix4fv(program.pointer, pointer, false, value.toFloatBuffer())
} else {
glProgramUniformMatrix4fv(program.pointer, pointer, false, value.toFloatArray())
}
checkForGLError()
} else {
throw IllegalArgumentException("Can not use matrix with these dimensions: ${value.rows}x${value.columns}")
@ -59,16 +75,28 @@ class GLShaderProgram(val state: GLStateTracker, vararg shaders: GLShader) {
private val attached = HashSet<GLShader>()
val access = object : Collection<GLShader> by attached {}
private val locationCache = Object2ObjectArrayMap<String, Optional<GLUniformLocation>>()
/**
* Возвращает GLUniformLocation или null, если у данной программы нет такого uniform
*
* В т.ч. если она была в коде шейдера, но нигде не использовалась (отсечена компилятором)
*
* Результат поиска кешируется, но для повышения производительности вызывающий код желательно так же
* должен кешировать результат (как локальное свойство или переменная, в случае многократного использования)
*/
operator fun get(name: String): GLUniformLocation? {
state.ensureSameThread()
check(linked) { "Shader program is not linked!" }
val location = glGetUniformLocation(pointer, name)
return locationCache.computeIfAbsent(name, Object2ObjectFunction {
val location = glGetUniformLocation(pointer, name)
if (location == -1)
return null
if (location == -1)
return@Object2ObjectFunction Optional.empty<GLUniformLocation>()
return GLUniformLocation(this, name, location)
return@Object2ObjectFunction Optional.of(GLUniformLocation(this, name, location))
}).orElse(null)
}
operator fun set(name: String, value: Uniform4f) = this[name]?.set(value)

View File

@ -1,5 +1,8 @@
package ru.dbotthepony.kstarbound.math
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import kotlin.math.PI
import kotlin.math.tan
@ -74,6 +77,68 @@ interface MutableFloatMatrix<T : MutableFloatMatrix<T>> : FloatMatrix<T> {
}
abstract class AbstractMatrix4f<T : AbstractMatrix4f<T>> : FloatMatrix<T> {
companion object {
private val directBuffer by lazy { ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()!! }
}
/**
* Наполняет синглтон и возвращает DirectFloatBuffer который необходимо использовать сразу,
* нет никаких гарантий что он не будет изменён в дальнейшем
*
* Функция предназначена исключительно для загрузки данной матрицы в память видеокарты
*
* Данный буфер всегда будет одним и тем же, но его содержимое обновлено
*/
fun toFloatBuffer(columnMajor: Boolean = true): FloatBuffer {
val directBuffer = directBuffer
directBuffer.position(0)
if (columnMajor) {
directBuffer.put(m00)
directBuffer.put(m10)
directBuffer.put(m20)
directBuffer.put(m30)
directBuffer.put(m01)
directBuffer.put(m11)
directBuffer.put(m21)
directBuffer.put(m31)
directBuffer.put(m02)
directBuffer.put(m12)
directBuffer.put(m22)
directBuffer.put(m32)
directBuffer.put(m03)
directBuffer.put(m13)
directBuffer.put(m23)
directBuffer.put(m33)
} else {
directBuffer.put(m00)
directBuffer.put(m01)
directBuffer.put(m02)
directBuffer.put(m03)
directBuffer.put(m10)
directBuffer.put(m11)
directBuffer.put(m12)
directBuffer.put(m13)
directBuffer.put(m20)
directBuffer.put(m21)
directBuffer.put(m22)
directBuffer.put(m23)
directBuffer.put(m30)
directBuffer.put(m31)
directBuffer.put(m32)
directBuffer.put(m33)
}
directBuffer.position(0)
return directBuffer
}
abstract val m00: Float; abstract val m01: Float; abstract val m02: Float; abstract val m03: Float
abstract val m10: Float; abstract val m11: Float; abstract val m12: Float; abstract val m13: Float
abstract val m20: Float; abstract val m21: Float; abstract val m22: Float; abstract val m23: Float
@ -545,6 +610,52 @@ data class MutableMatrix4f(
}
abstract class AbstractMatrix3f<T : AbstractMatrix3f<T>> : FloatMatrix<T> {
companion object {
private val directBuffer by lazy { ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()!! }
}
/**
* Наполняет синглтон и возвращает DirectFloatBuffer который необходимо использовать сразу,
* нет никаких гарантий что он не будет изменён в дальнейшем
*
* Функция предназначена исключительно для загрузки данной матрицы в память видеокарты
*
* Данный буфер всегда будет одним и тем же, но его содержимое обновлено
*/
fun toFloatBuffer(columnMajor: Boolean = true): FloatBuffer {
val directBuffer = directBuffer
directBuffer.position(0)
if (columnMajor) {
directBuffer.put(m00)
directBuffer.put(m10)
directBuffer.put(m20)
directBuffer.put(m01)
directBuffer.put(m11)
directBuffer.put(m21)
directBuffer.put(m02)
directBuffer.put(m12)
directBuffer.put(m22)
} else {
directBuffer.put(m00)
directBuffer.put(m01)
directBuffer.put(m02)
directBuffer.put(m10)
directBuffer.put(m11)
directBuffer.put(m12)
directBuffer.put(m20)
directBuffer.put(m21)
directBuffer.put(m22)
}
directBuffer.position(0)
return directBuffer
}
abstract val m00: Float; abstract val m01: Float; abstract val m02: Float
abstract val m10: Float; abstract val m11: Float; abstract val m12: Float
abstract val m20: Float; abstract val m21: Float; abstract val m22: Float

View File

@ -1,12 +1,11 @@
package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.TileDefinition
import ru.dbotthepony.kstarbound.math.Vector2i
data class ChunkTile(val def: TileDefinition) {
companion object {
}
var color = -1
var forceVariant = -1
}
interface IChunk {
@ -16,8 +15,23 @@ interface IChunk {
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
operator fun get(x: Int, y: Int): ChunkTile?
/**
* Возвращает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
operator fun get(pos: Vector2i) = get(pos.x, pos.y)
/**
* Возвращает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
fun getBackground(x: Int, y: Int): ChunkTile?
/**
* Возвращает фоновый тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
fun getBackground(pos: Vector2i) = getBackground(pos.x, pos.y)
/**
* Относительная проверка находится ли координата вне границ чагка
*/
@ -39,7 +53,6 @@ interface IChunk {
return long
}
/**
* Возвращает псевдослучайное нормализированное Double для заданной позиции
*
@ -49,7 +62,6 @@ interface IChunk {
return (randomLongFor(x, y) / 9.223372036854776E18) / 2.0 + 0.5
}
/**
* Возвращает псевдослучайное Long для заданной позиции
*
@ -105,7 +117,21 @@ interface IChunkSetter {
/**
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
operator fun set(x: Int, y: Int, tile: ChunkTile?)
operator fun set(x: Int, y: Int, tile: TileDefinition?): ChunkTile?
/**
* Устанавливает тайл по ОТНОСИТЕЛЬНЫМ координатам внутри чанка
*/
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
@ -121,11 +147,16 @@ 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)
}
class Chunk(val world: World, override val pos: ChunkPos) : IMutableChunk {
open class Chunk(val world: World, override val pos: ChunkPos) : IMutableChunk {
/**
* Хранит тайлы как x + y * CHUNK_SIZE
*/
val tiles = arrayOfNulls<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE)
private val tiles = arrayOfNulls<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE)
/**
* Хранит фоновые тайлы как x + y * CHUNK_SIZE
*/
private val backgroundTiles = arrayOfNulls<ChunkTile>(CHUNK_SIZE * CHUNK_SIZE)
override operator fun get(x: Int, y: Int): ChunkTile? {
if (isOutside(x, y))
@ -134,13 +165,45 @@ class Chunk(val world: World, override val pos: ChunkPos) : IMutableChunk {
return tiles[x or (y shl CHUNK_SHIFT)]
}
override operator fun set(x: Int, y: Int, tile: ChunkTile?) {
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
}
@ -149,13 +212,10 @@ class Chunk(val world: World, override val pos: ChunkPos) : IMutableChunk {
val EMPTY = object : IMutableChunk {
override val pos = ChunkPos(0, 0)
override fun get(x: Int, y: Int): ChunkTile? {
return null
}
override fun set(x: Int, y: Int, tile: ChunkTile?) {
}
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

@ -1,13 +1,20 @@
package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.defs.TileDefinition
import ru.dbotthepony.kstarbound.math.Vector2i
class World(val seed: Long = 0L) {
val chunkMap = ArrayList<Pair<ChunkPos, Chunk>>()
private val chunkMap = ArrayList<Pair<ChunkPos, Chunk>>()
private var lastAccessedChunk: Chunk? = null
fun getChunk(pos: ChunkPos): Chunk? {
if (lastAccessedChunk?.pos == pos) {
return lastAccessedChunk
}
for ((k, v) in chunkMap) {
if (k == pos) {
lastAccessedChunk = v
return v
}
}
@ -15,9 +22,11 @@ class World(val seed: Long = 0L) {
return null
}
fun getChunk(pos: Vector2i) = getChunk(ChunkPos(pos))
fun getOrMakeChunk(pos: ChunkPos): Chunk {
if (lastAccessedChunk?.pos == pos) {
return lastAccessedChunk!!
}
for ((k, v) in chunkMap) {
if (k == pos) {
return v
@ -25,17 +34,19 @@ class World(val seed: Long = 0L) {
}
val chunk = Chunk(this, pos)
lastAccessedChunk = chunk
chunkMap.add(pos to chunk)
return chunk
}
fun getChunk(pos: Vector2i) = getChunk(ChunkPos(pos))
fun getOrMakeChunk(pos: Vector2i) = getOrMakeChunk(ChunkPos(pos))
fun getTile(pos: Vector2i): ChunkTile? {
return getChunk(pos)?.get(pos.x, pos.y)
}
fun setTile(pos: Vector2i, tile: ChunkTile?): Chunk {
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
return chunk