From b40f3e8dcadefd3577967bc8449a4437ba86963d Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 21 Feb 2023 07:43:28 +0700 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0=20=D1=88=D0=B5=D0=B9=D0=B4=D0=B5=D1=80=D0=BE=D0=B2,=20?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BD=D0=B0=D0=B4=D0=BE=20=D0=B5=D1=89=D1=91=20?= =?UTF-8?q?=D0=B8=D0=B7=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=81=D1=8F=20=D0=BE?= =?UTF-8?q?=D1=82=20=D0=BC=D1=83=D1=81=D0=BE=D1=80=D0=B0=20=D0=B2=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B4=D0=B5=20configuredshaderprogram?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kstarbound/client/ClientChunk.kt | 16 +- .../kstarbound/client/gl/BlendFunc.kt | 75 ++++++ .../kstarbound/client/gl/ErrorCheck.kt | 32 +-- .../kstarbound/client/gl/GLFrameBuffer.kt | 6 +- .../kstarbound/client/gl/GLStateTracker.kt | 224 ++++++++---------- .../kstarbound/client/gl/GLTexture.kt | 4 +- .../kstarbound/client/gl/VertexArrayObject.kt | 5 +- .../client/gl/VertexBufferObject.kt | 6 +- .../client/gl/program/GLInternalProgram.kt | 37 --- .../kstarbound/client/gl/shader/GLShader.kt | 61 ----- .../client/gl/shader/GLShaderProgram.kt | 221 ++++++++++++++--- .../shader/GLTransformableColorableProgram.kt | 8 - .../gl/shader/GLTransformableProgram.kt | 8 - .../client/gl/shader/GLUniformLocation.kt | 78 ------ .../client/gl/{program => shader}/Programs.kt | 134 +++++------ .../kstarbound/client/render/Box2DRenderer.kt | 8 +- .../client/render/ConfiguredShaderProgram.kt | 17 +- .../kstarbound/client/render/Font.kt | 4 +- .../client/render/GPULightRenderer.kt | 24 +- .../kstarbound/client/render/TileRenderer.kt | 23 +- .../client/render/entity/ItemRenderer.kt | 4 +- 21 files changed, 498 insertions(+), 497 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/BlendFunc.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/GLInternalProgram.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShader.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt rename src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/{program => shader}/Programs.kt (52%) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt index 6cc123ba..3b757df0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt @@ -3,8 +3,8 @@ package ru.dbotthepony.kstarbound.client 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.program.GLHardLightGeometryProgram -import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram +import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram +import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh @@ -301,10 +301,10 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk {} - GL_INVALID_ENUM -> throw OpenGLInvalidEnumException() - GL_INVALID_VALUE -> throw OpenGLInvalidValueException() - GL_INVALID_OPERATION -> throw OpenGLInvalidOperationException() - GL_STACK_OVERFLOW -> throw OpenGLStackOverflowException() - GL_STACK_UNDERFLOW -> throw OpenGLStackUnderflowException() - GL_OUT_OF_MEMORY -> throw OpenGLOutOfMemoryException() - else -> throw OpenGLUnknownError(errorCode) + GL_INVALID_ENUM -> throw OpenGLInvalidEnumException(message) + GL_INVALID_VALUE -> throw OpenGLInvalidValueException(message) + GL_INVALID_OPERATION -> throw OpenGLInvalidOperationException(message) + GL_STACK_OVERFLOW -> throw OpenGLStackOverflowException(message) + GL_STACK_UNDERFLOW -> throw OpenGLStackUnderflowException(message) + GL_OUT_OF_MEMORY -> throw OpenGLOutOfMemoryException(message) + else -> throw OpenGLUnknownError(errorCode, message) } } 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 3c37d2f3..906a6f65 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLFrameBuffer.kt @@ -21,7 +21,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { val pointer = GL46.glGenFramebuffers() init { - checkForGLError() + checkForGLError("Creating framebuffer") } val isComplete: Boolean get() { @@ -77,7 +77,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { } } - private val cleanable = state.registerCleanable(this, GL46::glDeleteFramebuffers, "Framebuffer", pointer) + private val cleanable = state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer) var isValid = true private set @@ -85,7 +85,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable { if (!isValid) return - cleanable.cleanManual() + cleanable.clean() 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 50cf2ad6..f2e1b9c8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLStateTracker.kt @@ -3,14 +3,14 @@ package ru.dbotthepony.kstarbound.client.gl import org.apache.logging.log4j.LogManager import org.lwjgl.opengl.GL import org.lwjgl.opengl.GL46.* +import org.lwjgl.opengl.GLCapabilities import ru.dbotthepony.kstarbound.api.ISBFileLocator import ru.dbotthepony.kstarbound.client.freetype.FreeType import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException -import ru.dbotthepony.kstarbound.client.gl.program.GLPrograms -import ru.dbotthepony.kstarbound.client.gl.shader.GLShader +import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram -import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgram import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableProgram +import ru.dbotthepony.kstarbound.client.gl.shader.ShaderCompilationException import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType @@ -21,6 +21,7 @@ import ru.dbotthepony.kvector.api.IStruct4f import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.vector.Color +import java.io.File import java.io.FileNotFoundException import java.lang.ref.Cleaner import java.util.concurrent.ThreadFactory @@ -114,89 +115,6 @@ private class TexturesTracker(maxValue: Int) : ReadWriteProperty Unit>() + // This line is critical for LWJGL's interoperation with GLFW's + // OpenGL context, or any context that is managed externally. + // LWJGL detects the context that is current in the current thread, + // creates the GLCapabilities instance and makes the OpenGL + // bindings available for use. + val capabilities: GLCapabilities = GL.createCapabilities() + + private val cleanerBacklog = ArrayList<() -> Unit>() + + var objectsCleaned = 0 + private set private val cleaner = Cleaner.create(object : ThreadFactory { override fun newThread(r: Runnable): Thread { @@ -226,19 +149,16 @@ class GLStateTracker(val locator: ISBFileLocator) { } }) - fun registerCleanable(ref: Any, fn: (Int) -> Unit, name: String, nativeRef: Int): GLCleanable { - var cleanManual = false - + fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable { val cleanable = cleaner.register(ref) { - if (!cleanManual && LOGGER.isTraceEnabled) - LOGGER.trace("{} with ID {} was GC'd by JVM, without manually calling close()", name, nativeRef) + objectsCleaned++ if (isSameThread()) { fn(nativeRef) checkForGLError() } else { - synchronized(cleanerHits) { - cleanerHits.add { + synchronized(cleanerBacklog) { + cleanerBacklog.add { fn(nativeRef) checkForGLError() } @@ -246,23 +166,16 @@ class GLStateTracker(val locator: ISBFileLocator) { } } - return object : GLCleanable { - override fun cleanManual() { - cleanManual = true - clean() - } - - override fun clean() = cleanable.clean() - } + return cleanable } fun cleanup() { - synchronized(cleanerHits) { - for (lambda in cleanerHits) { + synchronized(cleanerBacklog) { + for (lambda in cleanerBacklog) { lambda.invoke() } - cleanerHits.clear() + cleanerBacklog.clear() } } @@ -427,10 +340,6 @@ class GLStateTracker(val locator: ISBFileLocator) { fun isSameThread() = thread === Thread.currentThread() - fun program(vararg shaders: GLShader): GLShaderProgram { - return GLShaderProgram(this, *shaders) - } - fun newVBO(type: VBOType = VBOType.ARRAY): VertexBufferObject { return VertexBufferObject(this, type) } @@ -542,20 +451,25 @@ class GLStateTracker(val locator: ISBFileLocator) { return obj } - val shaderVertexTexture: GLTransformableProgram - val shaderVertexTextureColor: GLTransformableColorableProgram + val shaderVertexTexture: TexturedProgram + val shaderVertexTextureColor: TexturedColoredProgram + + inner class TexturedProgram(shaders: Iterable) : GLTransformableProgram(this@GLStateTracker, shaders) { + val texture = IUniform("_texture") + } + + inner class TexturedColoredProgram(shaders: Iterable) : GLTransformableProgram(this@GLStateTracker, shaders) { + val texture = IUniform("_texture") + val color = F4Uniform("_color") + } init { - val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl") - val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl") - val textureV = GLShader.internalVertex("shaders/vertex/texture.glsl") + val textureF = internalFragment("shaders/fragment/texture.glsl") + val textureColorF = internalFragment("shaders/fragment/texture_color.glsl") + val textureV = internalVertex("shaders/vertex/texture.glsl") - shaderVertexTexture = GLTransformableProgram(this, textureF, textureV) - shaderVertexTextureColor = GLTransformableColorableProgram(this, textureColorF, textureV) - - textureF.unlink() - textureColorF.unlink() - textureV.unlink() + shaderVertexTexture = TexturedProgram(listOf(textureF, textureV)) + shaderVertexTextureColor = TexturedColoredProgram(listOf(textureColorF, textureV)) } val programs = GLPrograms(this) @@ -578,8 +492,8 @@ class GLStateTracker(val locator: ISBFileLocator) { builder.upload() programs.flat.use() - programs.flat.color.set(color) - programs.flat.transform.set(matrixStack.last) + programs.flat.color.value = color + programs.flat.transform.value = matrixStack.last builder.draw(GL_LINES) } @@ -592,7 +506,7 @@ class GLStateTracker(val locator: ISBFileLocator) { builder.upload() programs.flatColor.use() - programs.flatColor.transform.set(matrixStack.last) + programs.flatColor.transform.value = matrixStack.last builder.draw(GL_TRIANGLES) } @@ -606,8 +520,60 @@ class GLStateTracker(val locator: ISBFileLocator) { val box2dRenderer = Box2DRenderer(this) + inner class Shader(body: String, type: Int) { + constructor(body: File, type: Int) : this(body.also { require(it.exists()) { "Shader file does not exists: $body" } }.readText(), type) + + init { + ensureSameThread() + } + + val pointer = glCreateShader(type) + + init { + checkForGLError() + registerCleanable(this, ::glDeleteShader, pointer) + } + + init { + if (body == "") { + throw IllegalArgumentException("Shader source is empty") + } + + glShaderSource(pointer, body) + glCompileShader(pointer) + + val result = intArrayOf(0) + glGetShaderiv(pointer, GL_COMPILE_STATUS, result) + + if (result[0] == 0) { + throw ShaderCompilationException(glGetShaderInfoLog(pointer)) + } + + checkForGLError() + } + } + + fun vertex(file: File) = Shader(file, GL_VERTEX_SHADER) + fun fragment(file: File) = Shader(file, GL_FRAGMENT_SHADER) + + fun vertex(contents: String) = Shader(contents, GL_VERTEX_SHADER) + fun fragment(contents: String) = Shader(contents, GL_FRAGMENT_SHADER) + + fun internalVertex(file: String) = Shader(readInternal(file), GL_VERTEX_SHADER) + fun internalFragment(file: String) = Shader(readInternal(file), GL_FRAGMENT_SHADER) + fun internalGeometry(file: String) = Shader(readInternal(file), GL_GEOMETRY_SHADER) + companion object { private val LOGGER = LogManager.getLogger(GLStateTracker::class.java) private val TRACKERS = ThreadLocal() + + private fun readInternal(file: String): String { + return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader() + .let { + val read = it.readText() + it.close() + read + } + } } } 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 c946553a..feeba689 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/GLTexture.kt @@ -35,7 +35,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "") : A checkForGLError() } - private val cleanable = state.registerCleanable(this, ::glDeleteTextures, "2D Texture", pointer) + private val cleanable = state.registerCleanable(this, ::glDeleteTextures, pointer) var width = 0 private set @@ -273,7 +273,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "") : A state.texture2D = null } - cleanable.cleanManual() + cleanable.clean() isValid = false } 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 4bb0f9ce..49bd84c2 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexArrayObject.kt @@ -10,7 +10,7 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable { checkForGLError() } - private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, "Vertex Array Object", pointer) + private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, pointer) fun bind(): VertexArrayObject { check(isValid) { "Tried to use NULL GLVertexArrayObject" } @@ -51,8 +51,7 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable { state.VAO = null } - cleanable.cleanManual() - + 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 20afdb19..0286b446 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/VertexBufferObject.kt @@ -14,10 +14,10 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. val pointer = glGenBuffers() init { - checkForGLError() + checkForGLError("Creating Vertex Buffer Object") } - private val cleanable = state.registerCleanable(this, ::glDeleteBuffers, "Vertex Buffer Object", pointer) + private val cleanable = state.registerCleanable(this, ::glDeleteBuffers, pointer) val isArray get() = type == VBOType.ARRAY val isElementArray get() = type == VBOType.ELEMENT_ARRAY @@ -100,7 +100,7 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType. state.VBO = null } - cleanable.cleanManual() + cleanable.clean() isValid = false } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/GLInternalProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/GLInternalProgram.kt deleted file mode 100644 index 8a986a5e..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/GLInternalProgram.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ru.dbotthepony.kstarbound.client.gl.program - -import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.gl.shader.GLShader -import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram - -private fun loadShaders( - vertex: Collection, - fragment: Collection, - geometry: Collection -): Array { - val result = arrayOfNulls(vertex.size + fragment.size + geometry.size) - var i = 0 - - for (name in vertex) { - result[i++] = GLShader.internalVertex("shaders/$name.vsh") - } - - for (name in fragment) { - result[i++] = GLShader.internalFragment("shaders/$name.fsh") - } - - for (name in geometry) { - result[i++] = GLShader.internalGeometry("shaders/$name.gsh") - } - - return result as Array -} - -open class GLInternalProgram( - state: GLStateTracker, - vertex: Collection, - fragment: Collection, - geometry: Collection = listOf() -) : GLShaderProgram(state, *loadShaders(vertex, fragment, geometry)) { - constructor(state: GLStateTracker, name: String) : this(state, listOf(name), listOf(name)) -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShader.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShader.kt deleted file mode 100644 index 8f975d6a..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLShader.kt +++ /dev/null @@ -1,61 +0,0 @@ -package ru.dbotthepony.kstarbound.client.gl.shader - -import org.lwjgl.opengl.GL46.* -import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import java.io.File - -class GLShader( - body: String, - type: Int -) { - constructor(body: File, type: Int) : this(body.also { require(it.exists()) { "Shader file does not exists: $body" } }.readText(), type) - - val pointer = glCreateShader(type) - var unlinked = false - private set - - init { - if (body == "") { - throw IllegalArgumentException("Shader source is empty") - } - - glShaderSource(pointer, body) - glCompileShader(pointer) - - val result = intArrayOf(0) - glGetShaderiv(pointer, GL_COMPILE_STATUS, result) - - if (result[0] == 0) { - throw ShaderCompilationException(glGetShaderInfoLog(pointer)) - } - - checkForGLError() - } - - fun unlink(): Boolean { - if (unlinked) - return false - - glDeleteShader(pointer) - checkForGLError() - return true - } - - companion object { - fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER) - fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER) - - private fun readInternal(file: String): String { - return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader() - .let { - val read = it.readText() - it.close() - return@let read - } - } - - fun internalVertex(file: String) = GLShader(readInternal(file), GL_VERTEX_SHADER) - fun internalFragment(file: String) = GLShader(readInternal(file), GL_FRAGMENT_SHADER) - fun internalGeometry(file: String) = GLShader(readInternal(file), GL_GEOMETRY_SHADER) - } -} 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 b3419ea3..b67b259e 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 @@ -1,24 +1,35 @@ package ru.dbotthepony.kstarbound.client.gl.shader +import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap +import it.unimi.dsi.fastutil.objects.Object2BooleanFunction +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ObjectArraySet 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.GLShader -import ru.dbotthepony.kstarbound.client.gl.shader.GLUniformLocation -import ru.dbotthepony.kstarbound.client.gl.shader.ShaderLinkException import ru.dbotthepony.kvector.api.IFloatMatrix +import ru.dbotthepony.kvector.api.IStruct2f import ru.dbotthepony.kvector.api.IStruct3f import ru.dbotthepony.kvector.api.IStruct4f +import ru.dbotthepony.kvector.api.concrete.IMatrix3f +import ru.dbotthepony.kvector.api.concrete.IMatrix4f +import ru.dbotthepony.kvector.matrix.nfloat.Matrix3f +import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f +import ru.dbotthepony.kvector.vector.nfloat.Vector2f +import ru.dbotthepony.kvector.vector.nfloat.Vector3f +import ru.dbotthepony.kvector.vector.nfloat.Vector4f +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.FloatBuffer import java.util.* import java.util.stream.Stream -import kotlin.collections.HashSet - -open class GLShaderProgram(val state: GLStateTracker, shaders: Stream) { - constructor(state: GLStateTracker, shaders: Collection) : this(state, shaders.stream()) - constructor(state: GLStateTracker, vararg shaders: GLShader) : this(state, Arrays.stream(shaders)) +import kotlin.NoSuchElementException +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +open class GLShaderProgram(val state: GLStateTracker, shaders: Iterable) { init { state.ensureSameThread() } @@ -26,10 +37,12 @@ open class GLShaderProgram(val state: GLStateTracker, shaders: Stream) val pointer = glCreateProgram() init { - checkForGLError() + checkForGLError("Creating shader program") + state.registerCleanable(this, ::glDeleteProgram, pointer) for (shader in shaders) { glAttachShader(pointer, shader.pointer) + checkForGLError("Attaching shader") } glLinkProgram(pointer) @@ -44,33 +57,175 @@ open class GLShaderProgram(val state: GLStateTracker, shaders: Stream) glGetError() } - private val locationCache = Object2ObjectOpenHashMap>() + fun use() = state.use(this) - /** - * Возвращает GLUniformLocation или null, если у данной программы нет такого uniform - * - * В т.ч. если она была в коде шейдера, но нигде не использовалась (отсечена компилятором) - * - * Результат поиска кешируется, но для повышения производительности вызывающий код желательно так же - * должен кешировать результат (как локальное свойство или переменная, в случае многократного использования) - */ - operator fun get(name: String): GLUniformLocation? { + private val locations = Object2ObjectArrayMap>() + private val uniformsExist = Object2BooleanArrayMap() + + fun isUniformPresent(name: String): Boolean { state.ensureSameThread() - - return locationCache.computeIfAbsent(name, Object2ObjectFunction { - val location = glGetUniformLocation(pointer, name) - - if (location == -1) - return@Object2ObjectFunction Optional.empty() - - return@Object2ObjectFunction Optional.of(GLUniformLocation(this, name, location)) - }).orElse(null) + return uniformsExist.computeIfAbsent(name, Object2BooleanFunction { glGetUniformLocation(pointer, name) != -1 }) } - operator fun set(name: String, value: IStruct4f) = this[name]?.set(value) - operator fun set(name: String, value: IStruct3f) = this[name]?.set(value) - operator fun set(name: String, value: Int) = this[name]?.set(value) - operator fun set(name: String, value: IFloatMatrix<*>) = this[name]?.set(value) + fun getUniform(name: String, orElse: ((String) -> Uniform<*>)? = null): Uniform<*>? { + return locations[name] ?: orElse?.invoke(name) + } - fun use() = state.use(this) + fun int(name: String): IUniform { + return locations.computeIfAbsent(name, Object2ObjectFunction { IUniform(name) }) as? IUniform ?: throw IllegalStateException("Uniform $name has type of ${locations[name]!!::class.simpleName}") + } + + abstract inner class Uniform(val name: String) : ReadWriteProperty { + init { + state.ensureSameThread() + require(!locations.containsKey(name)) { "Already has uniform $name for ${this@GLShaderProgram} (${locations[name]})" } + } + + val location = glGetUniformLocation(pointer, name) + + init { + if (location == -1) + throw NoSuchElementException("Program ${this@GLShaderProgram} does not have uniform with name $name") + + locations[name] = this + } + + abstract var value: V + + override fun toString(): String { + return "Uniform[$name, $value]" + } + + // они переопределены в дочерних классах для бетонной реализации примитивов + override fun getValue(thisRef: Any?, property: KProperty<*>): V { + return value + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) { + this.value = value + } + } + + inner class FUniform(name: String) : Uniform(name) { + override var value: Float = 0f + set(value) { + state.ensureSameThread() + + if (field != value) { + glProgramUniform1f(pointer, location, value) + checkForGLError() + } + + field = value + } + + override fun getValue(thisRef: Any?, property: KProperty<*>): Float { + return value + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Float) { + this.value = value + } + } + + inner class F2Uniform(name: String) : Uniform(name) { + override var value: IStruct2f = Vector2f.ZERO + set(value) { + state.ensureSameThread() + + if (field != value) { + glProgramUniform2f(pointer, location, value.component1(), value.component2()) + checkForGLError() + + field = if (value is Vector2f) value else Vector2f(value) + } + } + } + + inner class F3Uniform(name: String) : Uniform(name) { + override var value: IStruct3f = Vector3f.ZERO + set(value) { + state.ensureSameThread() + + if (field != value) { + glProgramUniform3f(pointer, location, value.component1(), value.component2(), value.component3()) + checkForGLError() + + field = if (value is Vector3f) value else Vector3f(value) + } + } + } + + private val buff3x3: FloatBuffer by lazy(LazyThreadSafetyMode.NONE) { ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() } + private val buff4x4: FloatBuffer by lazy(LazyThreadSafetyMode.NONE) { ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() } + + inner class F3x3Uniform(name: String) : Uniform>(name) { + override var value: IMatrix3f<*> = Matrix3f.ZERO + set(value) { + state.ensureSameThread() + + if (field != value) { + buff3x3.position(0) + value.write(buff3x3) + buff3x3.position(0) + glProgramUniformMatrix3fv(pointer, location, false, buff3x3) + checkForGLError() + + field = value.toMatrix3f() + } + } + } + + inner class F4x4Uniform(name: String) : Uniform>(name) { + override var value: IMatrix4f<*> = Matrix4f.ZERO + set(value) { + state.ensureSameThread() + + if (field != value) { + buff4x4.position(0) + value.write(buff4x4) + buff4x4.position(0) + glProgramUniformMatrix4fv(pointer, location, false, buff4x4) + checkForGLError() + + field = value.toMatrix4f() + } + } + } + + inner class F4Uniform(name: String) : Uniform(name) { + override var value: IStruct4f = Vector4f.ZERO + set(value) { + state.ensureSameThread() + + if (field != value) { + glProgramUniform4f(pointer, location, value.component1(), value.component2(), value.component3(), value.component4()) + checkForGLError() + + field = if (value is Vector4f) value else Vector4f(value) + } + } + } + + inner class IUniform(name: String) : Uniform(name) { + override var value: Int = 0 + set(value) { + state.ensureSameThread() + + if (field != value) { + glProgramUniform1i(pointer, location, value) + checkForGLError() + } + + field = value + } + + override fun getValue(thisRef: Any?, property: KProperty<*>): Int { + return value + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { + this.value = value + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableColorableProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableColorableProgram.kt index 16725abe..cfa26bf3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableColorableProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableColorableProgram.kt @@ -2,11 +2,3 @@ package ru.dbotthepony.kstarbound.client.gl.shader import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kvector.vector.Color - -open class GLTransformableColorableProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableProgram(state, *shaders) { - val color = this["_color"]!! - - init { - color.set(Color.WHITE) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableProgram.kt index 58638b5a..bd996a92 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLTransformableProgram.kt @@ -2,11 +2,3 @@ package ru.dbotthepony.kstarbound.client.gl.shader import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f - -open class GLTransformableProgram(state: GLStateTracker, vararg shaders: GLShader) : GLShaderProgram(state, *shaders) { - val transform = this["_transform"]!! - - init { - transform.set(Matrix4f.IDENTITY) - } -} 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 deleted file mode 100644 index 0d650aac..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/GLUniformLocation.kt +++ /dev/null @@ -1,78 +0,0 @@ -package ru.dbotthepony.kstarbound.client.gl.shader - -import org.lwjgl.opengl.GL41.* -import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import ru.dbotthepony.kvector.api.IFloatMatrix -import ru.dbotthepony.kvector.api.IStruct2f -import ru.dbotthepony.kvector.api.IStruct3f -import ru.dbotthepony.kvector.api.IStruct4f -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.FloatBuffer - -class GLUniformLocation(val program: GLShaderProgram, val name: String, val pointer: Int) { - fun set(value: IStruct4f): GLUniformLocation { - program.state.ensureSameThread() - val (v0, v1, v2, v3) = value - glProgramUniform4f(program.pointer, pointer, v0, v1, v2, v3) - checkForGLError() - return this - } - - fun set(value: IStruct2f): GLUniformLocation { - program.state.ensureSameThread() - val (v0, v1) = value - glProgramUniform2f(program.pointer, pointer, v0, v1) - checkForGLError() - return this - } - - fun set(value: IStruct3f): GLUniformLocation { - program.state.ensureSameThread() - val (v0, v1, v2) = value - glProgramUniform3f(program.pointer, pointer, v0, v1, v2) - checkForGLError() - return this - } - - fun set(value: Int): GLUniformLocation { - program.state.ensureSameThread() - glProgramUniform1i(program.pointer, pointer, value) - checkForGLError() - 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() } - - fun set(value: IFloatMatrix<*>): GLUniformLocation { - program.state.ensureSameThread() - - if (value.rows == 3 && value.columns == 3) { - // Матрица 3x3 - buff3x3.position(0) - value.write(buff3x3) - buff3x3.position(0) - glProgramUniformMatrix3fv(program.pointer, pointer, false, buff3x3) - checkForGLError() - } else if (value.rows == 4 && value.columns == 4) { - // Матрица 4x4 - buff4x4.position(0) - value.write(buff4x4) - buff4x4.position(0) - glProgramUniformMatrix4fv(program.pointer, pointer, false, buff4x4) - checkForGLError() - } else { - throw IllegalArgumentException("Can not use matrix with these dimensions: ${value.columns}x${value.rows}") - } - - return this - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt similarity index 52% rename from src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt rename to src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt index 4c9e1bcd..a454b081 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/shader/Programs.kt @@ -1,49 +1,47 @@ -package ru.dbotthepony.kstarbound.client.gl.program +package ru.dbotthepony.kstarbound.client.gl.shader import ru.dbotthepony.kstarbound.client.gl.BlendFunc import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLType -import ru.dbotthepony.kstarbound.client.gl.shader.GLShader -import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram -import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgram import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.render.GPULightRenderer +import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f import ru.dbotthepony.kvector.vector.Color -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty -private class SimpleProgram(private val name: String, private val factory: (GLStateTracker, Array) -> T) : ReadOnlyProperty { - private var value: T? = null +open class GLTransformableProgram(state: GLStateTracker, shaders: Iterable) : GLShaderProgram(state, shaders) { + val transform = F4x4Uniform("_transform") - override fun getValue(thisRef: GLPrograms, property: KProperty<*>): T { - thisRef.state.ensureSameThread() - - val value = value - - if (value != null) { - return value - } - - val vertex = GLShader.internalVertex("shaders/$name.vsh") - val fragment = GLShader.internalFragment("shaders/$name.fsh") - - val newValue = factory.invoke(thisRef.state, arrayOf(vertex, fragment)) - - this.value = newValue - - vertex.unlink() - fragment.unlink() - - return newValue + init { + transform.value = Matrix4f.IDENTITY } } -class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid") { - val baselineColor = this["baselineColor"]!! - val transform = this["transform"]!! +open class GLTransformableColorableProgram(state: GLStateTracker, shaders: Iterable) : GLTransformableProgram(state, shaders) { + val color = F4Uniform("_color") + + init { + color.value = Color.WHITE + } +} + +private fun GLStateTracker.shaders(name: String): List { + return listOf(internalVertex("shaders/$name.vsh"), internalFragment("shaders/$name.fsh")) +} + +private fun GLStateTracker.gshaders(name: String): List { + return listOf( + internalVertex(name), + internalFragment(name), + internalGeometry(name) + ) +} + +class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("liquid")) { + val baselineColor = F4Uniform("baselineColor") + val transform = F4x4Uniform("transform") val builder by lazy { StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384) @@ -54,9 +52,9 @@ class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid" } } -class GLLightProgram(state: GLStateTracker) : GLInternalProgram(state, "light") { - val baselineColor = this["baselineColor"]!! - val transform = this["transform"]!! +class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light")) { + val baselineColor = F4Uniform("baselineColor") + val transform = F4x4Uniform("transform") val builder by lazy { StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 32) @@ -67,34 +65,34 @@ class GLLightProgram(state: GLStateTracker) : GLInternalProgram(state, "light") } } -class GLHardLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("hard_light_geometry"), listOf("hard_light_geometry"), listOf("hard_light_geometry")) { - val transform = this["transform"]!! - val localToWorldTransform = this["localToWorldTransform"]!! - val lightPosition = this["lightPosition"]!! - val lightPenetration = this["lightPenetration"]!! +class GLHardLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.gshaders("hard_light_geometry")) { + val transform = F4x4Uniform("transform") + val localToWorldTransform = F4x4Uniform("localToWorldTransform") + val lightPosition = F2Uniform("lightPosition") + val lightPenetration = FUniform("lightPenetration") val builder by lazy { StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 32) } } -class GLSoftLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("soft_light_geometry2"), listOf("soft_light_geometry2")) { - val transform = this["transform"]!! - val lightPenetration = this["lightPenetration"]!! - val localToWorldTransform = this["localToWorldTransform"]!! +class GLSoftLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("soft_light_geometry2")) { + val transform = F4x4Uniform("transform") + val lightPenetration = FUniform("lightPenetration") + val localToWorldTransform = F4x4Uniform("localToWorldTransform") /** * Vector3f(x, y, size) */ - val lightPositionAndSize = this["lightPositionAndSize"]!! + val lightPositionAndSize = F3Uniform("lightPositionAndSize") val builder by lazy { StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_AS_LINES, 32) } } -class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad") { - val color = this["color"]!! +class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) { + val color = F4Uniform("color") private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) @@ -109,7 +107,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre fun clearAlpha() { use() - color.set(ALPHA) + color.value = ALPHA val old = state.blend val oldFunc = state.blendFunc @@ -124,7 +122,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre fun clearColor(color: Color = Color.WHITE) { use() - this.color.set(color) + this.color.value = color val old = state.blend @@ -139,8 +137,8 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre } } -class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex") { - val texture = this["texture0"]!! +class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex")) { + val texture = IUniform("texture0") private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) @@ -154,7 +152,7 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc fun run(texture: Int = 0) { use() - this.texture.set(texture) + this.texture.value = texture builder.draw() } @@ -163,8 +161,8 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc } } -class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex_blur") { - val texture = this["texture0"]!! +class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur")) { + val texture = IUniform("texture0") private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) @@ -178,7 +176,7 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(sta fun run(texture: Int = 0) { use() - this.texture.set(texture) + this.texture.value = texture builder.draw() } @@ -187,18 +185,8 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(sta } } -class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) { - val builder by lazy { - StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 2048) - } - - companion object { - val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build() - } -} - -class GLFlatColorProgram(state: GLStateTracker) : GLInternalProgram(state, "flat_color") { - val transform = this["transform"]!! +class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color")) { + val transform = F4x4Uniform("transform") val builder by lazy { StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384) @@ -209,10 +197,18 @@ class GLFlatColorProgram(state: GLStateTracker) : GLInternalProgram(state, "flat } } +class GLTileProgram(state: GLStateTracker) : GLTransformableColorableProgram(state, state.shaders("tile")) { + var texture by IUniform("_texture") +} + +class GLFontProgram(state: GLStateTracker) : GLTransformableColorableProgram(state, state.shaders("font")) { + var texture by IUniform("_texture") +} + class GLPrograms(val state: GLStateTracker) { - val tile by SimpleProgram("tile", ::GLTransformableColorableProgram) - val font by SimpleProgram("font", ::GLTransformableColorableProgram) - val flat by SimpleProgram("flat", ::GLFlatProgram) + val tile by lazy { GLTileProgram(state) } + val font by lazy { GLFontProgram(state) } + val flat by lazy { GLTransformableColorableProgram(state, state.shaders("flat")) } val flatColor by lazy { GLFlatColorProgram(state) } val liquid by lazy { GLLiquidProgram(state) } val light by lazy { GLLightProgram(state) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt index 5f2e39ca..df3c85c1 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt @@ -34,8 +34,8 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw { builder.upload() state.programs.flat.use() - state.programs.flat.color.set(color) - state.programs.flat.transform.set(state.matrixStack.last) + state.programs.flat.color.value = color + state.programs.flat.transform.value = state.matrixStack.last builder.draw(GL_LINES) } @@ -60,8 +60,8 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw { builder.upload() state.programs.flat.use() - state.programs.flat.color.set(color) - state.programs.flat.transform.set(state.matrixStack.last) + state.programs.flat.color.value = color + state.programs.flat.transform.value = state.matrixStack.last builder.draw(GL_TRIANGLES) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt index 837cd4d3..e0a9fbf8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt @@ -6,6 +6,7 @@ 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.api.IFloatMatrix +import ru.dbotthepony.kvector.api.concrete.IMatrix4f import ru.dbotthepony.kvector.matrix.Matrix4fStack /** @@ -18,13 +19,13 @@ import ru.dbotthepony.kvector.matrix.Matrix4fStack * Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения * шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом) */ -open class ConfiguredShaderProgram( - val program: GLShaderProgram, +open class ConfiguredShaderProgram( + val program: T, ) { - private val transformLocation = program["_transform"] + private val transformLocation = program.getUniform("_transform") as? GLShaderProgram.F4x4Uniform - open fun setTransform(value: IFloatMatrix<*>) { - transformLocation?.set(value) + open fun setTransform(value: IMatrix4f<*>) { + transformLocation?.value = value } /** @@ -40,14 +41,14 @@ open class ConfiguredShaderProgram( * с заданной матрицей трансформации */ class ConfiguredStaticMesh( - val programState: ConfiguredShaderProgram, + val programState: ConfiguredShaderProgram<*>, val indexCount: Int, val vao: VertexArrayObject, val elementIndexType: Int, ) : AutoCloseable { private var onClose = {} - constructor(programState: ConfiguredShaderProgram, builder: VertexBuilder) : this( + constructor(programState: ConfiguredShaderProgram<*>, builder: VertexBuilder) : this( programState, builder.indexCount, programState.program.state.newVAO(), @@ -73,7 +74,7 @@ class ConfiguredStaticMesh( ebo.unbind() } - fun render(transform: IFloatMatrix<*>? = null) { + fun render(transform: IMatrix4f<*>? = null) { check(isValid) { "$this is no longer valid" } programState.setup() 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 b6645873..3480c040 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt @@ -113,7 +113,7 @@ class Font( stack.scale(x = scale, y = scale) state.programs.font.use() - state.programs.font.color.set(color) + state.programs.font.color.value = color state.activeTexture = 0 val space = getGlyph(' ') @@ -343,7 +343,7 @@ class Font( stack.translateWithMultiplication(bearingX, -bearingY) texture!!.bind() - state.programs.font.transform.set(stack.last) + state.programs.font.transform.value = stack.last glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L) checkForGLError() 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 9dbca75e..9c3ef164 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt @@ -10,8 +10,8 @@ import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLTexture2D import ru.dbotthepony.kstarbound.client.gl.GLType import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram -import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram +import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram +import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers @@ -164,9 +164,9 @@ class GPULightRenderer(val state: GLStateTracker) { state.clearColor = old state.programs.hardLightGeometry.use() - state.programs.hardLightGeometry.transform.set(stack.last) - state.programs.hardLightGeometry.lightPosition.set(position) - state.programs.hardLightGeometry.lightPenetration.set(1f - lightPenetration) + state.programs.hardLightGeometry.transform.value = (stack.last) + state.programs.hardLightGeometry.lightPosition.value = (position) + state.programs.hardLightGeometry.lightPenetration.value = (1f - lightPenetration) state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA @@ -175,8 +175,8 @@ class GPULightRenderer(val state: GLStateTracker) { } state.programs.light.use() - state.programs.light.transform.set(stack.last) - state.programs.light.baselineColor.set(color) + state.programs.light.transform.value = (stack.last) + state.programs.light.baselineColor.value = (color) // Свет val builder = state.programs.light.builder @@ -225,9 +225,9 @@ class GPULightRenderer(val state: GLStateTracker) { state.clearColor = old state.programs.softLightGeometry.use() - state.programs.softLightGeometry.transform.set(stack.last) - state.programs.softLightGeometry.lightPositionAndSize.set(Vector3f(position, innerRadius)) - state.programs.softLightGeometry.lightPenetration.set(lightPenetration) + state.programs.softLightGeometry.transform.value = (stack.last) + state.programs.softLightGeometry.lightPositionAndSize.value = (Vector3f(position, innerRadius)) + state.programs.softLightGeometry.lightPenetration.value = (lightPenetration) state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA @@ -241,8 +241,8 @@ class GPULightRenderer(val state: GLStateTracker) { state.cull = false state.programs.light.use() - state.programs.light.transform.set(stack.last) - state.programs.light.baselineColor.set(color) + state.programs.light.transform.value = (stack.last) + state.programs.light.baselineColor.value = (color) // Свет val builder = state.programs.light.builder 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 f55939da..e0078e46 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt @@ -7,6 +7,7 @@ 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.* @@ -17,20 +18,20 @@ import ru.dbotthepony.kvector.vector.nint.Vector2i import kotlin.collections.HashMap data class TileLayer( - val bakedProgramState: ConfiguredShaderProgram, + val bakedProgramState: ConfiguredShaderProgram, val vertexBuilder: VertexBuilder, val zPos: Int ) class TileLayerList { - private val layers = HashMap>() + private val layers = HashMap, Int2ObjectAVLTreeMap>() /** * Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel]. * * Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute]. */ - fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder { + fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder { return layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction { return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel) }).vertexBuilder @@ -83,17 +84,17 @@ class TileRenderers(val client: StarboundClient) { } } - private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.programs.tile) { + private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.programs.tile) { override fun setup() { super.setup() state.activeTexture = 0 state.depthTest = false - program["_texture"] = 0 + program.texture = 0 texture.bind() texture.textureMagFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST - program["_color"] = FOREGROUND_COLOR + program.color.value = FOREGROUND_COLOR } override fun equals(other: Any?): Boolean { @@ -113,17 +114,17 @@ class TileRenderers(val client: StarboundClient) { } } - private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram(state.programs.tile) { + 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 + program.texture = 0 texture.bind() texture.textureMagFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST - program["_color"] = BACKGROUND_COLOR + program.color.value = BACKGROUND_COLOR } override fun equals(other: Any?): Boolean { @@ -146,14 +147,14 @@ class TileRenderers(val client: StarboundClient) { /** * Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой */ - fun foreground(texture: GLTexture2D): ConfiguredShaderProgram { + fun foreground(texture: GLTexture2D): ConfiguredShaderProgram { return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram) } /** * Возвращает запечённое состояние шейдера shaderVertexTextureColor с данной текстурой */ - fun background(texture: GLTexture2D): ConfiguredShaderProgram { + fun background(texture: GLTexture2D): ConfiguredShaderProgram { return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt index c6925757..3b660e3a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt @@ -16,9 +16,9 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk return state.shaderVertexTexture.use() - state.shaderVertexTexture.transform.set(stack.last) + state.shaderVertexTexture.transform.value = (stack.last) state.activeTexture = 0 - state.shaderVertexTexture["_texture"] = 0 + state.shaderVertexTexture.texture.value = 0 for (texture in textures) { texture.bind()