Теперь тайлы сортируются по 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 {
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
@ -139,6 +139,9 @@ private fun loop() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
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)
chunk[3, 2] = rock
@ -147,6 +150,16 @@ private fun loop() {
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) {

View File

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

View File

@ -3,12 +3,11 @@ package ru.dbotthepony.kstarbound.render
import ru.dbotthepony.kstarbound.gl.*
import ru.dbotthepony.kstarbound.math.FloatMatrix
import ru.dbotthepony.kstarbound.world.Chunk
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
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 unloadableBakedMeshes = ArrayList<BakedStaticMesh>()
@ -30,13 +29,13 @@ class ChunkRenderer(val state: GLStateTracker, val chunk: Chunk) : AutoCloseable
bakedMeshes.clear()
}
builders.clear()
layers.clear()
// TODO: Синхронизация (ибо обновления игровой логики будут в потоке вне рендер потока)
for ((pos, tile) in chunk.posToTile) {
if (tile != null) {
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()
for ((baked, builder) in builders) {
for ((baked, builder) in layers.buildList()) {
bakedMeshes.add(BakedStaticMesh(baked, builder))
}
if (clear) {
layers.clear()
}
}
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.defs.TileDefinition
import ru.dbotthepony.kstarbound.defs.TileRenderMatchPiece
import ru.dbotthepony.kstarbound.defs.TileRenderMatchedPiece
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.CHUNK_SIZE
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
import ru.dbotthepony.kstarbound.world.IChunk
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) {
private val simpleBakedPrograms = HashMap<GLTexture2D, SimpleBakedProgram>()
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 (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 {
val variant = (getter.randomDoubleFor(pos) * tile.render.variants).toInt()
val (u0, v0) = texture.pixelToUV(piece.texturePosition + 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)) {
for (renderPiece in matchPiece.pieces) {
if (renderPiece.piece.texture != null) {
tesselateAt(renderPiece.piece, getter, builders.computeIfAbsent(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture))) {
return@computeIfAbsent VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
tesselateAt(renderPiece.piece, getter, layers.getLayer(state.tileRenderers.simpleProgram(state.loadNamedTexture(renderPiece.piece.texture)), tile.render.zLevel - 1) {
return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
}, pos, renderPiece.offset)
} else {
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) {
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] Нужен для получения информации о ближайших блоках
*
* [builders] содержит текущие программы и их билдеры
* [layers] содержит текущие программы и их билдеры и их zPos
*
* Тесселирует тайлы в границы -1f .. CHUNK_SIZEf + 1f на основе [pos]
*/
fun tesselate(getter: IChunk, builders: MutableMap<BakedProgramState, VertexBuilder>, pos: Vector2i) {
fun tesselate(getter: IChunk, layers: TileLayerList, pos: Vector2i) {
// если у нас нет renderTemplate
// то мы просто не можем его отрисовать
tile.render.renderTemplate ?: return
val builder = builders.computeIfAbsent(bakedProgramState) {
return@computeIfAbsent VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
val builder = layers.getLayer(bakedProgramState, tile.render.zLevel) {
return@getLayer VertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS)
}
for ((_, matcher) in tile.render.renderTemplate.matches) {
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 {
const val BASELINE_TEXTURE_SIZE = 8f
const val Z_LEVEL = 1f
}
}