Больше рефакторинга шейдеров, но надо ещё избавится от мусора в виде 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_LINES
import org.lwjgl.opengl.GL11.GL_TRIANGLES import org.lwjgl.opengl.GL11.GL_TRIANGLES
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh 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) renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) { ) {
if (!setOnce) { if (!setOnce) {
program.localToWorldTransform.set( program.localToWorldTransform.value =
Matrix4f.IDENTITY.translateWithMultiplication( Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = renderOrigin.x * CHUNK_SIZEf, Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
y = renderOrigin.y * CHUNK_SIZEf))) y = renderOrigin.y * CHUNK_SIZEf))
setOnce = true 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) renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) { ) {
if (!setOnce) { if (!setOnce) {
program.localToWorldTransform.set( program.localToWorldTransform.value =
Matrix4f.IDENTITY.translateWithMultiplication( Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = renderOrigin.x * CHUNK_SIZEf, Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
y = renderOrigin.y * CHUNK_SIZEf))) y = renderOrigin.y * CHUNK_SIZEf))
setOnce = true setOnce = true
} }
@ -393,7 +393,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
val program = state.programs.liquid val program = state.programs.liquid
program.use() program.use()
program.transform.set(it.last) program.transform.value = it.last
val builder = program.builder 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.upload()
builder.draw() 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_STACK_UNDERFLOW
// GL_OUT_OF_MEMORY // 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 OpenGLInvalidEnumException(message: String? = null) : OpenGLError(message ?: "Invalid enum provided", GL_INVALID_ENUM)
class OpenGLInvalidValueException(message: String = "Invalid value provided") : OpenGLError(message, GL_INVALID_VALUE) class OpenGLInvalidValueException(message: String? = null) : OpenGLError(message ?: "Invalid value provided", GL_INVALID_VALUE)
class OpenGLInvalidOperationException(message: String = "Invalid operation in this context or invalid arguments provided") : OpenGLError(message, GL_INVALID_OPERATION) class OpenGLInvalidOperationException(message: String? = null) : OpenGLError(message ?: "Invalid operation in this context or invalid arguments provided", GL_INVALID_OPERATION)
class OpenGLStackOverflowException(message: String = "Stack overflow in OpenGL") : OpenGLError(message, GL_STACK_OVERFLOW) class OpenGLStackOverflowException(message: String? = null) : OpenGLError(message ?: "Stack overflow in OpenGL", GL_STACK_OVERFLOW)
class OpenGLStackUnderflowException(message: String = "Stack underflow in OpenGL") : OpenGLError(message, GL_STACK_UNDERFLOW) class OpenGLStackUnderflowException(message: String? = null) : OpenGLError(message ?: "Stack underflow in OpenGL", GL_STACK_UNDERFLOW)
class OpenGLOutOfMemoryException(message: String = "Out of Memory in OpenGL") : OpenGLError(message, GL_OUT_OF_MEMORY) 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()) { when (val errorCode = glGetError()) {
GL_NO_ERROR -> {} GL_NO_ERROR -> {}
GL_INVALID_ENUM -> throw OpenGLInvalidEnumException() GL_INVALID_ENUM -> throw OpenGLInvalidEnumException(message)
GL_INVALID_VALUE -> throw OpenGLInvalidValueException() GL_INVALID_VALUE -> throw OpenGLInvalidValueException(message)
GL_INVALID_OPERATION -> throw OpenGLInvalidOperationException() GL_INVALID_OPERATION -> throw OpenGLInvalidOperationException(message)
GL_STACK_OVERFLOW -> throw OpenGLStackOverflowException() GL_STACK_OVERFLOW -> throw OpenGLStackOverflowException(message)
GL_STACK_UNDERFLOW -> throw OpenGLStackUnderflowException() GL_STACK_UNDERFLOW -> throw OpenGLStackUnderflowException(message)
GL_OUT_OF_MEMORY -> throw OpenGLOutOfMemoryException() GL_OUT_OF_MEMORY -> throw OpenGLOutOfMemoryException(message)
else -> throw OpenGLUnknownError(errorCode) else -> throw OpenGLUnknownError(errorCode, message)
} }
} }

View File

@ -21,7 +21,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
val pointer = GL46.glGenFramebuffers() val pointer = GL46.glGenFramebuffers()
init { init {
checkForGLError() checkForGLError("Creating framebuffer")
} }
val isComplete: Boolean get() { 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 var isValid = true
private set private set
@ -85,7 +85,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
if (!isValid) if (!isValid)
return return
cleanable.cleanManual() cleanable.clean()
texture?.close() texture?.close()
isValid = false isValid = false
} }

View File

@ -3,14 +3,14 @@ package ru.dbotthepony.kstarbound.client.gl
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL46.* import org.lwjgl.opengl.GL46.*
import org.lwjgl.opengl.GLCapabilities
import ru.dbotthepony.kstarbound.api.ISBFileLocator import ru.dbotthepony.kstarbound.api.ISBFileLocator
import ru.dbotthepony.kstarbound.client.freetype.FreeType import ru.dbotthepony.kstarbound.client.freetype.FreeType
import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException
import ru.dbotthepony.kstarbound.client.gl.program.GLPrograms import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram 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.GLTransformableProgram
import ru.dbotthepony.kstarbound.client.gl.shader.ShaderCompilationException
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType 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.matrix.Matrix4fStack
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.Color
import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.lang.ref.Cleaner import java.lang.ref.Cleaner
import java.util.concurrent.ThreadFactory 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") @Suppress("PropertyName", "unused")
class GLStateTracker(val locator: ISBFileLocator) { class GLStateTracker(val locator: ISBFileLocator) {
private fun isMe(state: GLStateTracker?) { private fun isMe(state: GLStateTracker?) {
@ -208,15 +126,20 @@ class GLStateTracker(val locator: ISBFileLocator) {
init { init {
check(TRACKERS.get() == null) { "Already has state tracker existing at this thread!" } check(TRACKERS.get() == null) { "Already has state tracker existing at this thread!" }
TRACKERS.set(this) 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 { private val cleaner = Cleaner.create(object : ThreadFactory {
override fun newThread(r: Runnable): Thread { 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 { fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
var cleanManual = false
val cleanable = cleaner.register(ref) { val cleanable = cleaner.register(ref) {
if (!cleanManual && LOGGER.isTraceEnabled) objectsCleaned++
LOGGER.trace("{} with ID {} was GC'd by JVM, without manually calling close()", name, nativeRef)
if (isSameThread()) { if (isSameThread()) {
fn(nativeRef) fn(nativeRef)
checkForGLError() checkForGLError()
} else { } else {
synchronized(cleanerHits) { synchronized(cleanerBacklog) {
cleanerHits.add { cleanerBacklog.add {
fn(nativeRef) fn(nativeRef)
checkForGLError() checkForGLError()
} }
@ -246,23 +166,16 @@ class GLStateTracker(val locator: ISBFileLocator) {
} }
} }
return object : GLCleanable { return cleanable
override fun cleanManual() {
cleanManual = true
clean()
}
override fun clean() = cleanable.clean()
}
} }
fun cleanup() { fun cleanup() {
synchronized(cleanerHits) { synchronized(cleanerBacklog) {
for (lambda in cleanerHits) { for (lambda in cleanerBacklog) {
lambda.invoke() lambda.invoke()
} }
cleanerHits.clear() cleanerBacklog.clear()
} }
} }
@ -427,10 +340,6 @@ class GLStateTracker(val locator: ISBFileLocator) {
fun isSameThread() = thread === Thread.currentThread() fun isSameThread() = thread === Thread.currentThread()
fun program(vararg shaders: GLShader): GLShaderProgram {
return GLShaderProgram(this, *shaders)
}
fun newVBO(type: VBOType = VBOType.ARRAY): VertexBufferObject { fun newVBO(type: VBOType = VBOType.ARRAY): VertexBufferObject {
return VertexBufferObject(this, type) return VertexBufferObject(this, type)
} }
@ -542,20 +451,25 @@ class GLStateTracker(val locator: ISBFileLocator) {
return obj return obj
} }
val shaderVertexTexture: GLTransformableProgram val shaderVertexTexture: TexturedProgram
val shaderVertexTextureColor: GLTransformableColorableProgram 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 { init {
val textureF = GLShader.internalFragment("shaders/fragment/texture.glsl") val textureF = internalFragment("shaders/fragment/texture.glsl")
val textureColorF = GLShader.internalFragment("shaders/fragment/texture_color.glsl") val textureColorF = internalFragment("shaders/fragment/texture_color.glsl")
val textureV = GLShader.internalVertex("shaders/vertex/texture.glsl") val textureV = internalVertex("shaders/vertex/texture.glsl")
shaderVertexTexture = GLTransformableProgram(this, textureF, textureV) shaderVertexTexture = TexturedProgram(listOf(textureF, textureV))
shaderVertexTextureColor = GLTransformableColorableProgram(this, textureColorF, textureV) shaderVertexTextureColor = TexturedColoredProgram(listOf(textureColorF, textureV))
textureF.unlink()
textureColorF.unlink()
textureV.unlink()
} }
val programs = GLPrograms(this) val programs = GLPrograms(this)
@ -578,8 +492,8 @@ class GLStateTracker(val locator: ISBFileLocator) {
builder.upload() builder.upload()
programs.flat.use() programs.flat.use()
programs.flat.color.set(color) programs.flat.color.value = color
programs.flat.transform.set(matrixStack.last) programs.flat.transform.value = matrixStack.last
builder.draw(GL_LINES) builder.draw(GL_LINES)
} }
@ -592,7 +506,7 @@ class GLStateTracker(val locator: ISBFileLocator) {
builder.upload() builder.upload()
programs.flatColor.use() programs.flatColor.use()
programs.flatColor.transform.set(matrixStack.last) programs.flatColor.transform.value = matrixStack.last
builder.draw(GL_TRIANGLES) builder.draw(GL_TRIANGLES)
} }
@ -606,8 +520,60 @@ class GLStateTracker(val locator: ISBFileLocator) {
val box2dRenderer = Box2DRenderer(this) 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 { companion object {
private val LOGGER = LogManager.getLogger(GLStateTracker::class.java) private val LOGGER = LogManager.getLogger(GLStateTracker::class.java)
private val TRACKERS = ThreadLocal<GLStateTracker>() 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() checkForGLError()
} }
private val cleanable = state.registerCleanable(this, ::glDeleteTextures, "2D Texture", pointer) private val cleanable = state.registerCleanable(this, ::glDeleteTextures, pointer)
var width = 0 var width = 0
private set private set
@ -273,7 +273,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
state.texture2D = null state.texture2D = null
} }
cleanable.cleanManual() cleanable.clean()
isValid = false isValid = false
} }

View File

@ -10,7 +10,7 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable {
checkForGLError() checkForGLError()
} }
private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, "Vertex Array Object", pointer) private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, pointer)
fun bind(): VertexArrayObject { fun bind(): VertexArrayObject {
check(isValid) { "Tried to use NULL GLVertexArrayObject" } check(isValid) { "Tried to use NULL GLVertexArrayObject" }
@ -51,8 +51,7 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable {
state.VAO = null state.VAO = null
} }
cleanable.cleanManual() cleanable.clean()
isValid = false isValid = false
} }
} }

View File

@ -14,10 +14,10 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
val pointer = glGenBuffers() val pointer = glGenBuffers()
init { 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 isArray get() = type == VBOType.ARRAY
val isElementArray get() = type == VBOType.ELEMENT_ARRAY val isElementArray get() = type == VBOType.ELEMENT_ARRAY
@ -100,7 +100,7 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
state.VBO = null state.VBO = null
} }
cleanable.cleanManual() cleanable.clean()
isValid = false 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 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.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import org.lwjgl.opengl.GL46.* import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.checkForGLError 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.IFloatMatrix
import ru.dbotthepony.kvector.api.IStruct2f
import ru.dbotthepony.kvector.api.IStruct3f import ru.dbotthepony.kvector.api.IStruct3f
import ru.dbotthepony.kvector.api.IStruct4f 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.*
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.collections.HashSet import kotlin.NoSuchElementException
import kotlin.properties.ReadWriteProperty
open class GLShaderProgram(val state: GLStateTracker, shaders: Stream<GLShader>) { import kotlin.reflect.KProperty
constructor(state: GLStateTracker, shaders: Collection<GLShader>) : this(state, shaders.stream())
constructor(state: GLStateTracker, vararg shaders: GLShader) : this(state, Arrays.stream(shaders))
open class GLShaderProgram(val state: GLStateTracker, shaders: Iterable<GLStateTracker.Shader>) {
init { init {
state.ensureSameThread() state.ensureSameThread()
} }
@ -26,10 +37,12 @@ open class GLShaderProgram(val state: GLStateTracker, shaders: Stream<GLShader>)
val pointer = glCreateProgram() val pointer = glCreateProgram()
init { init {
checkForGLError() checkForGLError("Creating shader program")
state.registerCleanable(this, ::glDeleteProgram, pointer)
for (shader in shaders) { for (shader in shaders) {
glAttachShader(pointer, shader.pointer) glAttachShader(pointer, shader.pointer)
checkForGLError("Attaching shader")
} }
glLinkProgram(pointer) glLinkProgram(pointer)
@ -44,33 +57,175 @@ open class GLShaderProgram(val state: GLStateTracker, shaders: Stream<GLShader>)
glGetError() glGetError()
} }
private val locationCache = Object2ObjectOpenHashMap<String, Optional<GLUniformLocation>>() fun use() = state.use(this)
/** private val locations = Object2ObjectArrayMap<String, Uniform<*>>()
* Возвращает GLUniformLocation или null, если у данной программы нет такого uniform private val uniformsExist = Object2BooleanArrayMap<String>()
*
* В т.ч. если она была в коде шейдера, но нигде не использовалась (отсечена компилятором) fun isUniformPresent(name: String): Boolean {
*
* Результат поиска кешируется, но для повышения производительности вызывающий код желательно так же
* должен кешировать результат (как локальное свойство или переменная, в случае многократного использования)
*/
operator fun get(name: String): GLUniformLocation? {
state.ensureSameThread() state.ensureSameThread()
return uniformsExist.computeIfAbsent(name, Object2BooleanFunction { glGetUniformLocation(pointer, name) != -1 })
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)
} }
operator fun set(name: String, value: IStruct4f) = this[name]?.set(value) fun getUniform(name: String, orElse: ((String) -> Uniform<*>)? = null): Uniform<*>? {
operator fun set(name: String, value: IStruct3f) = this[name]?.set(value) return locations[name] ?: orElse?.invoke(name)
operator fun set(name: String, value: Int) = this[name]?.set(value) }
operator fun set(name: String, value: IFloatMatrix<*>) = this[name]?.set(value)
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.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kvector.vector.Color 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.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f 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.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.GLType 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.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
import ru.dbotthepony.kvector.vector.Color 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> { open class GLTransformableProgram(state: GLStateTracker, shaders: Iterable<GLStateTracker.Shader>) : GLShaderProgram(state, shaders) {
private var value: T? = null val transform = F4x4Uniform("_transform")
override fun getValue(thisRef: GLPrograms, property: KProperty<*>): T { init {
thisRef.state.ensureSameThread() transform.value = Matrix4f.IDENTITY
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
} }
} }
class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid") { open class GLTransformableColorableProgram(state: GLStateTracker, shaders: Iterable<GLStateTracker.Shader>) : GLTransformableProgram(state, shaders) {
val baselineColor = this["baselineColor"]!! val color = F4Uniform("_color")
val transform = this["transform"]!!
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 { val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384) StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384)
@ -54,9 +52,9 @@ class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid"
} }
} }
class GLLightProgram(state: GLStateTracker) : GLInternalProgram(state, "light") { class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light")) {
val baselineColor = this["baselineColor"]!! val baselineColor = F4Uniform("baselineColor")
val transform = this["transform"]!! val transform = F4x4Uniform("transform")
val builder by lazy { val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 32) 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")) { class GLHardLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.gshaders("hard_light_geometry")) {
val transform = this["transform"]!! val transform = F4x4Uniform("transform")
val localToWorldTransform = this["localToWorldTransform"]!! val localToWorldTransform = F4x4Uniform("localToWorldTransform")
val lightPosition = this["lightPosition"]!! val lightPosition = F2Uniform("lightPosition")
val lightPenetration = this["lightPenetration"]!! val lightPenetration = FUniform("lightPenetration")
val builder by lazy { val builder by lazy {
StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 32) StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 32)
} }
} }
class GLSoftLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("soft_light_geometry2"), listOf("soft_light_geometry2")) { class GLSoftLightGeometryProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("soft_light_geometry2")) {
val transform = this["transform"]!! val transform = F4x4Uniform("transform")
val lightPenetration = this["lightPenetration"]!! val lightPenetration = FUniform("lightPenetration")
val localToWorldTransform = this["localToWorldTransform"]!! val localToWorldTransform = F4x4Uniform("localToWorldTransform")
/** /**
* Vector3f(x, y, size) * Vector3f(x, y, size)
*/ */
val lightPositionAndSize = this["lightPositionAndSize"]!! val lightPositionAndSize = F3Uniform("lightPositionAndSize")
val builder by lazy { val builder by lazy {
StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_AS_LINES, 32) StreamVertexBuilder(state, GPULightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_AS_LINES, 32)
} }
} }
class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad") { class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) {
val color = this["color"]!! val color = F4Uniform("color")
private val builder by lazy { private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
@ -109,7 +107,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre
fun clearAlpha() { fun clearAlpha() {
use() use()
color.set(ALPHA) color.value = ALPHA
val old = state.blend val old = state.blend
val oldFunc = state.blendFunc val oldFunc = state.blendFunc
@ -124,7 +122,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre
fun clearColor(color: Color = Color.WHITE) { fun clearColor(color: Color = Color.WHITE) {
use() use()
this.color.set(color) this.color.value = color
val old = state.blend val old = state.blend
@ -139,8 +137,8 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre
} }
} }
class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex") { class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex")) {
val texture = this["texture0"]!! val texture = IUniform("texture0")
private val builder by lazy { private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
@ -154,7 +152,7 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc
fun run(texture: Int = 0) { fun run(texture: Int = 0) {
use() use()
this.texture.set(texture) this.texture.value = texture
builder.draw() builder.draw()
} }
@ -163,8 +161,8 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc
} }
} }
class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex_blur") { class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur")) {
val texture = this["texture0"]!! val texture = IUniform("texture0")
private val builder by lazy { private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
@ -178,7 +176,7 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(sta
fun run(texture: Int = 0) { fun run(texture: Int = 0) {
use() use()
this.texture.set(texture) this.texture.value = texture
builder.draw() builder.draw()
} }
@ -187,18 +185,8 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(sta
} }
} }
class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) { class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color")) {
val builder by lazy { val transform = F4x4Uniform("transform")
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"]!!
val builder by lazy { val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384) 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) { class GLPrograms(val state: GLStateTracker) {
val tile by SimpleProgram("tile", ::GLTransformableColorableProgram) val tile by lazy { GLTileProgram(state) }
val font by SimpleProgram("font", ::GLTransformableColorableProgram) val font by lazy { GLFontProgram(state) }
val flat by SimpleProgram("flat", ::GLFlatProgram) val flat by lazy { GLTransformableColorableProgram(state, state.shaders("flat")) }
val flatColor by lazy { GLFlatColorProgram(state) } val flatColor by lazy { GLFlatColorProgram(state) }
val liquid by lazy { GLLiquidProgram(state) } val liquid by lazy { GLLiquidProgram(state) }
val light by lazy { GLLightProgram(state) } val light by lazy { GLLightProgram(state) }

View File

@ -34,8 +34,8 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
builder.upload() builder.upload()
state.programs.flat.use() state.programs.flat.use()
state.programs.flat.color.set(color) state.programs.flat.color.value = color
state.programs.flat.transform.set(state.matrixStack.last) state.programs.flat.transform.value = state.matrixStack.last
builder.draw(GL_LINES) builder.draw(GL_LINES)
} }
@ -60,8 +60,8 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
builder.upload() builder.upload()
state.programs.flat.use() state.programs.flat.use()
state.programs.flat.color.set(color) state.programs.flat.color.value = color
state.programs.flat.transform.set(state.matrixStack.last) state.programs.flat.transform.value = state.matrixStack.last
builder.draw(GL_TRIANGLES) 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.checkForGLError
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
import ru.dbotthepony.kvector.api.IFloatMatrix import ru.dbotthepony.kvector.api.IFloatMatrix
import ru.dbotthepony.kvector.api.concrete.IMatrix4f
import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.matrix.Matrix4fStack
/** /**
@ -18,13 +19,13 @@ import ru.dbotthepony.kvector.matrix.Matrix4fStack
* Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения * Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения
* шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом) * шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом)
*/ */
open class ConfiguredShaderProgram( open class ConfiguredShaderProgram<T : GLShaderProgram>(
val program: GLShaderProgram, val program: T,
) { ) {
private val transformLocation = program["_transform"] private val transformLocation = program.getUniform("_transform") as? GLShaderProgram.F4x4Uniform
open fun setTransform(value: IFloatMatrix<*>) { open fun setTransform(value: IMatrix4f<*>) {
transformLocation?.set(value) transformLocation?.value = value
} }
/** /**
@ -40,14 +41,14 @@ open class ConfiguredShaderProgram(
* с заданной матрицей трансформации * с заданной матрицей трансформации
*/ */
class ConfiguredStaticMesh( class ConfiguredStaticMesh(
val programState: ConfiguredShaderProgram, val programState: ConfiguredShaderProgram<*>,
val indexCount: Int, val indexCount: Int,
val vao: VertexArrayObject, val vao: VertexArrayObject,
val elementIndexType: Int, val elementIndexType: Int,
) : AutoCloseable { ) : AutoCloseable {
private var onClose = {} private var onClose = {}
constructor(programState: ConfiguredShaderProgram, builder: VertexBuilder) : this( constructor(programState: ConfiguredShaderProgram<*>, builder: VertexBuilder) : this(
programState, programState,
builder.indexCount, builder.indexCount,
programState.program.state.newVAO(), programState.program.state.newVAO(),
@ -73,7 +74,7 @@ class ConfiguredStaticMesh(
ebo.unbind() ebo.unbind()
} }
fun render(transform: IFloatMatrix<*>? = null) { fun render(transform: IMatrix4f<*>? = null) {
check(isValid) { "$this is no longer valid" } check(isValid) { "$this is no longer valid" }
programState.setup() programState.setup()

View File

@ -113,7 +113,7 @@ class Font(
stack.scale(x = scale, y = scale) stack.scale(x = scale, y = scale)
state.programs.font.use() state.programs.font.use()
state.programs.font.color.set(color) state.programs.font.color.value = color
state.activeTexture = 0 state.activeTexture = 0
val space = getGlyph(' ') val space = getGlyph(' ')
@ -343,7 +343,7 @@ class Font(
stack.translateWithMultiplication(bearingX, -bearingY) stack.translateWithMultiplication(bearingX, -bearingY)
texture!!.bind() texture!!.bind()
state.programs.font.transform.set(stack.last) state.programs.font.transform.value = stack.last
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L) glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
checkForGLError() 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.GLTexture2D
import ru.dbotthepony.kstarbound.client.gl.GLType import ru.dbotthepony.kstarbound.client.gl.GLType
import ru.dbotthepony.kstarbound.client.gl.checkForGLError import ru.dbotthepony.kstarbound.client.gl.checkForGLError
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.shader.GLHardLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.shader.GLSoftLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
@ -164,9 +164,9 @@ class GPULightRenderer(val state: GLStateTracker) {
state.clearColor = old state.clearColor = old
state.programs.hardLightGeometry.use() state.programs.hardLightGeometry.use()
state.programs.hardLightGeometry.transform.set(stack.last) state.programs.hardLightGeometry.transform.value = (stack.last)
state.programs.hardLightGeometry.lightPosition.set(position) state.programs.hardLightGeometry.lightPosition.value = (position)
state.programs.hardLightGeometry.lightPenetration.set(1f - lightPenetration) state.programs.hardLightGeometry.lightPenetration.value = (1f - lightPenetration)
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
@ -175,8 +175,8 @@ class GPULightRenderer(val state: GLStateTracker) {
} }
state.programs.light.use() state.programs.light.use()
state.programs.light.transform.set(stack.last) state.programs.light.transform.value = (stack.last)
state.programs.light.baselineColor.set(color) state.programs.light.baselineColor.value = (color)
// Свет // Свет
val builder = state.programs.light.builder val builder = state.programs.light.builder
@ -225,9 +225,9 @@ class GPULightRenderer(val state: GLStateTracker) {
state.clearColor = old state.clearColor = old
state.programs.softLightGeometry.use() state.programs.softLightGeometry.use()
state.programs.softLightGeometry.transform.set(stack.last) state.programs.softLightGeometry.transform.value = (stack.last)
state.programs.softLightGeometry.lightPositionAndSize.set(Vector3f(position, innerRadius)) state.programs.softLightGeometry.lightPositionAndSize.value = (Vector3f(position, innerRadius))
state.programs.softLightGeometry.lightPenetration.set(lightPenetration) state.programs.softLightGeometry.lightPenetration.value = (lightPenetration)
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
@ -241,8 +241,8 @@ class GPULightRenderer(val state: GLStateTracker) {
state.cull = false state.cull = false
state.programs.light.use() state.programs.light.use()
state.programs.light.transform.set(stack.last) state.programs.light.transform.value = (stack.last)
state.programs.light.baselineColor.set(color) state.programs.light.baselineColor.value = (color)
// Свет // Свет
val builder = state.programs.light.builder 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.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.* 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.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.* import ru.dbotthepony.kstarbound.client.gl.vertex.*
import ru.dbotthepony.kstarbound.defs.tile.* import ru.dbotthepony.kstarbound.defs.tile.*
@ -17,20 +18,20 @@ import ru.dbotthepony.kvector.vector.nint.Vector2i
import kotlin.collections.HashMap import kotlin.collections.HashMap
data class TileLayer( data class TileLayer(
val bakedProgramState: ConfiguredShaderProgram, val bakedProgramState: ConfiguredShaderProgram<GLTileProgram>,
val vertexBuilder: VertexBuilder, val vertexBuilder: VertexBuilder,
val zPos: Int val zPos: Int
) )
class TileLayerList { class TileLayerList {
private val layers = HashMap<ConfiguredShaderProgram, Int2ObjectAVLTreeMap<TileLayer>>() private val layers = HashMap<ConfiguredShaderProgram<GLTileProgram>, Int2ObjectAVLTreeMap<TileLayer>>()
/** /**
* Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel]. * Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel].
* *
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute]. * Если такого слоя нет, вызывается [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 layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction {
return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel) return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
}).vertexBuilder }).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() { override fun setup() {
super.setup() super.setup()
state.activeTexture = 0 state.activeTexture = 0
state.depthTest = false state.depthTest = false
program["_texture"] = 0 program.texture = 0
texture.bind() texture.bind()
texture.textureMagFilter = GL_NEAREST texture.textureMagFilter = GL_NEAREST
texture.textureMinFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST
program["_color"] = FOREGROUND_COLOR program.color.value = FOREGROUND_COLOR
} }
override fun equals(other: Any?): Boolean { 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() { override fun setup() {
super.setup() super.setup()
state.activeTexture = 0 state.activeTexture = 0
state.depthTest = false state.depthTest = false
program["_texture"] = 0 program.texture = 0
texture.bind() texture.bind()
texture.textureMagFilter = GL_NEAREST texture.textureMagFilter = GL_NEAREST
texture.textureMinFilter = GL_NEAREST texture.textureMinFilter = GL_NEAREST
program["_color"] = BACKGROUND_COLOR program.color.value = BACKGROUND_COLOR
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -146,14 +147,14 @@ class TileRenderers(val client: StarboundClient) {
/** /**
* Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой * Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой
*/ */
fun foreground(texture: GLTexture2D): ConfiguredShaderProgram { fun foreground(texture: GLTexture2D): ConfiguredShaderProgram<GLTileProgram> {
return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram) return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram)
} }
/** /**
* Возвращает запечённое состояние шейдера shaderVertexTextureColor с данной текстурой * Возвращает запечённое состояние шейдера shaderVertexTextureColor с данной текстурой
*/ */
fun background(texture: GLTexture2D): ConfiguredShaderProgram { fun background(texture: GLTexture2D): ConfiguredShaderProgram<GLTileProgram> {
return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram) return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram)
} }

View File

@ -16,9 +16,9 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk
return return
state.shaderVertexTexture.use() state.shaderVertexTexture.use()
state.shaderVertexTexture.transform.set(stack.last) state.shaderVertexTexture.transform.value = (stack.last)
state.activeTexture = 0 state.activeTexture = 0
state.shaderVertexTexture["_texture"] = 0 state.shaderVertexTexture.texture.value = 0
for (texture in textures) { for (texture in textures) {
texture.bind() texture.bind()