Больше рефакторинга шейдеров, но надо ещё избавится от мусора в виде configuredshaderprogram

This commit is contained in:
DBotThePony 2023-02-21 07:43:28 +07:00
parent b85b90f74c
commit b40f3e8dca
Signed by: DBot
GPG Key ID: DCC23B5715498507
21 changed files with 498 additions and 497 deletions

View File

@ -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<ClientWorld, Client
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) {
if (!setOnce) {
program.localToWorldTransform.set(
program.localToWorldTransform.value =
Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
y = renderOrigin.y * CHUNK_SIZEf)))
y = renderOrigin.y * CHUNK_SIZEf))
setOnce = true
}
@ -337,10 +337,10 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) {
if (!setOnce) {
program.localToWorldTransform.set(
program.localToWorldTransform.value =
Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
y = renderOrigin.y * CHUNK_SIZEf)))
y = renderOrigin.y * CHUNK_SIZEf))
setOnce = true
}
@ -393,7 +393,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
val program = state.programs.liquid
program.use()
program.transform.set(it.last)
program.transform.value = it.last
val builder = program.builder
@ -410,7 +410,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
}
}
program.baselineColor.set(type.color)
program.baselineColor.value = type.color
builder.upload()
builder.draw()

View File

@ -0,0 +1,75 @@
package ru.dbotthepony.kstarbound.client.gl
import org.lwjgl.opengl.GL11.*
@Suppress("unused")
data class BlendFunc(
val sourceColor: Func,
val destinationColor: Func,
val sourceAlpha: Func,
val destinationAlpha: Func
) {
constructor() : this(
Func.ONE,
Func.ZERO,
Func.ONE,
Func.ZERO
)
constructor(
source: Func, destination: Func
) : this(
source,
destination,
source,
destination
)
enum class Func(val enum: Int) {
ZERO(GL_ZERO),
ONE(GL_ONE),
SRC_COLOR(GL_SRC_COLOR),
ONE_MINUS_SRC_COLOR(GL_ONE_MINUS_SRC_COLOR),
SRC_ALPHA(GL_SRC_ALPHA),
ONE_MINUS_SRC_ALPHA(GL_ONE_MINUS_SRC_ALPHA),
DST_ALPHA(GL_DST_ALPHA),
ONE_MINUS_DST_ALPHA(GL_ONE_MINUS_DST_ALPHA),
DST_COLOR(GL_DST_COLOR),
ONE_MINUS_DST_COLOR(GL_ONE_MINUS_DST_COLOR),
SRC_ALPHA_SATURATE(GL_SRC_ALPHA_SATURATE);
}
companion object {
val DEFAULT = BlendFunc()
val MULTIPLY_WITH_ALPHA = BlendFunc(Func.SRC_ALPHA, Func.ONE_MINUS_SRC_ALPHA)
val ADDITIVE = BlendFunc(Func.ONE, Func.ONE)
val ONLY_ALPHA = BlendFunc(
Func.ZERO,
Func.ONE,
Func.ONE,
Func.ZERO
)
val ONLY_BLEND_ALPHA = BlendFunc(
Func.ZERO,
Func.ONE,
Func.ONE,
Func.ONE
)
val ONLY_COLOR = BlendFunc(
Func.ONE,
Func.ZERO,
Func.ZERO,
Func.ONE
)
val MULTIPLY_BY_SRC = BlendFunc(
Func.ZERO,
Func.SRC_COLOR,
Func.ONE,
Func.ZERO
)
}
}

View File

@ -9,26 +9,26 @@ import org.lwjgl.opengl.GL46.*
// GL_STACK_UNDERFLOW
// GL_OUT_OF_MEMORY
sealed class OpenGLError(message: String, val statusCode: Int) : RuntimeException(message)
sealed class OpenGLError(message: String?, val statusCode: Int) : RuntimeException(message)
class OpenGLUnknownError(statusCode: Int, message: String = "Unknown OpenGL error occured: $statusCode") : OpenGLError(message, statusCode)
class OpenGLUnknownError(statusCode: Int, message: String? = null) : OpenGLError(message ?: "Unknown OpenGL error occurred: $statusCode", statusCode)
class OpenGLInvalidEnumException(message: String = "Invalid enum provided") : OpenGLError(message, GL_INVALID_ENUM)
class OpenGLInvalidValueException(message: String = "Invalid value provided") : OpenGLError(message, GL_INVALID_VALUE)
class OpenGLInvalidOperationException(message: String = "Invalid operation in this context or invalid arguments provided") : OpenGLError(message, GL_INVALID_OPERATION)
class OpenGLStackOverflowException(message: String = "Stack overflow in OpenGL") : OpenGLError(message, GL_STACK_OVERFLOW)
class OpenGLStackUnderflowException(message: String = "Stack underflow in OpenGL") : OpenGLError(message, GL_STACK_UNDERFLOW)
class OpenGLOutOfMemoryException(message: String = "Out of Memory in OpenGL") : OpenGLError(message, GL_OUT_OF_MEMORY)
class OpenGLInvalidEnumException(message: String? = null) : OpenGLError(message ?: "Invalid enum provided", GL_INVALID_ENUM)
class OpenGLInvalidValueException(message: String? = null) : OpenGLError(message ?: "Invalid value provided", GL_INVALID_VALUE)
class OpenGLInvalidOperationException(message: String? = null) : OpenGLError(message ?: "Invalid operation in this context or invalid arguments provided", GL_INVALID_OPERATION)
class OpenGLStackOverflowException(message: String? = null) : OpenGLError(message ?: "Stack overflow in OpenGL", GL_STACK_OVERFLOW)
class OpenGLStackUnderflowException(message: String? = null) : OpenGLError(message ?: "Stack underflow in OpenGL", GL_STACK_UNDERFLOW)
class OpenGLOutOfMemoryException(message: String? = null) : OpenGLError(message ?: "Out of Memory in OpenGL", GL_OUT_OF_MEMORY)
fun checkForGLError() {
fun checkForGLError(message: String? = null) {
when (val errorCode = glGetError()) {
GL_NO_ERROR -> {}
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)
}
}

View File

@ -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
}

View File

@ -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<GLStateTracker,
}
}
@Suppress("unused")
data class BlendFunc(
val sourceColor: Func,
val destinationColor: Func,
val sourceAlpha: Func,
val destinationAlpha: Func
) {
constructor() : this(
Func.ONE,
Func.ZERO,
Func.ONE,
Func.ZERO
)
constructor(
source: Func, destination: Func
) : this(
source,
destination,
source,
destination
)
enum class Func(val enum: Int) {
ZERO(GL_ZERO),
ONE(GL_ONE),
SRC_COLOR(GL_SRC_COLOR),
ONE_MINUS_SRC_COLOR(GL_ONE_MINUS_SRC_COLOR),
SRC_ALPHA(GL_SRC_ALPHA),
ONE_MINUS_SRC_ALPHA(GL_ONE_MINUS_SRC_ALPHA),
DST_ALPHA(GL_DST_ALPHA),
ONE_MINUS_DST_ALPHA(GL_ONE_MINUS_DST_ALPHA),
DST_COLOR(GL_DST_COLOR),
ONE_MINUS_DST_COLOR(GL_ONE_MINUS_DST_COLOR),
SRC_ALPHA_SATURATE(GL_SRC_ALPHA_SATURATE);
}
companion object {
val DEFAULT = BlendFunc()
val MULTIPLY_WITH_ALPHA = BlendFunc(Func.SRC_ALPHA, Func.ONE_MINUS_SRC_ALPHA)
val ADDITIVE = BlendFunc(Func.ONE, Func.ONE)
val ONLY_ALPHA = BlendFunc(
Func.ZERO,
Func.ONE,
Func.ONE,
Func.ZERO
)
val ONLY_BLEND_ALPHA = BlendFunc(
Func.ZERO,
Func.ONE,
Func.ONE,
Func.ONE
)
val ONLY_COLOR = BlendFunc(
Func.ONE,
Func.ZERO,
Func.ZERO,
Func.ONE
)
val MULTIPLY_BY_SRC = BlendFunc(
Func.ZERO,
Func.SRC_COLOR,
Func.ONE,
Func.ZERO
)
}
}
interface GLCleanable : Cleaner.Cleanable {
/**
* Выставляет флаг на то, что объект был удалён вручную и вызывает clean()
*/
fun cleanManual()
}
interface GLStreamBuilderList {
val small: StreamVertexBuilder
}
@Suppress("PropertyName", "unused")
class GLStateTracker(val locator: ISBFileLocator) {
private fun isMe(state: GLStateTracker?) {
@ -208,15 +126,20 @@ class GLStateTracker(val locator: ISBFileLocator) {
init {
check(TRACKERS.get() == null) { "Already has state tracker existing at this thread!" }
TRACKERS.set(this)
// 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.
GL.createCapabilities()
}
private val cleanerHits = ArrayList<() -> 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<Shader>) : GLTransformableProgram(this@GLStateTracker, shaders) {
val texture = IUniform("_texture")
}
inner class TexturedColoredProgram(shaders: Iterable<Shader>) : 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<GLStateTracker>()
private fun readInternal(file: String): String {
return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader()
.let {
val read = it.readText()
it.close()
read
}
}
}
}

View File

@ -35,7 +35,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : 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 = "<unknown>") : A
state.texture2D = null
}
cleanable.cleanManual()
cleanable.clean()
isValid = false
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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<String>,
fragment: Collection<String>,
geometry: Collection<String>
): Array<GLShader> {
val result = arrayOfNulls<GLShader>(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<GLShader>
}
open class GLInternalProgram(
state: GLStateTracker,
vertex: Collection<String>,
fragment: Collection<String>,
geometry: Collection<String> = listOf()
) : GLShaderProgram(state, *loadShaders(vertex, fragment, geometry)) {
constructor(state: GLStateTracker, name: String) : this(state, listOf(name), listOf(name))
}

View File

@ -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)
}
}

View File

@ -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<GLShader>) {
constructor(state: GLStateTracker, shaders: Collection<GLShader>) : 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<GLStateTracker.Shader>) {
init {
state.ensureSameThread()
}
@ -26,10 +37,12 @@ open class GLShaderProgram(val state: GLStateTracker, shaders: Stream<GLShader>)
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<GLShader>)
glGetError()
}
private val locationCache = Object2ObjectOpenHashMap<String, Optional<GLUniformLocation>>()
fun use() = state.use(this)
/**
* Возвращает GLUniformLocation или null, если у данной программы нет такого uniform
*
* В т.ч. если она была в коде шейдера, но нигде не использовалась (отсечена компилятором)
*
* Результат поиска кешируется, но для повышения производительности вызывающий код желательно так же
* должен кешировать результат (как локальное свойство или переменная, в случае многократного использования)
*/
operator fun get(name: String): GLUniformLocation? {
private val locations = Object2ObjectArrayMap<String, Uniform<*>>()
private val uniformsExist = Object2BooleanArrayMap<String>()
fun isUniformPresent(name: String): Boolean {
state.ensureSameThread()
return locationCache.computeIfAbsent(name, Object2ObjectFunction {
val location = glGetUniformLocation(pointer, name)
if (location == -1)
return@Object2ObjectFunction Optional.empty<GLUniformLocation>()
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<V : Any>(val name: String) : ReadWriteProperty<Any?, V> {
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<Float>(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<IStruct2f>(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<IStruct3f>(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<IMatrix3f<*>>(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<IMatrix4f<*>>(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<IStruct4f>(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<Int>(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
}
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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<T : GLShaderProgram>(private val name: String, private val factory: (GLStateTracker, Array<out GLShader>) -> T) : ReadOnlyProperty<GLPrograms, T> {
private var value: T? = null
open class GLTransformableProgram(state: GLStateTracker, shaders: Iterable<GLStateTracker.Shader>) : 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<GLStateTracker.Shader>) : GLTransformableProgram(state, shaders) {
val color = F4Uniform("_color")
init {
color.value = Color.WHITE
}
}
private fun GLStateTracker.shaders(name: String): List<GLStateTracker.Shader> {
return listOf(internalVertex("shaders/$name.vsh"), internalFragment("shaders/$name.fsh"))
}
private fun GLStateTracker.gshaders(name: String): List<GLStateTracker.Shader> {
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) }

View File

@ -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)
}

View File

@ -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<T : GLShaderProgram>(
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()

View File

@ -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()

View File

@ -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

View File

@ -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<GLTileProgram>,
val vertexBuilder: VertexBuilder,
val zPos: Int
)
class TileLayerList {
private val layers = HashMap<ConfiguredShaderProgram, Int2ObjectAVLTreeMap<TileLayer>>()
private val layers = HashMap<ConfiguredShaderProgram<GLTileProgram>, Int2ObjectAVLTreeMap<TileLayer>>()
/**
* Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel].
*
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute].
*/
fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder {
fun computeIfAbsent(program: ConfiguredShaderProgram<GLTileProgram>, 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<GLTileProgram>(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<GLTileProgram>(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<GLTileProgram> {
return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram)
}
/**
* Возвращает запечённое состояние шейдера shaderVertexTextureColor с данной текстурой
*/
fun background(texture: GLTexture2D): ConfiguredShaderProgram {
fun background(texture: GLTexture2D): ConfiguredShaderProgram<GLTileProgram> {
return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram)
}

View File

@ -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()