From 08c2b5a685316a9af9fa4c66549cb40136a5a846 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Fri, 16 Sep 2022 19:54:41 +0700 Subject: [PATCH] even more raycasted lights tests --- .../kstarbound/client/ClientWorld.kt | 96 +++++++++++++++++-- .../kstarbound/client/StarboundClient.kt | 1 + .../kstarbound/client/gl/GLFrameBuffer.kt | 3 +- .../kstarbound/client/gl/GLStateTracker.kt | 44 ++++----- .../kstarbound/client/gl/program/Programs.kt | 52 +++++++++- .../client/render/GPULightRenderer.kt | 6 +- .../ru/dbotthepony/kstarbound/world/World.kt | 49 +++++----- src/main/resources/shaders/flat_color.fsh | 10 ++ src/main/resources/shaders/flat_color.vsh | 13 +++ 9 files changed, 211 insertions(+), 63 deletions(-) create mode 100644 src/main/resources/shaders/flat_color.fsh create mode 100644 src/main/resources/shaders/flat_color.vsh diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt index 516755f2..ce60e2ea 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt @@ -1,8 +1,11 @@ package ru.dbotthepony.kstarbound.client import org.lwjgl.opengl.GL46.* +import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.gl.BlendFunc +import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer +import ru.dbotthepony.kstarbound.client.gl.GLTexture2D import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ @@ -18,6 +21,8 @@ import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.nfloat.Vector2f import ru.dbotthepony.kvector.vector.nint.Vector2i +import java.nio.ByteBuffer +import java.nio.ByteOrder import kotlin.math.PI import kotlin.math.cos import kotlin.math.roundToInt @@ -192,17 +197,92 @@ class ClientWorld( } */ - client.gl.quadWireframe { - for ((intensity, tpos) in rayLightCircleNaive(pos, 24.0, falloffByTravel = 1.5, falloffByTile = 4.0)) { - it.quad( - tpos.x.toFloat(), - tpos.y.toFloat(), - tpos.x + 1f, - tpos.y + 1f - ) + val result = rayLightCircleNaive(pos, 48.0, falloffByTravel = 1.0, falloffByTile = 3.0) + val result2 = rayLightCircleNaive(pos + Vector2d(-8.0), 24.0, falloffByTravel = 1.0, falloffByTile = 3.0) + val frame = GLFrameBuffer(client.gl) + frame.attachTexture(client.viewportWidth, client.viewportHeight) + + frame.bind() + + client.gl.clearColor = Color.BLACK + glClear(GL_COLOR_BUFFER_BIT) + + client.gl.blendFunc = BlendFunc.ADDITIVE + + /*client.gl.quadColor { + for (row in 0 until result.rows) { + for (column in 0 until result.columns) { + if (result[column, row] > 0.05) { + val color = result[column, row].toFloat() * 1.5f + + it.quad( + pos.x.roundToInt() - result.rows.toFloat() / 2f + row.toFloat(), + pos.y.roundToInt() - result.columns.toFloat() / 2f + column.toFloat(), + pos.x.roundToInt() - result.rows.toFloat() / 2f + row + 1f, + pos.y.roundToInt() - result.columns.toFloat() / 2f + column + 1f + ) { a, b -> a.pushVec4f(color, color, color, 1f) } + } + } } } + client.gl.quadColor { + for (row in 0 until result2.rows) { + for (column in 0 until result2.columns) { + if (result2[column, row] > 0.05) { + val color = result2[column, row].toFloat() * 1.5f + + it.quad( + pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row.toFloat(), + pos.y.roundToInt() - result2.columns.toFloat() / 2f + column.toFloat(), + pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row + 1f, + pos.y.roundToInt() - result2.columns.toFloat() / 2f + column + 1f + ) { a, b -> a.pushVec4f(color, 0f, 0f, 1f) } + } + } + } + }*/ + + val lightTextureWidth = (client.viewportWidth / PIXELS_IN_STARBOUND_UNIT).roundToInt() + val lightTextureHeight = (client.viewportHeight / PIXELS_IN_STARBOUND_UNIT).roundToInt() + + val textureBuffer = ByteBuffer.allocateDirect(lightTextureWidth * lightTextureHeight * 3) + textureBuffer.order(ByteOrder.LITTLE_ENDIAN) + + for (x in 0 until result.columns.coerceAtMost(lightTextureWidth)) { + for (y in 0 until result.rows.coerceAtMost(lightTextureHeight)) { + textureBuffer.position(x * 3 + y * lightTextureWidth * 3) + + if (result[x, y] > 0.05) { + val color = result[x, y].toFloat() * 1.5f + + textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte()) + textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte()) + textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte()) + } + } + } + + frame.unbind() + + val texture = GLTexture2D(client.gl) + + textureBuffer.position(0) + texture.upload(GL_RGB, lightTextureWidth, lightTextureHeight, GL_RGB, GL_UNSIGNED_BYTE, textureBuffer) + + texture.textureMinFilter = GL_LINEAR + texture.textureMagFilter = GL_LINEAR + + client.gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC + + client.gl.activeTexture = 0 + client.gl.texture2D = texture + client.gl.programs.viewTextureQuad.run() + + client.gl.blendFunc = old + + frame.close() + texture.close() physics.debugDraw() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 788729a5..591cb512 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -362,6 +362,7 @@ class StarboundClient : AutoCloseable { if (frameRenderTime != 0.0 && Starbound.initialized) world?.think(frameRenderTime) + gl.clearColor = Color.SLATE_GREY glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f()) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt index 1449e6ee..3c37d2f3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt @@ -82,12 +82,11 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { private set override fun close() { - state.ensureSameThread() - if (!isValid) return cleanable.cleanManual() + texture?.close() isValid = false } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt index 86bf65c5..77f6b19a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt @@ -234,10 +234,15 @@ class GLStateTracker { if (!cleanManual && LOGGER.isTraceEnabled) LOGGER.trace("{} with ID {} was GC'd by JVM, without manually calling close()", name, nativeRef) - synchronized(cleanerHits) { - cleanerHits.add { - fn(nativeRef) - checkForGLError() + if (isSameThread()) { + fn(nativeRef) + checkForGLError() + } else { + synchronized(cleanerHits) { + cleanerHits.add { + fn(nativeRef) + checkForGLError() + } } } } @@ -570,30 +575,12 @@ class GLStateTracker { } } - val flat2DQuads = object : GLStreamBuilderList { - override val small by lazy { - return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS, 1024) - } - } - val flat2DTexturedQuads = object : GLStreamBuilderList { override val small by lazy { return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS, 1024) } } - val flat2DQuadLines = object : GLStreamBuilderList { - override val small by lazy { - return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES, 1024) - } - } - - val flat2DQuadWireframe = object : GLStreamBuilderList { - override val small by lazy { - return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 1024) - } - } - val quadWireframe by lazy { StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 16384) } @@ -617,6 +604,19 @@ class GLStateTracker { builder.draw(GL_LINES) } + inline fun quadColor(lambda: (StreamVertexBuilder) -> Unit) { + val builder = programs.flatColor.builder + + builder.begin() + lambda.invoke(builder) + builder.upload() + + programs.flatColor.use() + programs.flatColor.transform.set(matrixStack.last) + + builder.draw(GL_TRIANGLES) + } + inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) { quadWireframe(color) { it.quad(value.mins.x.toFloat(), value.mins.y.toFloat(), value.maxs.x.toFloat(), value.maxs.y.toFloat()) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt index df9f19fa..f9ab52e3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt @@ -191,9 +191,37 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc } } +class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex_blur") { + init { + link() + } + + val texture = this["texture0"]!! + + private val builder by lazy { + val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) + + builder.begin() + builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv()) + builder.upload() + + builder + } + + fun run(texture: Int = 0) { + use() + this.texture.set(texture) + builder.draw() + } + + companion object { + val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build() + } +} + class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) { val builder by lazy { - StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1024) + StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 2048) } companion object { @@ -201,15 +229,33 @@ class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransfo } } +class GLFlatColorProgram(state: GLStateTracker) : GLInternalProgram(state, "flat_color") { + init { + link() + } + + val transform = this["transform"]!! + + val builder by lazy { + StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384) + } + + companion object { + val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC4F).build() + } +} + class GLPrograms(val state: GLStateTracker) { val tile by SimpleProgram("tile", ::GLTransformableColorableProgram) val font by SimpleProgram("font", ::GLTransformableColorableProgram) val flat by SimpleProgram("flat", ::GLFlatProgram) + val flatColor by lazy { GLFlatColorProgram(state) } val liquid by lazy { GLLiquidProgram(state) } val light by lazy { GLLightProgram(state) } val hardLightGeometry by lazy { GLHardLightGeometryProgram(state) } val softLightGeometry by lazy { GLSoftLightGeometryProgram(state) } - val colorQuad by lazy { GLColorQuadProgram(state) } - val textureQuad by lazy { GLTextureQuadProgram(state) } + val viewColorQuad by lazy { GLColorQuadProgram(state) } + val viewTextureQuad by lazy { GLTextureQuadProgram(state) } + val viewTextureBlurQuad by lazy { GLTextureBlurredQuadProgram(state) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt index 49ca41dc..3d0ec7ff 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt @@ -98,7 +98,7 @@ class GPULightRenderer(val state: GLStateTracker) { state.activeTexture = 0 state.texture2D = outputTexture state.blendFunc = BlendFunc.ADDITIVE - state.programs.textureQuad.run(0) + state.programs.viewTextureQuad.run(0) } finally { state.blendFunc = oldFunc } @@ -114,7 +114,7 @@ class GPULightRenderer(val state: GLStateTracker) { state.activeTexture = 0 state.texture2D = outputTexture state.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA - state.programs.textureQuad.run(0) + state.programs.viewTextureQuad.run(0) } finally { state.blendFunc = oldFunc } @@ -129,7 +129,7 @@ class GPULightRenderer(val state: GLStateTracker) { state.blendFunc = BlendFunc.ADDITIVE state.activeTexture = 0 state.texture2D = framebufferRender.texture - state.programs.textureQuad.run(0) + state.programs.viewTextureQuad.run(0) } finally { state.blendFunc = oldFunc framebufferAccumulator.unbind() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 5ab2c03f..c6a2b047 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -17,11 +17,14 @@ import ru.dbotthepony.kstarbound.util.Timer import ru.dbotthepony.kstarbound.world.entities.CollisionResolution import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController +import ru.dbotthepony.kvector.narray.Double2Dimensional import ru.dbotthepony.kvector.narray.Int2Dimensional import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABBi import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.nint.Vector2i +import java.util.LinkedList +import java.util.function.DoubleBinaryOperator import kotlin.math.PI import kotlin.math.absoluteValue import kotlin.math.cos @@ -67,10 +70,10 @@ private val veryPreciseFan by lazy { makeDirFan(0.25) } private fun chooseLightRayFan(size: Double): List { return when (size) { in 0.0 .. 8.0 -> potatoDirFan - in 8.0 .. 12.0 -> veryRoughDirFan - in 12.0 .. 18.0 -> roughDirFan - in 18.0 .. 24.0 -> dirFan - in 24.0 .. 32.0 -> preciseFan + in 8.0 .. 16.0 -> veryRoughDirFan + in 16.0 .. 24.0 -> roughDirFan + in 24.0 .. 48.0 -> dirFan + in 48.0 .. 96.0 -> preciseFan // in 32.0 .. 48.0 -> veryPreciseFan else -> veryPreciseFan } @@ -399,7 +402,7 @@ abstract class World, ChunkType : Chunk>() + val tiles = LinkedList>() var prev = Vector2i(Int.MIN_VALUE, Int.MAX_VALUE) var hitTile: Pair? = null @@ -482,37 +485,33 @@ abstract class World, ChunkType : Chunk> { - val result = Object2DoubleAVLTreeMap { a, b -> - val cmp = a.x.compareTo(b.x) + falloffByTravel: Double = 1.0, + ): Double2Dimensional { + val combinedResult = Double2Dimensional(intensity.roundToInt() * 2, intensity.roundToInt() * 2) + val baselineX = position.x.roundToInt() - intensity.roundToInt() + val baselineY = position.y.roundToInt() - intensity.roundToInt() - if (cmp != 0) { - return@Object2DoubleAVLTreeMap cmp - } + val dirs = chooseLightRayFan(intensity) + val mul = 1.0 / dirs.size - return@Object2DoubleAVLTreeMap a.y.compareTo(b.y) - } - - result.defaultReturnValue(-1.0) - - for (dir in chooseLightRayFan(intensity)) { + for (dir in dirs) { val result2 = rayLightNaive(position, dir, intensity, falloffByTile, falloffByTravel) for (pair in result2) { - val existing = result.getDouble(pair.second) - - if (existing < pair.first) { - result.put(pair.second, pair.first) - } + combinedResult[pair.second.y - baselineY, pair.second.x - baselineX] += pair.first * mul } } - return result.map { it.value to it.key } + return combinedResult } } @@ -560,7 +559,7 @@ abstract class World, ChunkType : Chunk> { + ): Double2Dimensional { return CachedGetter().rayLightCircleNaive(position, intensity, falloffByTile, falloffByTravel) } diff --git a/src/main/resources/shaders/flat_color.fsh b/src/main/resources/shaders/flat_color.fsh new file mode 100644 index 00000000..8eda7992 --- /dev/null +++ b/src/main/resources/shaders/flat_color.fsh @@ -0,0 +1,10 @@ + +#version 460 + +in vec4 finalVertexColor; + +out vec4 color; + +void main() { + color = finalVertexColor; +} diff --git a/src/main/resources/shaders/flat_color.vsh b/src/main/resources/shaders/flat_color.vsh new file mode 100644 index 00000000..6989e33f --- /dev/null +++ b/src/main/resources/shaders/flat_color.vsh @@ -0,0 +1,13 @@ + +#version 460 + +layout (location = 0) in vec2 vertexPos; +layout (location = 1) in vec4 vertexColor; +uniform mat4 transform; + +out vec4 finalVertexColor; + +void main() { + gl_Position = transform * vec4(vertexPos, 0.5, 1.0); + finalVertexColor = vertexColor; +}