more shadows tests
This commit is contained in:
parent
1254fb276c
commit
86a8c4a130
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хранит состояние отрисовки этого чанка
|
* Хранит состояние отрисовки этого чанка
|
||||||
*
|
*
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user