Теперь тайлы сортируются по z глубине

This commit is contained in:
DBotThePony 2022-02-02 23:24:46 +07:00
parent 8a13a99713
commit 247e1a8ba5
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 73 additions and 21 deletions

View File

@ -38,7 +38,7 @@ private fun updateViewportMatrixA(): Matrix4f {
} }
private fun updateViewportMatrixB(): Matrix4f { private fun updateViewportMatrixB(): Matrix4f {
return Matrix4f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f) return Matrix4f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 1f, 100f)
} }
var window = 0L var window = 0L
@ -139,6 +139,9 @@ private fun loop() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
val rock = ChunkTile(Starbound.loadTileDefinition("alienrock")) val rock = ChunkTile(Starbound.loadTileDefinition("alienrock"))
val obsidian = ChunkTile(Starbound.loadTileDefinition("obsidian"))
val corruptdirt = ChunkTile(Starbound.loadTileDefinition("corruptdirt"))
val darkwood = ChunkTile(Starbound.loadTileDefinition("darkwood"))
val chunk = Starbound.world.setTile(Vector2i(2, 2), rock) val chunk = Starbound.world.setTile(Vector2i(2, 2), rock)
chunk[3, 2] = rock chunk[3, 2] = rock
@ -147,6 +150,16 @@ private fun loop() {
chunk[4, 4] = rock chunk[4, 4] = rock
chunk[3, 4] = rock chunk[3, 4] = rock
chunk[5, 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 (x in 0 until 1) {

View File

@ -43,6 +43,7 @@ class GLStateTracker {
} }
var blend by GLStateSwitchTracker(GL_BLEND) var blend by GLStateSwitchTracker(GL_BLEND)
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
var VBO: GLVertexBufferObject? = null var VBO: GLVertexBufferObject? = null
set(value) { set(value) {

View File

@ -3,12 +3,11 @@ package ru.dbotthepony.kstarbound.render
import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.gl.*
import ru.dbotthepony.kstarbound.math.FloatMatrix import ru.dbotthepony.kstarbound.math.FloatMatrix
import ru.dbotthepony.kstarbound.world.Chunk import ru.dbotthepony.kstarbound.world.Chunk
import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable { class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable {
private val builders = HashMap<BakedProgramState, VertexBuilder>() private val layers = TileLayerList()
private val bakedMeshes = ArrayList<BakedStaticMesh>() private val bakedMeshes = ArrayList<BakedStaticMesh>()
private val unloadableBakedMeshes = ArrayList<BakedStaticMesh>() private val unloadableBakedMeshes = ArrayList<BakedStaticMesh>()
@ -30,13 +29,13 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable
bakedMeshes.clear() bakedMeshes.clear()
} }
builders.clear() layers.clear()
// TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока) // TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока)
for ((pos, tile) in chunk.posToTile) { for ((pos, tile) in chunk.posToTile) {
if (tile != null) { if (tile != null) {
val renderer = state.tileRenderers.get(tile.def.materialName) val renderer = state.tileRenderers.get(tile.def.materialName)
renderer.tesselate(chunk, builders, pos) renderer.tesselate(chunk, layers, pos)
} }
} }
} }
@ -67,12 +66,16 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable
} }
} }
fun uploadStatic() { fun uploadStatic(clear: Boolean = true) {
unloadUnused() unloadUnused()
for ((baked, builder) in builders) { for ((baked, builder) in layers.buildList()) {
bakedMeshes.add(BakedStaticMesh(baked, builder)) bakedMeshes.add(BakedStaticMesh(baked, builder))
} }
if (clear) {
layers.clear()
}
} }
fun render(transform: FloatMatrix<*> = state.matrixStack.last) { fun render(transform: FloatMatrix<*> = state.matrixStack.last) {

View File

@ -5,16 +5,50 @@ import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.TileDefinition import ru.dbotthepony.kstarbound.defs.TileDefinition
import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece
import ru.dbotthepony.kstarbound.defs.TileRenderMatchedPiece
import ru.dbotthepony.kstarbound.defs.TileRenderPiece import ru.dbotthepony.kstarbound.defs.TileRenderPiece
import ru.dbotthepony.kstarbound.gl.* import ru.dbotthepony.kstarbound.gl.*
import ru.dbotthepony.kstarbound.math.Matrix4f import ru.dbotthepony.kstarbound.math.Matrix4f
import ru.dbotthepony.kstarbound.math.Vector2i import ru.dbotthepony.kstarbound.math.Vector2i
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.IChunk import ru.dbotthepony.kstarbound.world.IChunk
import kotlin.collections.HashMap import kotlin.collections.HashMap
data class TileLayer(val bakedProgramState: BakedProgramState, val vertexBuilder: VertexBuilder, val zPos: Int)
class TileLayerList {
private val layers = HashMap<BakedProgramState, ArrayList<TileLayer>>()
fun getLayer(programState: BakedProgramState, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder {
val list = layers.computeIfAbsent(programState) {ArrayList()}
for (layer in list) {
if (layer.zPos == zLevel) {
return layer.vertexBuilder
}
}
val computed = TileLayer(programState, compute.invoke(), zLevel)
list.add(computed)
return computed.vertexBuilder
}
fun buildList(): List<TileLayer> {
val list = ArrayList<TileLayer>()
for (getList in layers.values) {
list.addAll(getList)
}
list.sortBy {
// унарный минус для инвентирования порядка (сначала маленькие, потом большие)
return@sortBy -it.zPos
}
return list
}
fun clear() = layers.clear()
}
class TileRenderers(val state: GLStateTracker) { class TileRenderers(val state: GLStateTracker) {
private val simpleBakedPrograms = HashMap<GLTexture2D, SimpleBakedProgram>() private val simpleBakedPrograms = HashMap<GLTexture2D, SimpleBakedProgram>()
private val tileRenderers = HashMap<String, TileRenderer>() private val tileRenderers = HashMap<String, TileRenderer>()
@ -91,23 +125,23 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
val (u0, v0) = texture.pixelToUV(piece.texturePosition) val (u0, v0) = texture.pixelToUV(piece.texturePosition)
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize) val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize)
builder.quadZ(a, b, c, d, 1f, VertexTransformers.uv(u0, v1, u1, v0)) builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
} else { } else {
val variant = (getter.randomDoubleFor(pos) * tile.render.variants).toInt() val variant = (getter.randomDoubleFor(pos) * tile.render.variants).toInt()
val (u0, v0) = texture.pixelToUV(piece.texturePosition + piece.variantStride * variant) val (u0, v0) = texture.pixelToUV(piece.texturePosition + piece.variantStride * variant)
val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize + piece.variantStride * variant) val (u1, v1) = texture.pixelToUV(piece.texturePosition + piece.textureSize + piece.variantStride * variant)
builder.quadZ(a, b, c, d, 1f, VertexTransformers.uv(u0, v1, u1, v0)) builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0))
} }
} }
private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: IChunk, builders: MutableMap<BakedProgramState, VertexBuilder>, pos: Vector2i, thisBuilder: VertexBuilder) { private fun tesselatePiece(matchPiece: TileRenderMatchPiece, getter: IChunk, layers: TileLayerList, pos: Vector2i, thisBuilder: VertexBuilder) {
if (matchPiece.test(getter, tile, pos)) { if (matchPiece.test(getter, tile, pos)) {
for (renderPiece in matchPiece.pieces) { for (renderPiece in matchPiece.pieces) {
if (renderPiece.piece.texture != null) { if (renderPiece.piece.texture != null) {
tesselateAt(renderPiece.piece, getter, builders.computeIfAbsent(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture))) { tesselateAt(renderPiece.piece, getter, layers.getLayer(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture)), tile.render.zLevel - 1) {
return@computeIfAbsent VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
}, pos, renderPiece.offset) }, pos, renderPiece.offset)
} else { } else {
tesselateAt(renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset) tesselateAt(renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset)
@ -115,7 +149,7 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
} }
for (subPiece in matchPiece.subMatches) { for (subPiece in matchPiece.subMatches) {
tesselatePiece(subPiece, getter, builders, pos, thisBuilder) tesselatePiece(subPiece, getter, layers, pos, thisBuilder)
} }
} }
} }
@ -125,22 +159,22 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
* *
* [getter] Нужен для получения информации о ближайших блоках * [getter] Нужен для получения информации о ближайших блоках
* *
* [builders] содержит текущие программы и их билдеры * [layers] содержит текущие программы и их билдеры и их zPos
* *
* Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos] * Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos]
*/ */
fun tesselate(getter: IChunk, builders: MutableMap<BakedProgramState, VertexBuilder>, pos: Vector2i) { fun tesselate(getter: IChunk, layers: TileLayerList, pos: Vector2i) {
// если у нас нет renderTemplate // если у нас нет renderTemplate
// то мы просто не можем его отрисовать // то мы просто не можем его отрисовать
tile.render.renderTemplate ?: return tile.render.renderTemplate ?: return
val builder = builders.computeIfAbsent(bakedProgramState) { val builder = layers.getLayer(bakedProgramState, tile.render.zLevel) {
return@computeIfAbsent VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS) return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
} }
for ((_, matcher) in tile.render.renderTemplate.matches) { for ((_, matcher) in tile.render.renderTemplate.matches) {
for (matchPiece in matcher.pieces) { for (matchPiece in matcher.pieces) {
tesselatePiece(matchPiece, getter, builders, pos, builder) tesselatePiece(matchPiece, getter, layers, pos, builder)
} }
} }
@ -240,5 +274,6 @@ class TileRenderer(val state: GLStateTracker, val tile: TileDefinition) {
companion object { companion object {
const val BASELINE_TEXTURE_SIZE = 8f const val BASELINE_TEXTURE_SIZE = 8f
const val Z_LEVEL = 1f
} }
} }