From ef838d52c2b3c0600c5ff4dd50902b41f13955a8 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 6 Sep 2023 00:04:48 +0700 Subject: [PATCH] Updated render classes structure to be less alien --- .../kstarbound/client/ClientChunk.kt | 17 +-- .../kstarbound/client/ClientWorld.kt | 39 ++--- .../kstarbound/client/gl/BlendFunc.kt | 4 +- .../kstarbound/client/gl/GLFrameBuffer.kt | 17 +-- .../kstarbound/client/gl/GLTexture.kt | 22 +-- .../kstarbound/client/gl/VertexArrayObject.kt | 26 +--- .../client/gl/VertexBufferObject.kt | 31 +--- .../client/gl/shader/GLShaderProgram.kt | 7 +- .../kstarbound/client/gl/shader/Programs.kt | 46 +++--- .../client/gl/vertex/GLAttributeList.kt | 5 - .../client/gl/vertex/StreamVertexBuilder.kt | 9 +- .../client/render/ConfiguredShaderProgram.kt | 99 ------------- .../kstarbound/client/render/Font.kt | 10 +- .../kstarbound/client/render/Mesh.kt | 11 ++ .../client/render/MultiMeshBuilder.kt | 29 ++++ .../kstarbound/client/render/RenderConfig.kt | 13 ++ .../kstarbound/client/render/TileRenderer.kt | 135 ++++-------------- .../client/render/entity/EntityRenderer.kt | 7 +- .../dbotthepony/kstarbound/io/StarboundPak.kt | 1 - 19 files changed, 137 insertions(+), 391 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/render/MultiMeshBuilder.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderConfig.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt index 01a68ba2..1da24bd6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt @@ -1,21 +1,10 @@ package ru.dbotthepony.kstarbound.client -import it.unimi.dsi.fastutil.objects.ReferenceArraySet import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer -import ru.dbotthepony.kstarbound.client.render.LayeredRenderer -import ru.dbotthepony.kstarbound.client.render.TileLayerList -import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition -import ru.dbotthepony.kstarbound.world.* -import ru.dbotthepony.kstarbound.world.CHUNK_SIZEd -import ru.dbotthepony.kstarbound.world.api.ITileAccess +import ru.dbotthepony.kstarbound.world.Chunk +import ru.dbotthepony.kstarbound.world.ChunkPos import ru.dbotthepony.kstarbound.world.entities.Entity -import ru.dbotthepony.kvector.vector.Vector2d -import ru.dbotthepony.kvector.vector.Vector2f -import ru.dbotthepony.kvector.vector.Vector2i -import java.io.Closeable -import java.util.LinkedList /** * Псевдо zPos у фоновых тайлов @@ -70,6 +59,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk>() + val bakedMeshes = ArrayList, Int>>() var isDirty = true fun bake() { if (!isDirty) return isDirty = false - if (state.isSameThread()) { - for (mesh in bakedMeshes) { - mesh.first.close() - } - } - bakedMeshes.clear() - layers.clear() + val meshes = MultiMeshBuilder() for (x in 0 until renderRegionWidth) { for (y in 0 until renderRegionHeight) { @@ -77,30 +70,22 @@ class ClientWorld( val material = tile.material if (material != null) { - client.tileRenderers.getTileRenderer(material.materialName).tesselate(tile, view, layers, Vector2i(x, y), background = isBackground) + client.tileRenderers.getMaterialRenderer(material.materialName).tesselate(tile, view, meshes, Vector2i(x, y), background = isBackground) } val modifier = tile.modifier if (modifier != null) { - client.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, Vector2i(x, y), background = isBackground, isModifier = true) + client.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, meshes, Vector2i(x, y), background = isBackground, isModifier = true) } } } - if (layers.isNotEmpty) { - for (mesh in bakedMeshes) { - mesh.first.close() - } - - bakedMeshes.clear() - - for ((baked, builder, zLevel) in layers.layers()) { - bakedMeshes.add(ConfiguredStaticMesh(baked, builder) to zLevel) - } - - layers.clear() + for ((baked, builder, zLevel) in meshes.meshes()) { + bakedMeshes.add(ConfiguredMesh(baked, Mesh(state, builder)) to zLevel) } + + meshes.clear() } } @@ -150,7 +135,7 @@ class ClientWorld( for ((baked, zLevel) in background.bakedMeshes) { layers.add(zLevel + Z_LEVEL_BACKGROUND) { it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y) - baked.renderStacked(it) + baked.render(it.last()) it.pop() } } @@ -158,7 +143,7 @@ class ClientWorld( for ((baked, zLevel) in foreground.bakedMeshes) { layers.add(zLevel) { it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y) - baked.renderStacked(it) + baked.render(it.last()) it.pop() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/BlendFunc.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/BlendFunc.kt index 0b0552d5..246530e0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/BlendFunc.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/BlendFunc.kt @@ -16,9 +16,7 @@ data class BlendFunc( Func.ZERO ) - constructor( - source: Func, destination: Func - ) : this( + constructor(source: Func, destination: Func) : this( source, destination, source, 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 906a6f65..9546e827 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt @@ -13,7 +13,7 @@ import org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus import org.lwjgl.opengl.GL45.glNamedFramebufferTexture import org.lwjgl.opengl.GL46 -class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { +class GLFrameBuffer(val state: GLStateTracker) { init { state.ensureSameThread() } @@ -22,6 +22,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { init { checkForGLError("Creating framebuffer") + state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer) } val isComplete: Boolean get() { @@ -50,7 +51,6 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { } fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) { - texture?.close() texture = null attachTexture(width, height, format) } @@ -76,17 +76,4 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { state.readFramebuffer = null } } - - private val cleanable = state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer) - var isValid = true - private set - - override fun close() { - if (!isValid) - return - - cleanable.clean() - texture?.close() - isValid = false - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt index 374ad5cf..e3de1af3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt @@ -29,7 +29,7 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value: } @Suppress("SameParameterValue") -class GLTexture2D(val state: GLStateTracker, val name: String = "") : AutoCloseable { +class GLTexture2D(val state: GLStateTracker, val name: String = "") { init { state.ensureSameThread() } @@ -38,10 +38,9 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "") : A init { checkForGLError() + state.registerCleanable(this, ::glDeleteTextures, pointer) } - private val cleanable = state.registerCleanable(this, ::glDeleteTextures, pointer) - var width = 0 private set @@ -269,23 +268,6 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "") : A return this } - var isValid = true - private set - - override fun close() { - state.ensureSameThread() - - if (!isValid) - return - - if (state.texture2D == this) { - state.texture2D = null - } - - cleanable.clean() - isValid = false - } - companion object { private val LOGGER = LogManager.getLogger() } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt index 49bd84c2..5545454c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt @@ -1,29 +1,24 @@ package ru.dbotthepony.kstarbound.client.gl import org.lwjgl.opengl.GL46.* -import java.io.Closeable -class VertexArrayObject(val state: GLStateTracker) : Closeable { +class VertexArrayObject(val state: GLStateTracker) { val pointer = glGenVertexArrays() init { checkForGLError() + state.registerCleanable(this, ::glDeleteVertexArrays, pointer) } - private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, pointer) - fun bind(): VertexArrayObject { - check(isValid) { "Tried to use NULL GLVertexArrayObject" } return state.bind(this) } fun unbind(): VertexArrayObject { - check(isValid) { "Tried to use NULL GLVertexArrayObject" } return state.unbind(this) } fun attribute(position: Int, size: Int, type: Int, normalize: Boolean, stride: Int, offset: Long = 0L): VertexArrayObject { - check(isValid) { "Tried to use NULL GLVertexArrayObject" } state.ensureSameThread() glVertexAttribPointer(position, size, type, normalize, stride, offset) checkForGLError() @@ -31,27 +26,10 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable { } fun enableAttribute(position: Int): VertexArrayObject { - check(isValid) { "Tried to use NULL GLVertexArrayObject" } state.ensureSameThread() glEnableVertexArrayAttrib(pointer, position) //glEnableVertexAttribArray(position) checkForGLError() return this } - - var isValid = true - private set - - override fun close() { - state.ensureSameThread() - - if (!isValid) return - - if (state.VAO == this) { - state.VAO = null - } - - cleanable.clean() - isValid = false - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt index 0286b446..baa28bc5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.client.gl import org.lwjgl.opengl.GL46.* import org.lwjgl.system.MemoryUtil -import java.io.Closeable import java.nio.ByteBuffer enum class VBOType(val glType: Int) { @@ -10,32 +9,28 @@ enum class VBOType(val glType: Int) { ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER), } -class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.ARRAY) : Closeable { +class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.ARRAY) { val pointer = glGenBuffers() init { checkForGLError("Creating Vertex Buffer Object") + state.registerCleanable(this, ::glDeleteBuffers, pointer) } - private val cleanable = state.registerCleanable(this, ::glDeleteBuffers, pointer) - val isArray get() = type == VBOType.ARRAY val isElementArray get() = type == VBOType.ELEMENT_ARRAY fun bind(): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.bind(this) return this } fun unbind(): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.unbind(this) return this } fun bufferData(data: ByteBuffer, usage: Int): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() glNamedBufferData(pointer, data, usage) checkForGLError() @@ -43,7 +38,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. } fun bufferData(data: ByteBuffer, usage: Int, length: Long): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() if (length > data.remaining().toLong()) { @@ -57,7 +51,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. } fun bufferData(data: IntArray, usage: Int): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() glNamedBufferData(pointer, data, usage) checkForGLError() @@ -65,7 +58,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. } fun bufferData(data: FloatArray, usage: Int): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() glNamedBufferData(pointer, data, usage) checkForGLError() @@ -73,7 +65,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. } fun bufferData(data: DoubleArray, usage: Int): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() glNamedBufferData(pointer, data, usage) checkForGLError() @@ -81,27 +72,9 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. } fun bufferData(data: LongArray, usage: Int): VertexBufferObject { - check(isValid) { "Tried to use NULL GLVertexBufferObject" } state.ensureSameThread() glNamedBufferData(pointer, data, usage) checkForGLError() return this } - - var isValid = true - private set - - override fun close() { - state.ensureSameThread() - - if (!isValid) return - - if (state.VBO == this) { - state.VBO = null - } - - cleanable.clean() - - isValid = false - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShaderProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShaderProgram.kt index c917278e..07a97a06 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShaderProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShaderProgram.kt @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.checkForGLError +import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kvector.api.IStruct2f import ru.dbotthepony.kvector.api.IStruct3f import ru.dbotthepony.kvector.api.IStruct4f @@ -22,7 +23,11 @@ import kotlin.NoSuchElementException import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -open class GLShaderProgram(val state: GLStateTracker, shaders: Iterable) { +open class GLShaderProgram( + val state: GLStateTracker, + shaders: Iterable, + val attributes: GLAttributeList +) { init { state.ensureSameThread() } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt index e61dfc3d..8c0f448b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt @@ -22,7 +22,7 @@ private fun GLStateTracker.gshaders(name: String): List { ) } -class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("liquid")) { +class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("liquid"), FORMAT) { var baselineColor by F4Uniform("baselineColor") var transform by F4x4Uniform("transform") @@ -35,7 +35,7 @@ class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shad } } -class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light")) { +class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light"), FORMAT) { var baselineColor by F4Uniform("baselineColor") var transform by F4x4Uniform("transform") @@ -48,7 +48,7 @@ class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shade } } -class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) { +class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad"), FORMAT) { var color by F4Uniform("color") private val builder by lazy { @@ -94,7 +94,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.s } } -class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex")) { +class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex"), FORMAT) { var texture by IUniform("texture0") private val builder by lazy { @@ -118,7 +118,7 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state } } -class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur")) { +class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur"), FORMAT) { var texture by IUniform("texture0") private val builder by lazy { @@ -142,7 +142,7 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state } } -class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color")) { +class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color"), FORMAT) { var transform by F4x4Uniform("transform") val builder by lazy { @@ -154,7 +154,22 @@ class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.s } } -class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("tile")) { +class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("tile"), FORMAT) { + var transform by F4x4Uniform("transform") + var color by F4Uniform("color") + var texture by IUniform("texture0") + + init { + transform = Matrix4f.identity() + color = RGBAColor.WHITE + } + + companion object { + val FORMAT = GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build() + } +} + +class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("font"), GLAttributeList.VERTEX_2D_TEXTURE) { var transform by F4x4Uniform("transform") var color by F4Uniform("color") var texture by IUniform("texture0") @@ -165,18 +180,7 @@ class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader } } -class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("font")) { - var transform by F4x4Uniform("transform") - var color by F4Uniform("color") - var texture by IUniform("texture0") - - init { - transform = Matrix4f.identity() - color = RGBAColor.WHITE - } -} - -class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat")) { +class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat"), GLAttributeList.VEC2F) { var transform by F4x4Uniform("transform") var color by F4Uniform("color") @@ -185,7 +189,7 @@ class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader } } -class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture.glsl"))) { +class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) { var transform by F4x4Uniform("transform") var texture by IUniform("texture0") @@ -194,7 +198,7 @@ class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(s } } -class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture_color.glsl"))) { +class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture_color.glsl")), GLAttributeList.VERTEX_TEXTURE) { var transform by F4x4Uniform("transform") var texture by IUniform("texture0") var color by F4Uniform("color") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GLAttributeList.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GLAttributeList.kt index be9df708..7cee4cab 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GLAttributeList.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GLAttributeList.kt @@ -83,12 +83,7 @@ class GLAttributeList(builder: Builder) { companion object { val VEC2F = Builder().push(GLType.VEC2F).build() - val VEC3F = Builder().push(GLType.VEC3F).build() - val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build() - val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build() - - val TILE = Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt index 07b4de10..dc6d4dd1 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt @@ -3,7 +3,6 @@ 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 /** * Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка @@ -13,7 +12,7 @@ class StreamVertexBuilder( attributes: GLAttributeList, type: GeometryType, initialCapacity: Int = 64, -) : Closeable { +) { val builder = VertexBuilder(attributes, type, initialCapacity) private val vao = state.newVAO() private val vbo = state.newVBO() @@ -44,12 +43,6 @@ class StreamVertexBuilder( checkForGLError() } - override fun close() { - vao.close() - vbo.close() - ebo.close() - } - fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) { builder.begin() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt deleted file mode 100644 index 40aedd18..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt +++ /dev/null @@ -1,99 +0,0 @@ -package ru.dbotthepony.kstarbound.client.render - -import org.lwjgl.opengl.GL46.* -import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram -import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject -import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder -import ru.dbotthepony.kvector.arrays.Matrix4f -import ru.dbotthepony.kvector.arrays.Matrix4fStack - -/** - * Служит для быстрой настройки состояния для будущей отрисовки - * - * Установка текстурных блоков, программы, самих текстур и загрузка юниформ должна осуществляться тут - * - * Класс обязан быть наследован для осмысленных результатов - * - * Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения - * шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом) - */ -open class ConfiguredShaderProgram( - val program: T, -) { - private val transformLocation = program.getUniform("transform") as? GLShaderProgram.F4x4Uniform - - open fun setTransform(value: Matrix4f) { - transformLocation?.value = value - } - - /** - * Вызывается перед началом отрисовки - */ - open fun setup() { - program.use() - } -} - -/** - * Запечённый статичный меш, позволяет быстро отрисовать меш со всеми параметрами - * с заданной матрицей трансформации - */ -class ConfiguredStaticMesh( - val programState: ConfiguredShaderProgram<*>, - val indexCount: Int, - val vao: VertexArrayObject, - val elementIndexType: Int, -) : AutoCloseable { - private var onClose = {} - - constructor(programState: ConfiguredShaderProgram<*>, builder: VertexBuilder) : this( - programState, - builder.indexCount, - programState.program.state.newVAO(), - builder.indexType, - ) { - val vbo = programState.program.state.newVBO() - val ebo = programState.program.state.newEBO() - - onClose = { - vbo.close() - ebo.close() - } - - vao.bind() - vbo.bind() - ebo.bind() - - builder.upload(vbo, ebo, GL_STATIC_DRAW) - builder.attributes.apply(vao, true) - - vao.unbind() - vbo.unbind() - ebo.unbind() - } - - fun render(transform: Matrix4f? = null) { - check(isValid) { "$this is no longer valid" } - programState.setup() - - if (transform != null) { - programState.setTransform(transform) - } - - vao.bind() - glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L) - checkForGLError() - } - - fun renderStacked(transform: Matrix4fStack) = render(transform.last()) - - var isValid = true - private set - - override fun close() { - vao.close() - onClose.invoke() - isValid = false - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt index 09658aa2..0dc50967 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt @@ -244,7 +244,7 @@ class Font( return size(breakLines(text)) } - private inner class Glyph(val char: Char) : AutoCloseable { + private inner class Glyph(val char: Char) { private val texture: GLTexture2D? val isEmpty: Boolean @@ -349,13 +349,5 @@ class Font( stack.last().translateWithMultiplication(advanceX - bearingX, bearingY) } - - override fun close() { - vao?.close() - ebo?.close() - vbo?.close() - - texture?.close() - } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Mesh.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Mesh.kt index dfbb1b49..f72dd12e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Mesh.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Mesh.kt @@ -3,7 +3,9 @@ package ru.dbotthepony.kstarbound.client.render import org.lwjgl.opengl.GL46 import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.checkForGLError +import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder +import ru.dbotthepony.kvector.arrays.Matrix4f class Mesh(state: GLStateTracker) { constructor(state: GLStateTracker, builder: VertexBuilder) : this(state) { @@ -39,5 +41,14 @@ class Mesh(state: GLStateTracker) { vao.bind() GL46.glDrawElements(GL46.GL_TRIANGLES, indexCount, indexType, 0L) checkForGLError() + vao.unbind() + } +} + +data class ConfiguredMesh(val config: RenderConfig, val mesh: Mesh = Mesh(config.state)) { + fun render(transform: Matrix4f = config.state.matrixStack.last()) { + config.setup(transform) + mesh.render() + config.uninstall() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/MultiMeshBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/MultiMeshBuilder.kt new file mode 100644 index 00000000..95add3e0 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/MultiMeshBuilder.kt @@ -0,0 +1,29 @@ +package ru.dbotthepony.kstarbound.client.render + +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap +import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType +import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder +import java.util.stream.Stream + +class MultiMeshBuilder { + data class Entry(val config: RenderConfig<*>, val builder: VertexBuilder, val layer: Int) + + private val meshes = Reference2ObjectOpenHashMap, Int2ObjectOpenHashMap>() + + fun get(config: RenderConfig<*>, layer: Int): VertexBuilder { + return meshes.computeIfAbsent(config, Reference2ObjectFunction { + Int2ObjectOpenHashMap() + }).computeIfAbsent(layer, Int2ObjectFunction { Entry(config, VertexBuilder(config.program.attributes, GeometryType.QUADS), layer) }).builder + } + + fun clear() = meshes.clear() + fun isEmpty() = meshes.isEmpty() + fun isNotEmpty() = meshes.isNotEmpty() + + fun meshes(): Stream { + return meshes.values.stream().flatMap { it.values.stream() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderConfig.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderConfig.kt new file mode 100644 index 00000000..850fe7b2 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/RenderConfig.kt @@ -0,0 +1,13 @@ +package ru.dbotthepony.kstarbound.client.render + +import ru.dbotthepony.kstarbound.client.gl.GLStateTracker +import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram +import ru.dbotthepony.kvector.arrays.Matrix4f + +abstract class RenderConfig(val state: GLStateTracker, val program: T) { + open fun setup(transform: Matrix4f = state.matrixStack.last()) { + program.use() + } + + open fun uninstall() {} +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt index 80331f55..213a3c7a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt @@ -1,54 +1,21 @@ package ru.dbotthepony.kstarbound.client.render -import it.unimi.dsi.fastutil.ints.Int2ObjectFunction -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import org.apache.logging.log4j.LogManager import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.gl.* import ru.dbotthepony.kstarbound.client.gl.shader.GLTileProgram -import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.* import ru.dbotthepony.kstarbound.defs.tile.* import ru.dbotthepony.kstarbound.world.api.ITileAccess import ru.dbotthepony.kstarbound.world.api.ITileState import ru.dbotthepony.kstarbound.world.api.TileColor +import ru.dbotthepony.kvector.arrays.Matrix4f import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.Vector2i -import java.util.stream.Stream import kotlin.collections.HashMap -data class TileLayer( - val program: ConfiguredShaderProgram, - val vertices: VertexBuilder, - val zPos: Int -) - -class TileLayerList { - private val layers = HashMap, Int2ObjectOpenHashMap>() - - /** - * Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel]. - * - * Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute]. - */ - fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder { - return layers.computeIfAbsent(program) { Int2ObjectOpenHashMap() }.computeIfAbsent(zLevel, Int2ObjectFunction { - return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel) - }).vertices - } - - fun layers(): Stream { - return layers.values.stream().flatMap { it.values.stream() } - } - - fun clear() = layers.clear() - - val isEmpty get() = layers.isEmpty() - val isNotEmpty get() = layers.isNotEmpty() -} - /** * Хранит в себе программы для отрисовки определённых [TileDefinition] * @@ -56,28 +23,29 @@ class TileLayerList { */ class TileRenderers(val client: StarboundClient) { val state get() = client.gl - private val foregroundTilePrograms = HashMap() - private val backgroundTilePrograms = HashMap() - private val tileRenderersCache = HashMap() - private val modifierRenderersCache = HashMap() - fun getTileRenderer(defName: String): TileRenderer { - return tileRenderersCache.computeIfAbsent(defName) { + private val foreground = HashMap() + private val background = HashMap() + private val matCache = HashMap() + private val modCache = HashMap() + + fun getMaterialRenderer(defName: String): TileRenderer { + return matCache.computeIfAbsent(defName) { val def = client.starbound.tiles[defName] // TODO: Пустой рендерер return@computeIfAbsent TileRenderer(this, def!!.value) } } fun getModifierRenderer(defName: String): TileRenderer { - return modifierRenderersCache.computeIfAbsent(defName) { + return modCache.computeIfAbsent(defName) { val def = client.starbound.tileModifiers[defName] // TODO: Пустой рендерер return@computeIfAbsent TileRenderer(this, def!!.value) } } - private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.programs.tile) { - override fun setup() { - super.setup() + private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig(state, state.programs.tile) { + override fun setup(transform: Matrix4f) { + super.setup(transform) state.activeTexture = 0 state.depthTest = false program.texture = 0 @@ -85,68 +53,19 @@ class TileRenderers(val client: StarboundClient) { texture.textureMagFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST - program.color = FOREGROUND_COLOR + program.transform = transform + program.color = color } - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - if (other is ForegroundTileProgram) { - return texture == other.texture - } - - return super.equals(other) - } - - override fun hashCode(): Int { - return texture.hashCode() - } + override fun uninstall() {} } - private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.programs.tile) { - override fun setup() { - super.setup() - state.activeTexture = 0 - state.depthTest = false - program.texture = 0 - texture.bind() - texture.textureMagFilter = GL_NEAREST - texture.textureMinFilter = GL_NEAREST - - program.color = BACKGROUND_COLOR - } - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - if (other is BackgroundTileProgram) { - return texture == other.texture - } - - return super.equals(other) - } - - override fun hashCode(): Int { - return texture.hashCode() - } + fun foreground(texture: GLTexture2D): RenderConfig { + return foreground.computeIfAbsent(texture) { Config(it, FOREGROUND_COLOR) } } - /** - * Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой - */ - fun foreground(texture: GLTexture2D): ConfiguredShaderProgram { - return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram) - } - - /** - * Возвращает запечённое состояние шейдера shaderVertexTextureRGBAColor с данной текстурой - */ - fun background(texture: GLTexture2D): ConfiguredShaderProgram { - return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram) + fun background(texture: GLTexture2D): RenderConfig { + return background.computeIfAbsent(texture) { Config(it, BACKGROUND_COLOR) } } companion object { @@ -155,8 +74,6 @@ class TileRenderers(val client: StarboundClient) { } } -private fun vertexTextureBuilder() = VertexBuilder(GLAttributeList.TILE, GeometryType.QUADS) - private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean { return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift @@ -236,7 +153,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { self: ITileState, matchPiece: RenderMatch, getter: ITileAccess, - layers: TileLayerList, + meshBuilder: MultiMeshBuilder, pos: Vector2i, thisBuilder: VertexBuilder, background: Boolean, @@ -251,14 +168,14 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { renderers.foreground(state.loadTexture(renderPiece.piece.texture!!)) } - tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier) + tesselateAt(self, renderPiece.piece, getter, meshBuilder.get(program, def.renderParameters.zLevel), pos, renderPiece.offset, isModifier) } else { tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier) } } for (subPiece in matchPiece.subMatches) { - val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier) + val matched = tesselatePiece(self, subPiece, getter, meshBuilder, pos, thisBuilder, background, isModifier) if (matched == TestResult.HALT || matched == TestResult.CONTINUE && matchPiece.haltOnSubMatch) { return TestResult.HALT @@ -280,20 +197,20 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { * * [getter] Нужен для получения информации о ближайших блоках * - * [layers] содержит текущие программы и их билдеры и их zPos + * [meshBuilder] содержит текущие программы и их билдеры и их zPos * * Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf] */ - fun tesselate(self: ITileState, getter: ITileAccess, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) { + fun tesselate(self: ITileState, getter: ITileAccess, meshBuilder: MultiMeshBuilder, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) { // если у нас нет renderTemplate // то мы просто не можем его отрисовать val template = def.renderTemplate.value ?: return - val vertexBuilder = layers.computeIfAbsent(if (background) bakedBackgroundProgramState else bakedProgramState, def.renderParameters.zLevel, ::vertexTextureBuilder) + val vertexBuilder = meshBuilder.get(if (background) bakedBackgroundProgramState else bakedProgramState, def.renderParameters.zLevel) for ((_, matcher) in template.matches) { for (matchPiece in matcher) { - val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier) + val matched = tesselatePiece(self, matchPiece, getter, meshBuilder, pos, vertexBuilder, background, isModifier) if (matched == TestResult.HALT) { break diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/EntityRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/EntityRenderer.kt index 601a0066..77ff6e0a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/EntityRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/EntityRenderer.kt @@ -7,14 +7,13 @@ import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kvector.arrays.Matrix4fStack import ru.dbotthepony.kvector.vector.Vector2d -import java.io.Closeable /** * Базовый класс, отвечающий за отрисовку определённого ентити в мире * * Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход) */ -open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) : Closeable { +open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) { inline val state: GLStateTracker get() = client.gl open val renderPos: Vector2d get() = entity.position @@ -30,10 +29,6 @@ open class EntityRenderer(val client: StarboundClient, val entity: Entity, open open val layer: Int get() = Z_LEVEL_ENTITIES - override fun close() { - - } - companion object { /** * Pseudo Z position for entities, for them to appear behind tile geometry, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/StarboundPak.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/StarboundPak.kt index 344abb3d..6a0e64b2 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/io/StarboundPak.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/StarboundPak.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.io import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import ru.dbotthepony.kstarbound.api.IStarboundFile import ru.dbotthepony.kstarbound.io.json.BinaryJsonReader import java.io.BufferedInputStream