diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 2dde9dbd..f75fa460 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -52,8 +52,8 @@ fun main() { var set = 0L var parse = 0L - for (chunkX in 0 .. 94) { - for (chunkY in 0 .. 61) { + for (chunkX in 17 .. 18) { + for (chunkY in 21 .. 21) { var t = System.currentTimeMillis() val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte())) find += System.currentTimeMillis() - t @@ -179,6 +179,7 @@ fun main() { client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f) client.gl.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f) client.gl.font.render("${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f) + client.gl.font.render("${ChunkPos.fromTilePosition(client.camera.pos.toDoubleVector())}", y = 160f, scale = 0.25f) } client.onPreDrawWorld { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt index 9909bdd4..8c5bc9d4 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt @@ -9,6 +9,7 @@ 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.shadowLine import ru.dbotthepony.kstarbound.client.gl.vertex.shadowQuad import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh import ru.dbotthepony.kstarbound.client.render.EntityRenderer @@ -20,6 +21,7 @@ import ru.dbotthepony.kstarbound.world.* import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f +import ru.dbotthepony.kvector.util2d.intersectCircleRectangle import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.nfloat.Vector2f import ru.dbotthepony.kvector.vector.nfloat.Vector3f @@ -207,75 +209,89 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk Unit) { + builder.begin() - hardShadowGeometryRev = tileChangeset + for (x in this.x * SHADOW_GEOMETRY_SQUARE_SIZE until (this.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) { + for (y in this.y * SHADOW_GEOMETRY_SQUARE_SIZE until (this.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) { + if (foreground[x, y].material?.renderParameters?.lightTransparent == false) { + if (x == 0 || foreground[x - 1, y].material?.renderParameters?.lightTransparent != true) { + line(builder, x.toFloat(), y.toFloat(), x.toFloat(), y + 1f) + } - val builder = hardShadowGeometry + if (x == CHUNK_SIZE - 1 || foreground[x + 1, y].material?.renderParameters?.lightTransparent != true) { + line(builder, x + 1f, y.toFloat(), x + 1f, y + 1f) + } - builder.begin() + if (y == 0 || foreground[x, y - 1].material?.renderParameters?.lightTransparent != true) { + line(builder, x.toFloat(), y.toFloat(), x + 1f, y.toFloat()) + } - 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) + if (y == CHUNK_SIZE - 1 || foreground[x, y + 1].material?.renderParameters?.lightTransparent != true) { + line(builder, x.toFloat(), y + 1f, 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 - } + builder.upload() + } - softShadowGeometryRev = tileChangeset + fun buildHardGeometry() { + hardShadowGeometryRev = tileChangeset - val builder = softShadowGeometry + buildGeometry(hardShadowGeometry) { it, x0, y0, x1, y1 -> + it.vertex().pushVec2f(x0, y0) + it.vertex().pushVec2f(x1, y1) + } + } - builder.begin() + fun buildSoftGeometry() { + softShadowGeometryRev = tileChangeset + buildGeometry(softShadowGeometry, StatefulVertexBuilder::shadowLine) + } - 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) - } - } - } + override fun renderHardGeometry( + renderer: LightRenderer, + lightPosition: Vector2f, + lightRadius: Float, + stack: Matrix4fStack, + program: GLHardLightGeometryProgram + ) { + if (hardShadowGeometryRev != tileChangeset) { + buildHardGeometry() + } - builder.upload() + hardShadowGeometry.draw(GL_LINES) + } - 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, + lightRadius: Float, + stack: Matrix4fStack, + program: GLSoftLightGeometryProgram + ) { + if (softShadowGeometryRev != tileChangeset) { + buildSoftGeometry() + } + + softShadowGeometry.draw(GL_TRIANGLES) + } + } + + private val shadowGeometry = ArrayList() + + init { + for (x in 0 until SHADOW_GEOMETRY_SUBDIVISION) { + for (y in 0 until SHADOW_GEOMETRY_SUBDIVISION) { + shadowGeometry.add(ShadowGeometryTracker(x, y)) } } } @@ -286,9 +302,81 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk Unit, Int>>() + override fun renderHardGeometry( + renderer: LightRenderer, + lightPosition: Vector2f, + lightRadius: Float, + stack: Matrix4fStack, + program: GLHardLightGeometryProgram + ) { + if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) { + return + } + + var setOnce = false + + for (geometry in shadowGeometry) { + if (intersectCircleRectangle( + lightPosition, + lightRadius, + origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) + ) { + if (!setOnce) { + program.localToWorldTransform.set( + Matrix4f.IDENTITY.translateWithMultiplication( + Vector3f(x = origin.x * CHUNK_SIZEf, + y = origin.y * CHUNK_SIZEf))) + + setOnce = true + } + + geometry.renderHardGeometry(renderer, lightPosition, lightRadius, stack, program) + } + } + } + + override fun renderSoftGeometry( + renderer: LightRenderer, + lightPosition: Vector2f, + lightRadius: Float, + stack: Matrix4fStack, + program: GLSoftLightGeometryProgram + ) { + if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) { + return + } + + var setOnce = false + + for (geometry in shadowGeometry) { + if (intersectCircleRectangle( + lightPosition, + lightRadius, + origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE, + origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) + ) { + if (!setOnce) { + program.localToWorldTransform.set( + Matrix4f.IDENTITY.translateWithMultiplication( + Vector3f(x = origin.x * CHUNK_SIZEf, + y = origin.y * CHUNK_SIZEf))) + + setOnce = true + } + + geometry.renderSoftGeometry(renderer, lightPosition, lightRadius, stack, program) + } + } + } + init { for ((baked, zLevel) in backgroundRenderer.bakedMeshes) { layerQueue.add(baked::renderStacked to (zLevel + Z_LEVEL_BACKGROUND)) @@ -414,4 +502,9 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk LOGGER.error("LWJGL error {}: {}", error, description) @@ -166,6 +170,12 @@ class StarboundClient : AutoCloseable { input.installCallback(window) GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h -> + if (w == 0 || h == 0) { + isRenderingGame = false + return@glfwSetFramebufferSizeCallback + } + + isRenderingGame = true viewportWidth = w viewportHeight = h viewportMatrixScreen = updateViewportMatrixScreen() @@ -287,6 +297,13 @@ class StarboundClient : AutoCloseable { return false } + if (!isRenderingGame) { + gl.cleanup() + GLFW.glfwPollEvents() + LockSupport.parkNanos(1_000_000L) + return true + } + val measure = GLFW.glfwGetTime() if (frameRenderTime != 0.0 && Starbound.initialized) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt index 6a414294..47a8643a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt @@ -43,6 +43,13 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin return this } + fun set(value: Float): GLUniformLocation { + program.state.ensureSameThread() + glProgramUniform1f(program.pointer, pointer, value) + checkForGLError() + return this + } + private val buff3x3: FloatBuffer by lazy { ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() } private val buff4x4: FloatBuffer by lazy { ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LightRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LightRenderer.kt index f573619a..246eb168 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LightRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LightRenderer.kt @@ -26,8 +26,8 @@ import ru.dbotthepony.kvector.vector.nfloat.Vector3f // https://slembcke.github.io/SuperFastSoftShadows class LightRenderer(val state: GLStateTracker) { interface ShadowGeometryRenderer { - fun renderHardGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram) - fun renderSoftGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram) + fun renderHardGeometry(renderer: LightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLHardLightGeometryProgram) + fun renderSoftGeometry(renderer: LightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLSoftLightGeometryProgram) } private val geometry = ArrayList() @@ -169,7 +169,7 @@ class LightRenderer(val state: GLStateTracker) { state.blendFunc = BlendFunc.ONLY_ALPHA for (renderer in geometry) { - renderer.renderHardGeometry(this, position, stack, state.programs.hardLightGeometry) + renderer.renderHardGeometry(this, position, radius, stack, state.programs.hardLightGeometry) } state.programs.light.use() @@ -224,11 +224,12 @@ class LightRenderer(val state: GLStateTracker) { state.programs.softLightGeometry.use() state.programs.softLightGeometry.transform.set(stack.last) state.programs.softLightGeometry.lightPositionAndSize.set(Vector3f(position, innerRadius)) + state.programs.softLightGeometry.lightPenetration.set(1f) state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA for (renderer in geometry) { - renderer.renderSoftGeometry(this, position, stack, state.programs.softLightGeometry) + renderer.renderSoftGeometry(this, position, radius, stack, state.programs.softLightGeometry) } state.programs.light.use()