more shadows tests

This commit is contained in:
DBotThePony 2022-09-14 21:35:23 +07:00
parent 1254fb276c
commit 86a8c4a130
Signed by: DBot
GPG Key ID: DCC23B5715498507
11 changed files with 252 additions and 73 deletions

View File

@ -37,9 +37,9 @@ fun main() {
Starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak")) Starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak"))
//Starbound.addPakPath(File("packed.pak")) //Starbound.addPakPath(File("packed.pak"))
/*Starbound.initializeGame { finished, replaceStatus, status -> Starbound.initializeGame { finished, replaceStatus, status ->
client.putDebugLog(status, replaceStatus) client.putDebugLog(status, replaceStatus)
}*/ }
client.onTermination { client.onTermination {
Starbound.terminateLoading = true Starbound.terminateLoading = true
@ -172,8 +172,8 @@ fun main() {
//ent.position += Vector2d(y = 14.0, x = -10.0) //ent.position += Vector2d(y = 14.0, x = -10.0)
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0) ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
//client.camera.pos.x = 578f client.camera.pos.x = 578f
//client.camera.pos.y = 695f client.camera.pos.y = 695f
client.onDrawGUI { client.onDrawGUI {
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f) client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
@ -186,7 +186,7 @@ fun main() {
//client.camera.pos.y = ent.pos.y.toFloat() //client.camera.pos.y = ent.pos.y.toFloat()
} }
val lightRenderer = LightRenderer(client.gl) /*val lightRenderer = LightRenderer(client.gl)
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight) lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
client.onViewportChanged(lightRenderer::resizeFramebuffer) client.onViewportChanged(lightRenderer::resizeFramebuffer)
@ -223,7 +223,7 @@ fun main() {
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN, (client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE, (client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
)) { )) {
lightRenderer.renderHardLight(lightPosition, color, radius = 10f) lightRenderer.renderSoftLight(lightPosition, color, radius = 40f)
} }
for ((lightPosition, color) in listOf( for ((lightPosition, color) in listOf(
@ -231,11 +231,11 @@ fun main() {
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.GREEN, (client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(9.9f)) to Color.BLUE, (client.screenToWorld(client.mouseCoordinatesF) + Vector2f(9.9f)) to Color.BLUE,
)) { )) {
lightRenderer.renderSoftLight(lightPosition, color, radius = 10f) //lightRenderer.renderSoftLight(lightPosition, color, radius = 10f)
} }
lightRenderer.renderOutputAdditive() lightRenderer.renderOutputAdditive()
} }*/
client.gl.box2dRenderer.drawShapes = false client.gl.box2dRenderer.drawShapes = false
client.gl.box2dRenderer.drawPairs = false client.gl.box2dRenderer.drawPairs = false

View File

@ -1,17 +1,28 @@
package ru.dbotthepony.kstarbound.client package ru.dbotthepony.kstarbound.client
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import org.lwjgl.opengl.GL11.GL_LINES
import org.lwjgl.opengl.GL11.GL_TRIANGLES
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.StatefulVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowQuad
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
import ru.dbotthepony.kstarbound.client.render.EntityRenderer import ru.dbotthepony.kstarbound.client.render.EntityRenderer
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
import ru.dbotthepony.kstarbound.client.render.LightRenderer
import ru.dbotthepony.kstarbound.client.render.TileLayerList import ru.dbotthepony.kstarbound.client.render.TileLayerList
import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition
import ru.dbotthepony.kstarbound.world.* import ru.dbotthepony.kstarbound.world.*
import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.matrix.Matrix4fStack
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
import java.io.Closeable import java.io.Closeable
import java.util.IdentityHashMap import java.util.IdentityHashMap
import java.util.LinkedList import java.util.LinkedList
@ -38,16 +49,10 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
for (mesh in bakedMeshes) { for (mesh in bakedMeshes) {
mesh.first.close() mesh.first.close()
} }
bakedMeshes.clear()
} else {
for (mesh in bakedMeshes) {
unloadableBakedMeshes.add(mesh.first)
}
bakedMeshes.clear()
} }
bakedMeshes.clear()
layers.clear() layers.clear()
for ((pos, tile) in view.posToTile) { for ((pos, tile) in view.posToTile) {
@ -141,8 +146,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
val debugCollisions get() = world.client.settings.debugCollisions val debugCollisions get() = world.client.settings.debugCollisions
val posVector2d = Vector2d(x = pos.x * CHUNK_SIZEd, y = pos.y * CHUNK_SIZEd) val posVector2d = Vector2d(x = pos.x * CHUNK_SIZEd, y = pos.y * CHUNK_SIZEd)
private val unloadableBakedMeshes = ArrayList<ConfiguredStaticMesh>()
private val foregroundRenderer = TileLayerRenderer(foreground::changeset, isBackground = false) private val foregroundRenderer = TileLayerRenderer(foreground::changeset, isBackground = false)
private val backgroundRenderer = TileLayerRenderer(background::changeset, isBackground = true) private val backgroundRenderer = TileLayerRenderer(background::changeset, isBackground = true)
@ -160,28 +163,14 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
* Вызывается перед tesselateStatic() * Вызывается перед tesselateStatic()
*/ */
fun loadRenderers() { fun loadRenderers() {
unloadUnused()
foregroundRenderer.loadRenderers(getForegroundView()) foregroundRenderer.loadRenderers(getForegroundView())
backgroundRenderer.loadRenderers(getBackgroundView()) backgroundRenderer.loadRenderers(getBackgroundView())
} }
private fun unloadUnused() {
if (unloadableBakedMeshes.size != 0) {
for (baked in unloadableBakedMeshes) {
baked.close()
}
unloadableBakedMeshes.clear()
}
}
/** /**
* Отрисовывает всю геометрию напрямую * Отрисовывает всю геометрию напрямую
*/ */
fun render(stack: Matrix4fStack) { fun render(stack: Matrix4fStack) {
unloadUnused()
backgroundRenderer.render(stack) backgroundRenderer.render(stack)
foregroundRenderer.render(stack) foregroundRenderer.render(stack)
} }
@ -190,8 +179,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
* Отрисовывает всю геометрию напрямую, с проверкой, изменился ли чанк * Отрисовывает всю геометрию напрямую, с проверкой, изменился ли чанк
*/ */
fun bakeAndRender(stack: Matrix4fStack) { fun bakeAndRender(stack: Matrix4fStack) {
unloadUnused()
backgroundRenderer.bakeAndRender(stack, this::getBackgroundView) backgroundRenderer.bakeAndRender(stack, this::getBackgroundView)
foregroundRenderer.bakeAndRender(stack, this::getForegroundView) foregroundRenderer.bakeAndRender(stack, this::getForegroundView)
} }
@ -205,9 +192,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
* *
*/ */
fun bake() { fun bake() {
if (state.isSameThread())
unloadUnused()
backgroundRenderer.autoBake(this::getBackgroundView) backgroundRenderer.autoBake(this::getBackgroundView)
foregroundRenderer.autoBake(this::getForegroundView) foregroundRenderer.autoBake(this::getForegroundView)
@ -219,12 +203,83 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
* Загружает в видеопамять всю геометрию напрямую, если есть что загружать * Загружает в видеопамять всю геометрию напрямую, если есть что загружать
*/ */
fun upload() { fun upload() {
unloadUnused()
backgroundRenderer.autoUpload() backgroundRenderer.autoUpload()
foregroundRenderer.autoUpload() foregroundRenderer.autoUpload()
} }
private val hardShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES)
private var hardShadowGeometryRev = -1
private val softShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE)
private var softShadowGeometryRev = -1
val shadowGeometry: LightRenderer.ShadowGeometryRenderer get() {
return object : LightRenderer.ShadowGeometryRenderer {
override fun renderHardGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
stack: Matrix4fStack,
program: GLHardLightGeometryProgram
) {
if (hardShadowGeometryRev == tileChangeset) {
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
hardShadowGeometry.draw(GL_LINES)
return
}
hardShadowGeometryRev = tileChangeset
val builder = hardShadowGeometry
builder.begin()
for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) {
if (foreground[x, y].material != null) {
builder.quad(x.toFloat(), y.toFloat(), x + 1f, y + 1f)
}
}
}
builder.upload()
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
builder.draw(GL_LINES)
}
override fun renderSoftGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
stack: Matrix4fStack,
program: GLSoftLightGeometryProgram
) {
if (softShadowGeometryRev == tileChangeset) {
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
softShadowGeometry.draw(GL_TRIANGLES)
return
}
softShadowGeometryRev = tileChangeset
val builder = softShadowGeometry
builder.begin()
for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) {
if (foreground[x, y].material != null) {
builder.shadowQuad(x.toFloat(), y.toFloat(), x + 1f, y + 1f)
}
}
}
builder.upload()
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
builder.draw(GL_LINES)
}
}
}
/** /**
* Хранит состояние отрисовки этого чанка * Хранит состояние отрисовки этого чанка
* *

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client
import org.lwjgl.opengl.GL46.* import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf 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.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
@ -12,6 +13,8 @@ import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression
import ru.dbotthepony.kstarbound.world.* import ru.dbotthepony.kstarbound.world.*
import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
class ClientWorld( class ClientWorld(
val client: StarboundClient, val client: StarboundClient,
@ -101,13 +104,33 @@ class ClientWorld(
val determineRenderers = ArrayList<ILayeredRenderer>() val determineRenderers = ArrayList<ILayeredRenderer>()
client.lightRenderer.begin()
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) { for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
determineRenderers.add(chunk.second.OneShotRenderer(chunk.first)) determineRenderers.add(chunk.second.OneShotRenderer(chunk.first))
chunk.second.bake() chunk.second.bake()
client.lightRenderer.addShadowGeometry(chunk.second.shadowGeometry)
} }
renderLayeredList(client.gl.matrixStack, determineRenderers) renderLayeredList(client.gl.matrixStack, determineRenderers)
for ((lightPosition, color) in listOf(
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
)) {
client.lightRenderer.renderSoftLight(lightPosition, color, radius = 100f)
}
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.blendFunc = old
physics.debugDraw() physics.debugDraw()
/*for (renderer in determineRenderers) { /*for (renderer in determineRenderers) {

View File

@ -14,6 +14,7 @@ import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
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.LightRenderer
import ru.dbotthepony.kstarbound.client.render.TextAlignY import ru.dbotthepony.kstarbound.client.render.TextAlignY
import ru.dbotthepony.kstarbound.util.formatBytesShort import ru.dbotthepony.kstarbound.util.formatBytesShort
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
@ -171,6 +172,8 @@ class StarboundClient : AutoCloseable {
viewportMatrixWorld = updateViewportMatrixWorld() viewportMatrixWorld = updateViewportMatrixWorld()
glViewport(0, 0, w, h) glViewport(0, 0, w, h)
lightRenderer.resizeFramebuffer(viewportWidth, viewportHeight)
for (callback in onViewportChanged) { for (callback in onViewportChanged) {
callback.invoke(w, h) callback.invoke(w, h)
} }
@ -205,6 +208,11 @@ class StarboundClient : AutoCloseable {
} }
val gl = GLStateTracker() val gl = GLStateTracker()
val lightRenderer = LightRenderer(gl)
init {
lightRenderer.resizeFramebuffer(viewportWidth, viewportHeight)
}
var world: ClientWorld? = ClientWorld(this, 0L, 0) var world: ClientWorld? = ClientWorld(this, 0L, 0)

View File

@ -27,7 +27,10 @@ import java.lang.ref.Cleaner
import java.util.concurrent.ThreadFactory import java.util.concurrent.ThreadFactory
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
private class GLStateSwitchTracker(private val enum: Int, private var value: Boolean = false) { private class GLStateSwitchTracker(private val enum: Int, private var value: Boolean = false) {
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): Boolean { operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): Boolean {
@ -51,13 +54,13 @@ private class GLStateSwitchTracker(private val enum: Int, private var value: Boo
} }
} }
private class GLStateGenericTracker<T>(private var value: T, private val callback: (T) -> Unit) { private class GLStateGenericTracker<T>(private var value: T, private val callback: (T) -> Unit) : ReadWriteProperty<GLStateTracker, T> {
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): T { override fun getValue(thisRef: GLStateTracker, property: KProperty<*>): T {
return value return value
} }
operator fun setValue(glStateTracker: GLStateTracker, property: KProperty<*>, value: T) { override fun setValue(thisRef: GLStateTracker, property: KProperty<*>, value: T) {
glStateTracker.ensureSameThread() thisRef.ensureSameThread()
if (value == this.value) if (value == this.value)
return return
@ -68,6 +71,35 @@ private class GLStateGenericTracker<T>(private var value: T, private val callbac
} }
} }
private class TexturesTracker(maxValue: Int) : ReadWriteProperty<GLStateTracker, GLTexture2D?> {
private val values = arrayOfNulls<GLTexture2D>(maxValue)
override fun getValue(thisRef: GLStateTracker, property: KProperty<*>): GLTexture2D? {
return values[thisRef.activeTexture]
}
override fun setValue(thisRef: GLStateTracker, property: KProperty<*>, value: GLTexture2D?) {
thisRef.ensureSameThread()
require(value == null || thisRef === value.state) { "$value does not belong to $thisRef" }
if (values[thisRef.activeTexture] === value) {
return
}
values[thisRef.activeTexture] = value
if (value == null) {
glBindTexture(GL_TEXTURE_2D, 0)
checkForGLError()
return
}
glBindTexture(GL_TEXTURE_2D, value.pointer)
checkForGLError()
}
}
@Suppress("unused") @Suppress("unused")
data class BlendFunc( data class BlendFunc(
val sourceColor: Func, val sourceColor: Func,
@ -130,6 +162,13 @@ data class BlendFunc(
Func.ZERO, Func.ZERO,
Func.ONE Func.ONE
) )
val MULTIPLY_BY_SRC = BlendFunc(
Func.ZERO,
Func.SRC_COLOR,
Func.ONE,
Func.ZERO
)
} }
} }
@ -318,17 +357,6 @@ class GLStateTracker {
var program: GLShaderProgram? = null var program: GLShaderProgram? = null
private set private set
var texture2D: GLTexture2D? = null
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) return
glBindTexture(GL_TEXTURE_2D, value.pointer)
checkForGLError()
}
var activeTexture = 0 var activeTexture = 0
set(value) { set(value) {
ensureSameThread() ensureSameThread()
@ -340,6 +368,8 @@ class GLStateTracker {
checkForGLError() checkForGLError()
} }
var texture2D: GLTexture2D? by TexturesTracker(80)
var clearColor by GLStateGenericTracker<IStruct4f>(Color.WHITE) { var clearColor by GLStateGenericTracker<IStruct4f>(Color.WHITE) {
val (r, g, b, a) = it val (r, g, b, a) = it
glClearColor(r, g, b, a) glClearColor(r, g, b, a)

View File

@ -85,6 +85,7 @@ class GLHardLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(stat
} }
val transform = this["transform"]!! val transform = this["transform"]!!
val localToWorldTransform = this["localToWorldTransform"]!!
val lightPosition = this["lightPosition"]!! val lightPosition = this["lightPosition"]!!
val builder by lazy { val builder by lazy {
@ -99,6 +100,7 @@ class GLSoftLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(stat
val transform = this["transform"]!! val transform = this["transform"]!!
val lightPenetration = this["lightPenetration"]!! val lightPenetration = this["lightPenetration"]!!
val localToWorldTransform = this["localToWorldTransform"]!!
/** /**
* Vector3f(x, y, size) * Vector3f(x, y, size)

View File

@ -15,17 +15,17 @@ import java.nio.ByteBuffer
* *
* Полезен в случаях, когда размер данных для загрузки заранее не известен. * Полезен в случаях, когда размер данных для загрузки заранее не известен.
*/ */
class HeapVertexBuilder( open class HeapVertexBuilder<T : HeapVertexBuilder<T>>(
attributes: GLAttributeList, attributes: GLAttributeList,
type: GeometryType, type: GeometryType,
) : AbstractVertexBuilder<HeapVertexBuilder>(attributes, type) { ) : AbstractVertexBuilder<T>(attributes, type) {
override val vertexMemory = FastByteArrayOutputStream() final override val vertexMemory = FastByteArrayOutputStream()
override val elementMemory = FastByteArrayOutputStream() final override val elementMemory = FastByteArrayOutputStream()
override var elementIndexType: Int = GL_UNSIGNED_SHORT final override var elementIndexType: Int = GL_UNSIGNED_SHORT
private set private set
override fun ensureIndexCapacity() { final override fun ensureIndexCapacity() {
if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indices.size >= 30000) { if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indices.size >= 30000) {
val backing = elementMemory.array val backing = elementMemory.array
val copy = FastByteArrayInputStream(ByteArray(elementMemory.length) { backing[it] }) val copy = FastByteArrayInputStream(ByteArray(elementMemory.length) { backing[it] })
@ -40,12 +40,12 @@ class HeapVertexBuilder(
} }
} }
override fun resetMemory() { final override fun resetMemory() {
vertexMemory.reset() vertexMemory.reset()
elementMemory.reset() elementMemory.reset()
} }
override fun doUpload(vbo: VertexBufferObject, ebo: VertexBufferObject, drawType: Int) { final override fun doUpload(vbo: VertexBufferObject, ebo: VertexBufferObject, drawType: Int) {
val vboMemory = ByteBuffer.allocateDirect(vertexMemory.length) val vboMemory = ByteBuffer.allocateDirect(vertexMemory.length)
vboMemory.put(vertexMemory.array, 0, vertexMemory.length) vboMemory.put(vertexMemory.array, 0, vertexMemory.length)

View File

@ -0,0 +1,47 @@
package ru.dbotthepony.kstarbound.client.gl.vertex
import org.lwjgl.opengl.GL46
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
import java.io.Closeable
class StatefulVertexBuilder(
val state: GLStateTracker,
attributes: GLAttributeList,
type: GeometryType,
) : HeapVertexBuilder<StatefulVertexBuilder>(attributes, type), Closeable {
private val vao = state.newVAO()
private val vbo = state.newVBO()
private val ebo = state.newEBO()
init {
vao.bind()
vbo.bind()
ebo.bind()
attributes.apply(vao, true)
vao.unbind()
vbo.unbind()
ebo.unbind()
}
fun upload(drawType: Int = GL46.GL_STATIC_DRAW) {
upload(vbo, ebo, drawType)
}
fun bind() = vao.bind()
fun unbind() = vao.unbind()
fun draw(primitives: Int = GL46.GL_TRIANGLES) {
bind()
GL46.glDrawElements(primitives, indexCount, elementIndexType, 0L)
checkForGLError()
}
override fun close() {
vao.close()
vbo.close()
ebo.close()
}
}

View File

@ -26,8 +26,8 @@ import ru.dbotthepony.kvector.vector.nfloat.Vector3f
// https://slembcke.github.io/SuperFastSoftShadows // https://slembcke.github.io/SuperFastSoftShadows
class LightRenderer(val state: GLStateTracker) { class LightRenderer(val state: GLStateTracker) {
interface ShadowGeometryRenderer { interface ShadowGeometryRenderer {
fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram) fun renderHardGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
fun renderSoftGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram) fun renderSoftGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
} }
private val geometry = ArrayList<ShadowGeometryRenderer>() private val geometry = ArrayList<ShadowGeometryRenderer>()
@ -41,6 +41,10 @@ class LightRenderer(val state: GLStateTracker) {
return this.geometry.remove(geometry) return this.geometry.remove(geometry)
} }
fun clearShadowGeometry() {
geometry.clear()
}
/** /**
* Сюда происходит рендер маски света и самого света * Сюда происходит рендер маски света и самого света
*/ */
@ -49,7 +53,7 @@ class LightRenderer(val state: GLStateTracker) {
/** /**
* Сюда накапливается отрисованный свет * Сюда накапливается отрисованный свет
*/ */
private val framebufferAccumulator = GLFrameBuffer(state) val framebufferAccumulator = GLFrameBuffer(state)
val outputTexture: GLTexture2D? get() = framebufferAccumulator.texture val outputTexture: GLTexture2D? get() = framebufferAccumulator.texture
@ -58,7 +62,7 @@ class LightRenderer(val state: GLStateTracker) {
framebufferAccumulator.reattachTexture(width, height, GL_RGBA) framebufferAccumulator.reattachTexture(width, height, GL_RGBA)
} }
fun begin() { fun begin(clearGeometry: Boolean = true) {
state.ensureSameThread() state.ensureSameThread()
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) { if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
@ -77,6 +81,10 @@ class LightRenderer(val state: GLStateTracker) {
state.clearColor = old state.clearColor = old
framebufferAccumulator.unbind() framebufferAccumulator.unbind()
if (clearGeometry) {
geometry.clear()
}
} }
/** /**
@ -161,7 +169,7 @@ class LightRenderer(val state: GLStateTracker) {
state.blendFunc = BlendFunc.ONLY_ALPHA state.blendFunc = BlendFunc.ONLY_ALPHA
for (renderer in geometry) { for (renderer in geometry) {
renderer.renderHardGeometry(position, stack, state.programs.hardLightGeometry) renderer.renderHardGeometry(this, position, stack, state.programs.hardLightGeometry)
} }
state.programs.light.use() state.programs.light.use()
@ -189,7 +197,7 @@ class LightRenderer(val state: GLStateTracker) {
position: Vector2f, position: Vector2f,
color: Color = Color.WHITE, color: Color = Color.WHITE,
radius: Float = 10f, radius: Float = 10f,
innerRadius: Float = 1f, innerRadius: Float = radius / 3f,
stack: Matrix4fStack = state.matrixStack stack: Matrix4fStack = state.matrixStack
) { ) {
state.ensureSameThread() state.ensureSameThread()
@ -220,7 +228,7 @@ class LightRenderer(val state: GLStateTracker) {
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
for (renderer in geometry) { for (renderer in geometry) {
renderer.renderSoftGeometry(position, stack, state.programs.softLightGeometry) renderer.renderSoftGeometry(this, position, stack, state.programs.softLightGeometry)
} }
state.programs.light.use() state.programs.light.use()
@ -248,6 +256,10 @@ class LightRenderer(val state: GLStateTracker) {
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 256) StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 256)
} }
val hugeBuilder by lazy {
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 16384)
}
val lineBuilder by lazy { val lineBuilder by lazy {
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.LINES, 256) StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.LINES, 256)
} }

View File

@ -4,10 +4,11 @@
layout (location = 0) in vec2 vertexPos; layout (location = 0) in vec2 vertexPos;
uniform mat4 transform; uniform mat4 transform;
uniform mat4 localToWorldTransform;
out vec2 originalPos; out vec2 originalPos;
void main() { void main() {
gl_Position = transform * vec4(vertexPos, 0.0, 1.0); gl_Position = transform * localToWorldTransform * vec4(vertexPos, 0.0, 1.0);
originalPos = vertexPos; originalPos = (localToWorldTransform * vec4(vertexPos, 0.0, 1.0)).xy;
} }

View File

@ -9,6 +9,7 @@ layout (location = 0) in vec4 packedVertexPos;
layout (location = 1) in vec2 edgeType; layout (location = 1) in vec2 edgeType;
uniform mat4 transform; uniform mat4 transform;
uniform mat4 localToWorldTransform;
uniform vec3 lightPositionAndSize; uniform vec3 lightPositionAndSize;
uniform float lightPenetration; uniform float lightPenetration;
@ -28,8 +29,8 @@ void main() {
float lightSize = lightPositionAndSize.z; float lightSize = lightPositionAndSize.z;
// Unpack the vertex shader input. // Unpack the vertex shader input.
vec2 endpoint_a = packedVertexPos.zw; vec2 endpoint_a = (localToWorldTransform * vec4(packedVertexPos.zw, 0.0, 1.0)).xy;
vec2 endpoint_b = packedVertexPos.xy; vec2 endpoint_b = (localToWorldTransform * vec4(packedVertexPos.xy, 0.0, 1.0)).xy;
vec2 endpoint = mix(endpoint_a, endpoint_b, edgeType.x); vec2 endpoint = mix(endpoint_a, endpoint_b, edgeType.x);
// Deltas from the segment to the light center. // Deltas from the segment to the light center.