Немного прибераемся в самом низу render pipeline

This commit is contained in:
DBotThePony 2023-02-21 19:15:35 +07:00
parent c7980d9068
commit 0a00595520
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 143 additions and 126 deletions

View File

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

View File

@ -22,17 +22,10 @@ class ClientWorld(
) )
} }
/** fun addLayers(
* Отрисовывает этот с обрезкой невидимой геометрии с точки зрения [size] в Starbound Units
*
* Все координаты "местности" сохраняются, поэтому, если отрисовывать слишком далеко от 0, 0
* то геометрия может начать искажаться из-за погрешности плавающей запятой
*/
fun render(
size: AABB, size: AABB,
isScreenspaceRender: Boolean = true layers: LayeredRenderer
) { ) {
val layers = LayeredRenderer()
client.lightRenderer.begin() client.lightRenderer.begin()
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) { for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
@ -42,7 +35,6 @@ class ClientWorld(
//client.lightRenderer.addShadowGeometry(renderer) //client.lightRenderer.addShadowGeometry(renderer)
} }
layers.render(client.gl.matrixStack)
/* /*
for ((lightPosition, color) in listOf( for ((lightPosition, color) in listOf(
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED, (client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,

View File

@ -16,6 +16,7 @@ import ru.dbotthepony.kstarbound.client.gl.ScissorRect
import ru.dbotthepony.kstarbound.client.input.UserInput import ru.dbotthepony.kstarbound.client.input.UserInput
import ru.dbotthepony.kstarbound.client.render.Camera import ru.dbotthepony.kstarbound.client.render.Camera
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
import ru.dbotthepony.kstarbound.client.render.TextAlignY import ru.dbotthepony.kstarbound.client.render.TextAlignY
import ru.dbotthepony.kstarbound.client.render.TileRenderers import ru.dbotthepony.kstarbound.client.render.TileRenderers
import ru.dbotthepony.kstarbound.util.JVMTimeSource import ru.dbotthepony.kstarbound.util.JVMTimeSource
@ -40,26 +41,21 @@ class StarboundClient(val starbound: Starbound) : Closeable {
val window: Long val window: Long
val camera = Camera(this) val camera = Camera(this)
val input = UserInput() val input = UserInput()
val gl: GLStateTracker
var gameTerminated = false var gameTerminated = false
private set private set
var viewportWidth = 800
private set
var viewportHeight = 600
private set
/** /**
* Матрица преобразования экранных координат (в пикселях) в нормализованные координаты * Матрица преобразования экранных координат (в пикселях) в нормализованные координаты
*/ */
var viewportMatrixScreen = updateViewportMatrixScreen() var viewportMatrixScreen: Matrix4f
private set private set
/** /**
* Матрица преобразования мировых координат в нормализованные координаты * Матрица преобразования мировых координат в нормализованные координаты
*/ */
var viewportMatrixWorld = updateViewportMatrixWorld() var viewportMatrixWorld: Matrix4f
private set private set
private val startupTextList = ArrayList<String>() private val startupTextList = ArrayList<String>()
@ -172,29 +168,29 @@ class StarboundClient(val starbound: Starbound) : Closeable {
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 6) GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 6)
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE) GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE)
window = GLFW.glfwCreateWindow(viewportWidth, viewportHeight, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL) window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" } require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
startupTextList.add("Created GLFW window") startupTextList.add("Created GLFW window")
input.installCallback(window) input.installCallback(window)
GLFW.glfwMakeContextCurrent(window)
gl = GLStateTracker(starbound)
GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h -> GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h ->
if (w == 0 || h == 0) { if (w == 0 || h == 0) {
isRenderingGame = false isRenderingGame = false
return@glfwSetFramebufferSizeCallback } else {
} isRenderingGame = true
viewportMatrixScreen = updateViewportMatrixScreen()
viewportMatrixWorld = updateViewportMatrixWorld()
gl.setViewport(0, 0, w, h)
isRenderingGame = true lightRenderer.resizeFramebuffer(w, h)
viewportWidth = w
viewportHeight = h
viewportMatrixScreen = updateViewportMatrixScreen()
viewportMatrixWorld = updateViewportMatrixWorld()
glViewport(0, 0, w, h)
lightRenderer.resizeFramebuffer(viewportWidth, viewportHeight) for (callback in onViewportChanged) {
callback.invoke(w, h)
for (callback in onViewportChanged) { }
callback.invoke(w, h)
} }
} }
@ -213,12 +209,14 @@ class StarboundClient(val starbound: Starbound) : Closeable {
(vidmode.width() - pWidth[0]) / 2, (vidmode.width() - pWidth[0]) / 2,
(vidmode.height() - pHeight[0]) / 2 (vidmode.height() - pHeight[0]) / 2
) )
gl.setViewport(0, 0, pWidth[0], pHeight[0])
viewportMatrixScreen = updateViewportMatrixScreen()
viewportMatrixWorld = updateViewportMatrixWorld()
} finally { } finally {
stack.close() stack.close()
} }
GLFW.glfwMakeContextCurrent(window)
// vsync // vsync
GLFW.glfwSwapInterval(0) GLFW.glfwSwapInterval(0)
@ -226,7 +224,9 @@ class StarboundClient(val starbound: Starbound) : Closeable {
putDebugLog("Initialized GLFW window") putDebugLog("Initialized GLFW window")
} }
val gl = GLStateTracker(starbound) val viewportWidth by gl::viewportWidth
val viewportHeight by gl::viewportHeight
val tileRenderers = TileRenderers(this) val tileRenderers = TileRenderers(this)
val lightRenderer = GPULightRenderer(gl) val lightRenderer = GPULightRenderer(gl)
@ -236,8 +236,6 @@ class StarboundClient(val starbound: Starbound) : Closeable {
var world: ClientWorld? = ClientWorld(this, 0L, 0) var world: ClientWorld? = ClientWorld(this, 0L, 0)
fun ensureSameThread() = gl.ensureSameThread()
init { init {
putDebugLog("Initialized OpenGL context") putDebugLog("Initialized OpenGL context")
gl.clearColor = Color.SLATE_GREY gl.clearColor = Color.SLATE_GREY
@ -269,87 +267,34 @@ class StarboundClient(val starbound: Starbound) : Closeable {
val settings = ClientSettings() val settings = ClientSettings()
private val onDrawGUI = ArrayList<() -> Unit>()
private val onPreDrawWorld = ArrayList<(LayeredRenderer) -> Unit>()
private val onPostDrawWorld = ArrayList<() -> Unit>()
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>() private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
fun onViewportChanged(callback: (width: Int, height: Int) -> Unit) { fun onViewportChanged(callback: (width: Int, height: Int) -> Unit) {
onViewportChanged.add(callback) onViewportChanged.add(callback)
} }
private val onDrawGUI = ArrayList<() -> Unit>()
fun onDrawGUI(lambda: () -> Unit) { fun onDrawGUI(lambda: () -> Unit) {
onDrawGUI.add(lambda) onDrawGUI.add(lambda)
} }
private val onPreDrawWorld = ArrayList<() -> Unit>() fun onPreDrawWorld(lambda: (LayeredRenderer) -> Unit) {
fun onPreDrawWorld(lambda: () -> Unit) {
onPreDrawWorld.add(lambda) onPreDrawWorld.add(lambda)
} }
private val onPostDrawWorld = ArrayList<() -> Unit>()
fun onPostDrawWorld(lambda: () -> Unit) { fun onPostDrawWorld(lambda: () -> Unit) {
onPostDrawWorld.add(lambda) onPostDrawWorld.add(lambda)
} }
private val onPostDrawWorldOnce = ArrayList<() -> Unit>() fun onPostDrawWorldOnce(lambda: (LayeredRenderer) -> Unit) {
fun onPostDrawWorldOnce(lambda: () -> Unit) {
onPostDrawWorldOnce.add(lambda) onPostDrawWorldOnce.add(lambda)
} }
private val scissorStack = LinkedList<ScissorRect>()
fun pushScissorRect(x: Float, y: Float, width: Float, height: Float) {
return pushScissorRect(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt())
}
@Suppress("NAME_SHADOWING")
fun pushScissorRect(x: Int, y: Int, width: Int, height: Int) {
var x = x
var y = y
var width = width
var height = height
val peek = scissorStack.lastOrNull()
if (peek != null) {
x = x.coerceAtLeast(peek.x)
y = y.coerceAtLeast(peek.y)
width = width.coerceAtMost(peek.width)
height = height.coerceAtMost(peek.height)
if (peek.x == x && peek.y == y && peek.width == width && peek.height == height) {
scissorStack.add(peek)
return
}
}
val rect = ScissorRect(x, y, width, height)
scissorStack.add(rect)
gl.scissorRect = rect
gl.scissor = true
}
fun popScissorRect() {
scissorStack.removeLast()
val peek = scissorStack.lastOrNull()
if (peek == null) {
gl.scissor = false
return
}
val y = viewportHeight - peek.y - peek.height
gl.scissorRect = ScissorRect(peek.x, y, peek.width, peek.height)
}
val currentScissorRect get() = scissorStack.lastOrNull()
fun renderFrame(): Boolean { fun renderFrame(): Boolean {
ensureSameThread() gl.ensureSameThread()
if (GLFW.glfwWindowShouldClose(window)) { if (GLFW.glfwWindowShouldClose(window)) {
close() close()
@ -364,40 +309,48 @@ class StarboundClient(val starbound: Starbound) : Closeable {
} }
val measure = JVMTimeSource.INSTANCE.seconds val measure = JVMTimeSource.INSTANCE.seconds
val world = world
if (frameRenderTime != 0.0 && starbound.initialized) if (world != null) {
world?.think(frameRenderTime) val layers = LayeredRenderer()
gl.clearColor = Color.SLATE_GREY if (frameRenderTime != 0.0 && starbound.initialized)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) world.think(frameRenderTime)
gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f())
gl.matrixStack.push() gl.clearColor = Color.SLATE_GREY
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f())
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
for (lambda in onPreDrawWorld) { gl.matrixStack.push()
lambda.invoke() .translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
for (lambda in onPreDrawWorld) {
lambda.invoke(layers)
}
for (i in onPostDrawWorldOnce.size - 1 downTo 0) {
onPostDrawWorldOnce[i].invoke(layers)
onPostDrawWorldOnce.removeAt(i)
}
world.addLayers(
layers = layers,
size = AABB.rectangle(
camera.pos.toDoubleVector(),
viewportWidth / settings.zoom / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT))
layers.render(gl.matrixStack)
for (lambda in onPostDrawWorld) {
lambda.invoke()
}
gl.matrixStack.pop()
} }
for (i in onPostDrawWorldOnce.size - 1 downTo 0) {
onPostDrawWorldOnce[i].invoke()
onPostDrawWorldOnce.removeAt(i)
}
world?.render(
AABB.rectangle(
camera.pos.toDoubleVector(),
viewportWidth / settings.zoom / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT))
for (lambda in onPostDrawWorld) {
lambda.invoke()
}
gl.matrixStack.pop()
gl.matrixStack.clear(viewportMatrixScreen.toMutableMatrix4f()) gl.matrixStack.clear(viewportMatrixScreen.toMutableMatrix4f())
val thisTime = System.currentTimeMillis() val thisTime = System.currentTimeMillis()

View File

@ -24,10 +24,12 @@ import ru.dbotthepony.kvector.vector.Color
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.lang.ref.Cleaner import java.lang.ref.Cleaner
import java.util.*
import java.util.concurrent.ThreadFactory import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
import kotlin.math.roundToInt
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -150,6 +152,7 @@ class GLStateTracker(val locator: ISBFileLocator) {
val thread: Thread = Thread.currentThread() val thread: Thread = Thread.currentThread()
val box2dRenderer = Box2DRenderer(this) val box2dRenderer = Box2DRenderer(this)
private val scissorStack = LinkedList<ScissorRect>()
private val cleanerBacklog = ArrayList<() -> Unit>() private val cleanerBacklog = ArrayList<() -> Unit>()
@Volatile @Volatile
@ -347,6 +350,75 @@ class GLStateTracker(val locator: ISBFileLocator) {
checkForGLError() checkForGLError()
} }
var viewportX: Int = 0
private set
var viewportY: Int = 0
private set
var viewportWidth: Int = 0
private set
var viewportHeight: Int = 0
private set
fun setViewport(x: Int, y: Int, width: Int, height: Int) {
ensureSameThread()
if (viewportX != x || viewportY != y || viewportWidth != width || viewportHeight != height) {
glViewport(x, y, width, height)
checkForGLError("Setting viewport")
viewportX = x
viewportY = y
viewportWidth = width
viewportHeight = height
}
}
fun pushScissorRect(x: Float, y: Float, width: Float, height: Float) {
return pushScissorRect(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt())
}
@Suppress("NAME_SHADOWING")
fun pushScissorRect(x: Int, y: Int, width: Int, height: Int) {
var x = x
var y = y
var width = width
var height = height
val peek = scissorStack.lastOrNull()
if (peek != null) {
x = x.coerceAtLeast(peek.x)
y = y.coerceAtLeast(peek.y)
width = width.coerceAtMost(peek.width)
height = height.coerceAtMost(peek.height)
if (peek.x == x && peek.y == y && peek.width == width && peek.height == height) {
scissorStack.add(peek)
return
}
}
val rect = ScissorRect(x, y, width, height)
scissorStack.add(rect)
scissorRect = rect
scissor = true
}
fun popScissorRect() {
scissorStack.removeLast()
val peek = scissorStack.lastOrNull()
if (peek == null) {
scissor = false
return
}
val y = viewportHeight - peek.y - peek.height
scissorRect = ScissorRect(peek.x, y, peek.width, peek.height)
}
val currentScissorRect get() = scissorStack.lastOrNull()
fun ensureSameThread() { fun ensureSameThread() {
if (thread !== Thread.currentThread()) { if (thread !== Thread.currentThread()) {
throw IllegalAccessException("Trying to access $this outside of $thread!") throw IllegalAccessException("Trying to access $this outside of $thread!")