New shader render pipeline
This commit is contained in:
parent
62dfc63839
commit
339891b6e2
@ -83,7 +83,7 @@ dependencies {
|
|||||||
implementation("com.github.jnr:jnr-ffi:2.2.13")
|
implementation("com.github.jnr:jnr-ffi:2.2.13")
|
||||||
|
|
||||||
implementation("ru.dbotthepony:kbox2d:2.4.1.6")
|
implementation("ru.dbotthepony:kbox2d:2.4.1.6")
|
||||||
implementation("ru.dbotthepony:kvector:2.8.0")
|
implementation("ru.dbotthepony:kvector:2.9.0")
|
||||||
|
|
||||||
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import org.lwjgl.glfw.GLFW
|
|||||||
import org.lwjgl.glfw.GLFWErrorCallback
|
import org.lwjgl.glfw.GLFWErrorCallback
|
||||||
import org.lwjgl.opengl.GL
|
import org.lwjgl.opengl.GL
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
|
import org.lwjgl.opengl.GL15
|
||||||
import org.lwjgl.opengl.GL45.*
|
import org.lwjgl.opengl.GL45.*
|
||||||
import org.lwjgl.opengl.GLCapabilities
|
import org.lwjgl.opengl.GLCapabilities
|
||||||
import org.lwjgl.system.MemoryStack
|
import org.lwjgl.system.MemoryStack
|
||||||
@ -33,9 +34,8 @@ import ru.dbotthepony.kstarbound.client.gl.properties.GLStateSwitchTracker
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
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.vertex.GLAttributeList
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
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.StreamVertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.input.UserInput
|
import ru.dbotthepony.kstarbound.client.input.UserInput
|
||||||
@ -49,22 +49,23 @@ import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
|||||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
|
import ru.dbotthepony.kstarbound.util.forEachValid
|
||||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||||
import ru.dbotthepony.kvector.api.IStruct4f
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
import ru.dbotthepony.kvector.arrays.Matrix3fStack
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
import ru.dbotthepony.kvector.vector.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
import ru.dbotthepony.kvector.vector.Vector3f
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.Cleaner
|
import java.lang.ref.Cleaner
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -106,19 +107,13 @@ class StarboundClient : Closeable {
|
|||||||
var clientTerminated = false
|
var clientTerminated = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
var viewportMatrixScreen: Matrix3f
|
||||||
* Матрица преобразования экранных координат (в пикселях) в нормализованные координаты
|
|
||||||
*/
|
|
||||||
var viewportMatrixScreen: Matrix4f
|
|
||||||
private set
|
private set
|
||||||
get() = Matrix4f.unmodifiable(field)
|
get() = Matrix3f.unmodifiable(field)
|
||||||
|
|
||||||
/**
|
var viewportMatrixWorld: Matrix3f
|
||||||
* Матрица преобразования мировых координат в нормализованные координаты
|
|
||||||
*/
|
|
||||||
var viewportMatrixWorld: Matrix4f
|
|
||||||
private set
|
private set
|
||||||
get() = Matrix4f.unmodifiable(field)
|
get() = Matrix3f.unmodifiable(field)
|
||||||
|
|
||||||
var isRenderingGame = true
|
var isRenderingGame = true
|
||||||
private set
|
private set
|
||||||
@ -140,12 +135,10 @@ class StarboundClient : Closeable {
|
|||||||
thread
|
thread
|
||||||
}
|
}
|
||||||
|
|
||||||
@Volatile
|
var objectsCreated = 0L
|
||||||
var objectsCleaned = 0L
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@Volatile
|
var objectsCleaned = 0L
|
||||||
var gcHits = 0L
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -235,12 +228,19 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
GLFW.glfwShowWindow(window)
|
GLFW.glfwShowWindow(window)
|
||||||
putDebugLog("Initialized GLFW window")
|
putDebugLog("Initialized GLFW window")
|
||||||
|
|
||||||
|
val v = glGenBuffers()
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, v)
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v)
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
|
||||||
|
GL15.glDeleteBuffers(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DLines by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.LINES) }
|
val maxTextureBlocks = glGetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
|
||||||
val flat2DTriangles by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.TRIANGLES) }
|
val maxVertexAttribBindPoints = glGetInteger(GL_MAX_VERTEX_ATTRIB_BINDINGS)
|
||||||
val flat2DTexturedQuads by lazy { StreamVertexBuilder(GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS) }
|
|
||||||
val quadWireframe by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME) }
|
val stack = Matrix3fStack()
|
||||||
|
|
||||||
// минимальное время хранения 5 минут и...
|
// минимальное время хранения 5 минут и...
|
||||||
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
||||||
@ -260,24 +260,26 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val missingTexturePath = "/assetmissing.png"
|
private val missingTexturePath = "/assetmissing.png"
|
||||||
|
private val regularShaderPrograms = ArrayList<WeakReference<GLShaderProgram.Regular>>()
|
||||||
|
|
||||||
val matrixStack = Matrix4fStack()
|
fun addShaderProgram(program: GLShaderProgram) {
|
||||||
val freeType = FreeType()
|
if (program is GLShaderProgram.Regular) {
|
||||||
val font = Font()
|
regularShaderPrograms.add(WeakReference(program))
|
||||||
val box2dRenderer = Box2DRenderer()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
|
fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
|
||||||
val cleanable = cleaner.register(ref) {
|
objectsCreated++
|
||||||
objectsCleaned++
|
|
||||||
|
|
||||||
|
val cleanable = cleaner.register(ref) {
|
||||||
if (isSameThread()) {
|
if (isSameThread()) {
|
||||||
|
objectsCleaned++
|
||||||
fn(nativeRef)
|
fn(nativeRef)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
} else {
|
} else {
|
||||||
gcHits++
|
|
||||||
|
|
||||||
synchronized(cleanerBacklog) {
|
synchronized(cleanerBacklog) {
|
||||||
cleanerBacklog.add {
|
cleanerBacklog.add {
|
||||||
|
objectsCleaned++
|
||||||
fn(nativeRef)
|
fn(nativeRef)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
}
|
}
|
||||||
@ -318,13 +320,10 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
||||||
|
|
||||||
var vbo by GLObjectTracker<BufferObject.VBO>(::glBindBuffer, GL_ARRAY_BUFFER)
|
|
||||||
var ebo by GLObjectTracker<BufferObject.EBO>(::glBindBuffer, GL_ELEMENT_ARRAY_BUFFER)
|
|
||||||
var vao by GLObjectTracker<VertexArrayObject>(::glBindVertexArray)
|
var vao by GLObjectTracker<VertexArrayObject>(::glBindVertexArray)
|
||||||
var framebuffer by GLObjectTracker<GLFrameBuffer>(::glBindFramebuffer, GL_FRAMEBUFFER)
|
var framebuffer by GLObjectTracker<GLFrameBuffer>(::glBindFramebuffer, GL_FRAMEBUFFER)
|
||||||
var program by GLObjectTracker<GLShaderProgram>(::glUseProgram)
|
var program by GLObjectTracker<GLShaderProgram>(::glUseProgram)
|
||||||
|
|
||||||
val maxTextureBlocks = glGetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
|
|
||||||
private val textures = Array(maxTextureBlocks) { GLObjectTracker<GLTexture2D>(GL11::glBindTexture, GL_TEXTURE_2D) }
|
private val textures = Array(maxTextureBlocks) { GLObjectTracker<GLTexture2D>(GL11::glBindTexture, GL_TEXTURE_2D) }
|
||||||
|
|
||||||
var activeTexture = 0
|
var activeTexture = 0
|
||||||
@ -352,6 +351,9 @@ class StarboundClient : Closeable {
|
|||||||
glBlendFuncSeparate(it.sourceColor.enum, it.destinationColor.enum, it.sourceAlpha.enum, it.destinationAlpha.enum)
|
glBlendFuncSeparate(it.sourceColor.enum, it.destinationColor.enum, it.sourceAlpha.enum, it.destinationAlpha.enum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val freeType = FreeType()
|
||||||
|
val font = Font()
|
||||||
|
val box2dRenderer = Box2DRenderer()
|
||||||
val programs = GLPrograms()
|
val programs = GLPrograms()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -448,61 +450,24 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newVBO() = BufferObject.VBO()
|
|
||||||
fun newEBO() = BufferObject.EBO()
|
fun newEBO() = BufferObject.EBO()
|
||||||
|
fun newVBO() = BufferObject.VBO()
|
||||||
fun newVAO() = VertexArrayObject()
|
fun newVAO() = VertexArrayObject()
|
||||||
|
|
||||||
inline fun quadWireframe(color: RGBAColor = RGBAColor.WHITE, lambda: (VertexBuilder) -> Unit) {
|
inline fun quadWireframe(color: RGBAColor = RGBAColor.WHITE, lambda: (VertexBuilder) -> Unit) {
|
||||||
val builder = quadWireframe
|
val builder = programs.position.builder
|
||||||
|
|
||||||
builder.builder.begin()
|
builder.builder.begin(GeometryType.QUADS_AS_LINES_WIREFRAME)
|
||||||
lambda.invoke(builder.builder)
|
lambda.invoke(builder.builder)
|
||||||
builder.upload()
|
builder.upload()
|
||||||
|
|
||||||
programs.flat.use()
|
programs.position.use()
|
||||||
programs.flat.color = color
|
programs.position.colorMultiplier = color
|
||||||
programs.flat.transform = matrixStack.last()
|
programs.position.worldMatrix = stack.last()
|
||||||
|
|
||||||
builder.draw(GL_LINES)
|
builder.draw(GL_LINES)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun quadColor(lambda: (VertexBuilder) -> Unit) {
|
|
||||||
val builder = programs.flatColor.builder
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
lambda.invoke(builder.builder)
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
programs.flatColor.use()
|
|
||||||
programs.flatColor.transform = matrixStack.last()
|
|
||||||
|
|
||||||
builder.draw(GL_TRIANGLES)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun quadTexture(texture: GLTexture2D, lambda: (VertexBuilder) -> Unit) {
|
|
||||||
val builder = programs.textured2d.builder
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
lambda.invoke(builder.builder)
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
activeTexture = 0
|
|
||||||
texture.bind()
|
|
||||||
|
|
||||||
programs.textured2d.use()
|
|
||||||
programs.textured2d.transform = matrixStack.last()
|
|
||||||
programs.textured2d.texture = 0
|
|
||||||
|
|
||||||
builder.draw(GL_TRIANGLES)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun quadWireframe(value: AABB, color: RGBAColor = RGBAColor.WHITE, chain: (VertexBuilder) -> Unit = {}) {
|
|
||||||
quadWireframe(color) {
|
|
||||||
it.quad(value.mins.x.toFloat(), value.mins.y.toFloat(), value.maxs.x.toFloat(), value.maxs.y.toFloat())
|
|
||||||
chain(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER)
|
fun vertex(file: File) = GLShader(file, GL_VERTEX_SHADER)
|
||||||
fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER)
|
fun fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
@ -533,12 +498,12 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateViewportMatrixScreen(): Matrix4f {
|
private fun updateViewportMatrixScreen(): Matrix3f {
|
||||||
return Matrix4f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f).translate(Vector3f(z = 2f))
|
return Matrix3f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateViewportMatrixWorld(): Matrix4f {
|
private fun updateViewportMatrixWorld(): Matrix3f {
|
||||||
return Matrix4f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 1f, 100f)
|
return Matrix3f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
private val xMousePos = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
|
private val xMousePos = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
|
||||||
@ -764,12 +729,14 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
clearColor = RGBAColor.SLATE_GRAY
|
clearColor = RGBAColor.SLATE_GRAY
|
||||||
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
||||||
matrixStack.clear(viewportMatrixWorld)
|
stack.clear(Matrix3f.identity())
|
||||||
|
|
||||||
matrixStack.push().last()
|
val viewMatrix = viewportMatrixWorld.copy()
|
||||||
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
|
.translate(viewportWidth / 2f, viewportHeight / 2f) // центр экрана + координаты отрисовки мира
|
||||||
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
|
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
|
||||||
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
|
.translate(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
|
||||||
|
|
||||||
|
regularShaderPrograms.forEachValid { it.viewMatrix = viewMatrix }
|
||||||
|
|
||||||
for (lambda in onPreDrawWorld) {
|
for (lambda in onPreDrawWorld) {
|
||||||
lambda.invoke(layers)
|
lambda.invoke(layers)
|
||||||
@ -786,7 +753,7 @@ class StarboundClient : Closeable {
|
|||||||
layers = layers,
|
layers = layers,
|
||||||
size = viewportRectangle)
|
size = viewportRectangle)
|
||||||
|
|
||||||
layers.render(matrixStack)
|
layers.render()
|
||||||
|
|
||||||
val viewportLightingMem = viewportLightingMem
|
val viewportLightingMem = viewportLightingMem
|
||||||
|
|
||||||
@ -818,7 +785,7 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
||||||
|
|
||||||
quadTexture(viewportLightingTexture) {
|
/*quadTexture(viewportLightingTexture) {
|
||||||
it.quad(
|
it.quad(
|
||||||
(viewportCellX).toFloat(),
|
(viewportCellX).toFloat(),
|
||||||
(viewportCellY).toFloat(),
|
(viewportCellY).toFloat(),
|
||||||
@ -826,7 +793,7 @@ class StarboundClient : Closeable {
|
|||||||
(viewportCellY + viewportCellHeight).toFloat(),
|
(viewportCellY + viewportCellHeight).toFloat(),
|
||||||
QuadTransformers.uv()
|
QuadTransformers.uv()
|
||||||
)
|
)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
||||||
}
|
}
|
||||||
@ -836,11 +803,9 @@ class StarboundClient : Closeable {
|
|||||||
for (lambda in onPostDrawWorld) {
|
for (lambda in onPostDrawWorld) {
|
||||||
lambda.invoke()
|
lambda.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixStack.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixStack.clear(viewportMatrixScreen)
|
regularShaderPrograms.forEachValid { it.viewMatrix = viewportMatrixScreen }
|
||||||
|
|
||||||
val thisTime = System.currentTimeMillis()
|
val thisTime = System.currentTimeMillis()
|
||||||
|
|
||||||
@ -851,22 +816,24 @@ class StarboundClient : Closeable {
|
|||||||
alpha = (finishStartupRendering - thisTime) / 1000f
|
alpha = (finishStartupRendering - thisTime) / 1000f
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixStack.push()
|
stack.push()
|
||||||
matrixStack.last().translateWithMultiplication(y = viewportHeight.toFloat())
|
stack.last().translate(y = viewportHeight.toFloat())
|
||||||
var shade = 255
|
var shade = 255
|
||||||
|
|
||||||
for (i in startupTextList.size - 1 downTo 0) {
|
for (i in startupTextList.size - 1 downTo 0) {
|
||||||
val size = font.render(startupTextList[i], alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
val size = font.render(startupTextList[i], alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
||||||
matrixStack.last().translateWithMultiplication(y = -size.height * 1.2f)
|
stack.last().translate(y = -size.height * 1.2f)
|
||||||
|
|
||||||
if (shade > 120) {
|
if (shade > 120) {
|
||||||
shade -= 10
|
shade -= 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixStack.pop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack.clear(Matrix3f.identity())
|
||||||
|
|
||||||
for (fn in onDrawGUI) {
|
for (fn in onDrawGUI) {
|
||||||
fn.invoke()
|
fn.invoke()
|
||||||
}
|
}
|
||||||
@ -876,6 +843,7 @@ class StarboundClient : Closeable {
|
|||||||
font.render("Latency: ${(averageRenderWait * 1_00000.0).toInt() / 100f}ms", scale = 0.4f)
|
font.render("Latency: ${(averageRenderWait * 1_00000.0).toInt() / 100f}ms", scale = 0.4f)
|
||||||
font.render("Frame: ${(averageRenderTime * 1_00000.0).toInt() / 100f}ms", y = font.lineHeight * 0.6f, scale = 0.4f)
|
font.render("Frame: ${(averageRenderTime * 1_00000.0).toInt() / 100f}ms", y = font.lineHeight * 0.6f, scale = 0.4f)
|
||||||
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 1.2f, scale = 0.4f)
|
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 1.2f, scale = 0.4f)
|
||||||
|
font.render("OGL A: ${objectsCreated - objectsCleaned} D: $objectsCleaned", y = font.lineHeight * 1.8f, scale = 0.4f)
|
||||||
|
|
||||||
GLFW.glfwSwapBuffers(window)
|
GLFW.glfwSwapBuffers(window)
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
|
@ -5,23 +5,26 @@ import org.lwjgl.system.MemoryUtil
|
|||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
sealed class BufferObject(val glType: Int) : GLObject() {
|
sealed class BufferObject : GLObject() {
|
||||||
final override val client = StarboundClient.current()
|
final override val client = StarboundClient.current()
|
||||||
final override val pointer = glGenBuffers()
|
abstract val glType: Int
|
||||||
|
|
||||||
init {
|
override fun bind() {
|
||||||
checkForGLError("Creating Vertex Buffer Object")
|
// do nothing
|
||||||
client.registerCleanable(this, ::glDeleteBuffers, pointer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: ByteBuffer, usage: Int): BufferObject {
|
override fun unbind() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun bufferData(data: ByteBuffer, usage: Int): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: ByteBuffer, usage: Int, length: Long): BufferObject {
|
open fun bufferData(data: ByteBuffer, usage: Int, length: Long): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (length > data.remaining().toLong()) {
|
if (length > data.remaining().toLong()) {
|
||||||
@ -34,51 +37,103 @@ sealed class BufferObject(val glType: Int) : GLObject() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: IntArray, usage: Int): BufferObject {
|
open fun bufferData(data: IntArray, usage: Int): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: FloatArray, usage: Int): BufferObject {
|
open fun bufferData(data: FloatArray, usage: Int): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: DoubleArray, usage: Int): BufferObject {
|
open fun bufferData(data: DoubleArray, usage: Int): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: LongArray, usage: Int): BufferObject {
|
open fun bufferData(data: LongArray, usage: Int): BufferObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
class VBO : BufferObject(GL_ARRAY_BUFFER) {
|
class EBO : BufferObject() {
|
||||||
override fun bind() {
|
override val glType: Int
|
||||||
client.vbo = this
|
get() = GL_ELEMENT_ARRAY_BUFFER
|
||||||
|
|
||||||
|
override val pointer: Int = glCreateBuffers()
|
||||||
|
|
||||||
|
init {
|
||||||
|
checkForGLError("Creating Element Buffer Object")
|
||||||
|
client.registerCleanable(this, ::glDeleteBuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbind() {
|
override fun bufferData(data: ByteBuffer, usage: Int): EBO {
|
||||||
if (client.vbo == this) client.vbo = null
|
return super.bufferData(data, usage) as EBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: ByteBuffer, usage: Int, length: Long): EBO {
|
||||||
|
return super.bufferData(data, usage, length) as EBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: IntArray, usage: Int): EBO {
|
||||||
|
return super.bufferData(data, usage) as EBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: FloatArray, usage: Int): EBO {
|
||||||
|
return super.bufferData(data, usage) as EBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: DoubleArray, usage: Int): EBO {
|
||||||
|
return super.bufferData(data, usage) as EBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: LongArray, usage: Int): EBO {
|
||||||
|
return super.bufferData(data, usage) as EBO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EBO : BufferObject(GL_ELEMENT_ARRAY_BUFFER) {
|
class VBO : BufferObject() {
|
||||||
override fun bind() {
|
override val glType: Int
|
||||||
client.ebo = this
|
get() = GL_ARRAY_BUFFER
|
||||||
|
|
||||||
|
override val pointer: Int = glCreateBuffers()
|
||||||
|
|
||||||
|
init {
|
||||||
|
checkForGLError("Creating Vertex Buffer Object")
|
||||||
|
client.registerCleanable(this, ::glDeleteBuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbind() {
|
override fun bufferData(data: ByteBuffer, usage: Int): VBO {
|
||||||
if (client.ebo == this) client.ebo = null
|
return super.bufferData(data, usage) as VBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: ByteBuffer, usage: Int, length: Long): VBO {
|
||||||
|
return super.bufferData(data, usage, length) as VBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: IntArray, usage: Int): VBO {
|
||||||
|
return super.bufferData(data, usage) as VBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: FloatArray, usage: Int): VBO {
|
||||||
|
return super.bufferData(data, usage) as VBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: DoubleArray, usage: Int): VBO {
|
||||||
|
return super.bufferData(data, usage) as VBO
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bufferData(data: LongArray, usage: Int): VBO {
|
||||||
|
return super.bufferData(data, usage) as VBO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,25 +3,27 @@ package ru.dbotthepony.kstarbound.client.gl
|
|||||||
import org.lwjgl.opengl.GL45.*
|
import org.lwjgl.opengl.GL45.*
|
||||||
|
|
||||||
enum class GLType(
|
enum class GLType(
|
||||||
val identity: Int,
|
val type: Int,
|
||||||
val typeIndentity: Int,
|
val elementType: Int,
|
||||||
val byteSize: Int,
|
val byteSize: Int,
|
||||||
val logicalSize: Int,
|
val elementSize: Int,
|
||||||
|
// location(position) width
|
||||||
|
val width: Int
|
||||||
) {
|
) {
|
||||||
INT(GL_INT, GL_INT, 4, 1),
|
INT(GL_INT, GL_INT, 4, 1, 1),
|
||||||
UINT(GL_UNSIGNED_INT, GL_UNSIGNED_INT, 4, 1),
|
UINT(GL_UNSIGNED_INT, GL_UNSIGNED_INT, 4, 1, 1),
|
||||||
FLOAT(GL_FLOAT, GL_FLOAT, 4, 1),
|
FLOAT(GL_FLOAT, GL_FLOAT, 4, 1, 1),
|
||||||
DOUBLE(GL_DOUBLE, GL_DOUBLE, 8, 1),
|
DOUBLE(GL_DOUBLE, GL_DOUBLE, 8, 1, 1),
|
||||||
|
|
||||||
VEC2F(GL_FLOAT_VEC2, GL_FLOAT, 8, 2),
|
VEC2F(GL_FLOAT_VEC2, GL_FLOAT, 8, 2, 1),
|
||||||
VEC3F(GL_FLOAT_VEC3, GL_FLOAT, 12, 3),
|
VEC3F(GL_FLOAT_VEC3, GL_FLOAT, 12, 3, 1),
|
||||||
VEC4F(GL_FLOAT_VEC4, GL_FLOAT, 16, 4),
|
VEC4F(GL_FLOAT_VEC4, GL_FLOAT, 16, 4, 1),
|
||||||
|
|
||||||
VEC2I(GL_INT_VEC2, GL_INT, 8, 2),
|
VEC2I(GL_INT_VEC2, GL_INT, 8, 2, 1),
|
||||||
VEC3I(GL_INT_VEC3, GL_INT, 12, 3),
|
VEC3I(GL_INT_VEC3, GL_INT, 12, 3, 1),
|
||||||
VEC4I(GL_INT_VEC4, GL_INT, 16, 4),
|
VEC4I(GL_INT_VEC4, GL_INT, 16, 4, 1),
|
||||||
|
|
||||||
MAT2F(GL_FLOAT_MAT2, GL_FLOAT, 2 * 2 * 4, 2 * 2),
|
MAT2F(GL_FLOAT_MAT2, GL_FLOAT, 2 * 2 * 4, 2 * 2, 2),
|
||||||
MAT3F(GL_FLOAT_MAT3, GL_FLOAT, 3 * 3 * 4, 3 * 3),
|
MAT3F(GL_FLOAT_MAT3, GL_FLOAT, 3 * 3 * 4, 3 * 3, 3),
|
||||||
MAT4F(GL_FLOAT_MAT4, GL_FLOAT, 4 * 4 * 4, 4 * 4),
|
MAT4F(GL_FLOAT_MAT4, GL_FLOAT, 4 * 4 * 4, 4 * 4, 4),
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,39 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
package ru.dbotthepony.kstarbound.client.gl
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||||
import org.lwjgl.opengl.GL45.*
|
import org.lwjgl.opengl.GL45.*
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
|
import java.util.BitSet
|
||||||
|
|
||||||
class VertexArrayObject : GLObject() {
|
class VertexArrayObject : GLObject() {
|
||||||
override val client = StarboundClient.current()
|
override val client = StarboundClient.current()
|
||||||
override val pointer = glGenVertexArrays()
|
override val pointer = glCreateVertexArrays()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError("Creating Vertex Array Object")
|
||||||
client.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
client.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val enabledAttributes = BitSet()
|
||||||
|
private val boundBuffers = Int2ObjectAVLTreeMap<BindConfig>()
|
||||||
|
private data class BindConfig(val buffer: BufferObject, val stride: Int, val offset: Long)
|
||||||
|
|
||||||
|
var elementBuffer: BufferObject.EBO? = null
|
||||||
|
set(value) {
|
||||||
|
client.ensureSameThread()
|
||||||
|
|
||||||
|
if (field != value) {
|
||||||
|
if (value == null)
|
||||||
|
glVertexArrayElementBuffer(pointer, 0)
|
||||||
|
else
|
||||||
|
glVertexArrayElementBuffer(pointer, value.pointer)
|
||||||
|
|
||||||
|
checkForGLError()
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun bind() {
|
override fun bind() {
|
||||||
client.vao = this
|
client.vao = this
|
||||||
}
|
}
|
||||||
@ -21,18 +43,89 @@ class VertexArrayObject : GLObject() {
|
|||||||
client.vao = null
|
client.vao = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attribute(position: Int, size: Int, type: Int, normalize: Boolean, stride: Int, offset: Long = 0L): VertexArrayObject {
|
fun bindBufferToIndex(buffer: BufferObject.VBO, index: Int, vertexStride: Int, offsetFromBeginning: Long = 0L): VertexArrayObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glVertexAttribPointer(position, size, type, normalize, stride, offset)
|
require(index >= 0) { "Invalid bind index: $index" }
|
||||||
checkForGLError()
|
require(index < client.maxVertexAttribBindPoints) { "Bind index is too big: $index; Max ${client.maxVertexAttribBindPoints}" }
|
||||||
|
val config = BindConfig(buffer, vertexStride, offsetFromBeginning)
|
||||||
|
|
||||||
|
if (boundBuffers[index] != config) {
|
||||||
|
glVertexArrayVertexBuffer(pointer, index, buffer.pointer, offsetFromBeginning, vertexStride)
|
||||||
|
checkForGLError()
|
||||||
|
boundBuffers[index] = config
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unbindBufferFromIndex(index: Int): VertexArrayObject {
|
||||||
|
client.ensureSameThread()
|
||||||
|
require(index >= 0) { "Invalid attribute index: $index" }
|
||||||
|
require(index < client.maxVertexAttribBindPoints) { "Bind index is too big: $index; Max ${client.maxVertexAttribBindPoints}" }
|
||||||
|
|
||||||
|
if (index in boundBuffers) {
|
||||||
|
glVertexArrayVertexBuffer(pointer, index, 0, 0L, 0)
|
||||||
|
checkForGLError()
|
||||||
|
boundBuffers.remove(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bindAttributeToIndex(attrib: Int, index: Int): VertexArrayObject {
|
||||||
|
client.ensureSameThread()
|
||||||
|
|
||||||
|
require(attrib >= 0) { "Invalid attribute index: $attrib" }
|
||||||
|
require(index >= 0) { "Invalid bind index: $index" }
|
||||||
|
require(index < client.maxVertexAttribBindPoints) { "Bind index is too big: $index; Max ${client.maxVertexAttribBindPoints}" }
|
||||||
|
|
||||||
|
glVertexArrayAttribBinding(pointer, attrib, index)
|
||||||
|
checkForGLError()
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attributeFormat(attrib: Int, type: GLType, normalized: Boolean, relativeOffset: Int): VertexArrayObject {
|
||||||
|
client.ensureSameThread()
|
||||||
|
require(attrib >= 0) { "Invalid attribute index: $attrib" }
|
||||||
|
|
||||||
|
glVertexArrayAttribFormat(pointer, attrib, type.elementSize, type.elementType, normalized, relativeOffset)
|
||||||
|
checkForGLError()
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bindAttributes(buffer: BufferObject.VBO, attributes: VertexAttributes, bufferOffset: Long = 0L) {
|
||||||
|
bindBufferToIndex(buffer, 0, attributes.vertexStride, bufferOffset)
|
||||||
|
|
||||||
|
for (attr in attributes.attributeList) {
|
||||||
|
bindAttributeToIndex(attr.index, 0)
|
||||||
|
attributeFormat(attr.index, attr.type.type, false, attr.relativeOffset)
|
||||||
|
enableAttribute(attr.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun enableAttribute(position: Int): VertexArrayObject {
|
fun enableAttribute(position: Int): VertexArrayObject {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glEnableVertexArrayAttrib(pointer, position)
|
|
||||||
//glEnableVertexAttribArray(position)
|
if (!enabledAttributes[position]) {
|
||||||
checkForGLError()
|
glEnableVertexArrayAttrib(pointer, position)
|
||||||
|
checkForGLError()
|
||||||
|
enabledAttributes[position] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disableAttribute(position: Int): VertexArrayObject {
|
||||||
|
client.ensureSameThread()
|
||||||
|
|
||||||
|
if (enabledAttributes[position]) {
|
||||||
|
glDisableVertexArrayAttrib(pointer, position)
|
||||||
|
checkForGLError()
|
||||||
|
enabledAttributes[position] = false
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.opengl.GL20.GL_COMPILE_STATUS
|
import org.lwjgl.opengl.GL20.GL_COMPILE_STATUS
|
||||||
import org.lwjgl.opengl.GL20.glCompileShader
|
import org.lwjgl.opengl.GL20.glCompileShader
|
||||||
import org.lwjgl.opengl.GL20.glCreateShader
|
import org.lwjgl.opengl.GL20.glCreateShader
|
||||||
@ -33,9 +34,20 @@ class GLShader(body: String, type: Int) {
|
|||||||
glGetShaderiv(pointer, GL_COMPILE_STATUS, result)
|
glGetShaderiv(pointer, GL_COMPILE_STATUS, result)
|
||||||
|
|
||||||
if (result[0] == 0) {
|
if (result[0] == 0) {
|
||||||
|
val split = body.split("\n")
|
||||||
|
val reps = if (split.size < 10) 1 else if (split.size < 100) 2 else if (split.size < 1000) 3 else 4
|
||||||
|
|
||||||
|
LOGGER.fatal("Next shader source has failed to compile, with line numbers:\n${split.withIndex().map {
|
||||||
|
"${it.index}${" ".repeat((reps - it.index.toString().length).coerceAtLeast(0))}: ${it.value}"
|
||||||
|
}.joinToString("\n")}")
|
||||||
|
|
||||||
throw ShaderCompilationException(glGetShaderInfoLog(pointer))
|
throw ShaderCompilationException(glGetShaderInfoLog(pointer))
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import org.lwjgl.opengl.GL45.*
|
|||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLObject
|
import ru.dbotthepony.kstarbound.client.gl.GLObject
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
import ru.dbotthepony.kvector.api.IStruct2f
|
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
|
||||||
@ -26,8 +26,15 @@ import kotlin.reflect.KProperty
|
|||||||
|
|
||||||
open class GLShaderProgram(
|
open class GLShaderProgram(
|
||||||
shaders: Iterable<GLShader>,
|
shaders: Iterable<GLShader>,
|
||||||
val attributes: GLAttributeList
|
val attributes: VertexAttributes
|
||||||
) : GLObject() {
|
) : GLObject() {
|
||||||
|
interface Regular {
|
||||||
|
var viewMatrix: Matrix3f // set before rendering anything (camera and/or projection)
|
||||||
|
var worldMatrix: Matrix3f // global matrix stack
|
||||||
|
var modelMatrix: Matrix3f // should be set by drawing code itself
|
||||||
|
var colorMultiplier: IStruct4f
|
||||||
|
}
|
||||||
|
|
||||||
final override val client = StarboundClient.current()
|
final override val client = StarboundClient.current()
|
||||||
final override val pointer = glCreateProgram()
|
final override val pointer = glCreateProgram()
|
||||||
|
|
||||||
@ -42,14 +49,12 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
glLinkProgram(pointer)
|
glLinkProgram(pointer)
|
||||||
|
|
||||||
val success = intArrayOf(0)
|
if (glGetProgrami(pointer, GL_LINK_STATUS) == 0)
|
||||||
glGetProgramiv(pointer, GL_LINK_STATUS, success)
|
|
||||||
|
|
||||||
if (success[0] == 0) {
|
|
||||||
throw ShaderLinkException(glGetProgramInfoLog(pointer))
|
throw ShaderLinkException(glGetProgramInfoLog(pointer))
|
||||||
}
|
|
||||||
|
|
||||||
glGetError()
|
glGetError()
|
||||||
|
|
||||||
|
client.addShaderProgram(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun use(): GLShaderProgram {
|
fun use(): GLShaderProgram {
|
||||||
@ -82,16 +87,17 @@ open class GLShaderProgram(
|
|||||||
return locations.computeIfAbsent(name, Object2ObjectFunction { IUniform(name) }) as? IUniform ?: throw IllegalStateException("Uniform $name has type of ${locations[name]!!::class.simpleName}")
|
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> {
|
abstract inner class Uniform<V : Any>(val name: String, allowMissing: Boolean = false) : ReadWriteProperty<Any?, V> {
|
||||||
init {
|
init {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
require(!locations.containsKey(name)) { "Already has uniform $name for ${this@GLShaderProgram} (${locations[name]})" }
|
require(!locations.containsKey(name)) { "Already has uniform $name for ${this@GLShaderProgram} (${locations[name]})" }
|
||||||
}
|
}
|
||||||
|
|
||||||
val location = glGetUniformLocation(pointer, name)
|
val location = glGetUniformLocation(pointer, name)
|
||||||
|
val isMissing = location == -1
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (location == -1)
|
if (isMissing && !allowMissing)
|
||||||
throw NoSuchElementException("Program ${this@GLShaderProgram} does not have uniform with name $name")
|
throw NoSuchElementException("Program ${this@GLShaderProgram} does not have uniform with name $name")
|
||||||
|
|
||||||
locations[name] = this
|
locations[name] = this
|
||||||
@ -113,10 +119,11 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class FUniform(name: String) : Uniform<Float>(name) {
|
inner class FUniform(name: String, allowMissing: Boolean = false) : Uniform<Float>(name, allowMissing) {
|
||||||
override var value: Float = 0f
|
override var value: Float = 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
glProgramUniform1f(pointer, location, value)
|
glProgramUniform1f(pointer, location, value)
|
||||||
@ -135,13 +142,14 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class F2Uniform(name: String) : Uniform<IStruct2f>(name) {
|
inner class F2Uniform(name: String, allowMissing: Boolean = false) : Uniform<IStruct2f>(name, allowMissing) {
|
||||||
private var v0 = 0f
|
private var v0 = 0f
|
||||||
private var v1 = 0f
|
private var v1 = 0f
|
||||||
|
|
||||||
override var value: IStruct2f = Vector2f.ZERO
|
override var value: IStruct2f = Vector2f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
val (v0, v1) = value
|
val (v0, v1) = value
|
||||||
|
|
||||||
@ -156,7 +164,7 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class F3Uniform(name: String) : Uniform<IStruct3f>(name) {
|
inner class F3Uniform(name: String, allowMissing: Boolean = false) : Uniform<IStruct3f>(name, allowMissing) {
|
||||||
private var v0 = 0f
|
private var v0 = 0f
|
||||||
private var v1 = 0f
|
private var v1 = 0f
|
||||||
private var v2 = 0f
|
private var v2 = 0f
|
||||||
@ -164,6 +172,7 @@ open class GLShaderProgram(
|
|||||||
override var value: IStruct3f = Vector3f.ZERO
|
override var value: IStruct3f = Vector3f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
val (v0, v1, v2) = value
|
val (v0, v1, v2) = value
|
||||||
|
|
||||||
@ -182,10 +191,11 @@ open class GLShaderProgram(
|
|||||||
private val buff3x3: FloatBuffer = ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
private val buff3x3: FloatBuffer = ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
||||||
private val buff4x4: FloatBuffer = ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
private val buff4x4: FloatBuffer = ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
||||||
|
|
||||||
inner class F3x3Uniform(name: String) : Uniform<Matrix3f>(name) {
|
inner class F3x3Uniform(name: String, allowMissing: Boolean = false) : Uniform<Matrix3f>(name, allowMissing) {
|
||||||
override var value: Matrix3f = Matrix3f.zero()
|
override var value: Matrix3f = Matrix3f.zero()
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
buff3x3.position(0)
|
buff3x3.position(0)
|
||||||
@ -199,15 +209,16 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class F4x4Uniform(name: String) : Uniform<Matrix4f>(name) {
|
inner class F4x4Uniform(name: String, allowMissing: Boolean = false) : Uniform<Matrix4f>(name, allowMissing) {
|
||||||
private val _value = ByteBuffer.allocate(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
private val _value = ByteBuffer.allocate(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer()
|
||||||
|
|
||||||
override var value: Matrix4f = Matrix4f.zero()
|
override var value: Matrix4f = Matrix4f.zero()
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
buff4x4.position(0)
|
buff4x4.position(0)
|
||||||
value.storeRowColumn(buff4x4)
|
value.storeColumnRow(buff4x4)
|
||||||
buff4x4.position(0)
|
buff4x4.position(0)
|
||||||
_value.position(0)
|
_value.position(0)
|
||||||
|
|
||||||
@ -221,7 +232,7 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class F4Uniform(name: String) : Uniform<IStruct4f>(name) {
|
inner class F4Uniform(name: String, allowMissing: Boolean = false) : Uniform<IStruct4f>(name, allowMissing) {
|
||||||
private var v0 = 0f
|
private var v0 = 0f
|
||||||
private var v1 = 0f
|
private var v1 = 0f
|
||||||
private var v2 = 0f
|
private var v2 = 0f
|
||||||
@ -230,6 +241,7 @@ open class GLShaderProgram(
|
|||||||
override var value: IStruct4f = Vector4f.ZERO
|
override var value: IStruct4f = Vector4f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
val (v0, v1, v2, v3) = value
|
val (v0, v1, v2, v3) = value
|
||||||
|
|
||||||
@ -246,10 +258,11 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class IUniform(name: String) : Uniform<Int>(name) {
|
inner class IUniform(name: String, allowMissing: Boolean = false) : Uniform<Int>(name, allowMissing) {
|
||||||
override var value: Int = 0
|
override var value: Int = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
client.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
if (isMissing) return
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
glProgramUniform1i(pointer, location, value)
|
glProgramUniform1i(pointer, location, value)
|
||||||
|
@ -1,125 +1,50 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
|
||||||
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.GeometryType
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
|
||||||
private fun internalVertex(string: String) = StarboundClient.current().internalVertex(string)
|
|
||||||
private fun internalFragment(string: String) = StarboundClient.current().internalFragment(string)
|
|
||||||
|
|
||||||
private fun shaders(name: String): List<GLShader> {
|
private fun shaders(name: String): List<GLShader> {
|
||||||
val client = StarboundClient.current()
|
val client = StarboundClient.current()
|
||||||
return listOf(client.internalVertex("shaders/$name.vsh"), client.internalFragment("shaders/$name.fsh"))
|
return listOf(client.internalVertex("shaders/$name.vsh"), client.internalFragment("shaders/$name.fsh"))
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLLiquidProgram : GLShaderProgram(shaders("liquid"), FORMAT) {
|
private fun shaders1(name: String): List<GLShader> {
|
||||||
|
val client = StarboundClient.current()
|
||||||
|
return listOf(client.internalVertex("shaders/vertex/$name.vsh"), client.internalFragment("shaders/fragment/$name.fsh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
class FontProgram : GLShaderProgram(shaders1("font"), VertexAttributes.POSITION_UV), GLShaderProgram.Regular {
|
||||||
|
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix", true)
|
||||||
|
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix", true)
|
||||||
|
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix", true)
|
||||||
|
override var colorMultiplier: IStruct4f by F4Uniform("colorMultiplier", true)
|
||||||
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
|
val builder = StreamVertexBuilder(attributes, GeometryType.QUADS)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewMatrix = Matrix3f.identity()
|
||||||
|
modelMatrix = Matrix3f.identity()
|
||||||
|
colorMultiplier = RGBAColor.WHITE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLLiquidProgram : GLShaderProgram(shaders("liquid"), VertexAttributes.POSITION) {
|
||||||
var baselineColor by F4Uniform("baselineColor")
|
var baselineColor by F4Uniform("baselineColor")
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F3x3Uniform("transform")
|
||||||
|
val builder = StreamVertexBuilder(attributes, GeometryType.QUADS)
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(FORMAT, GeometryType.QUADS, 16384)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFlatColorProgram : GLShaderProgram(shaders("flat_color"), FORMAT) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(FORMAT, GeometryType.QUADS, 16384)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC4F).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTileProgram : GLShaderProgram(shaders("tile"), FORMAT) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
color = RGBAColor.WHITE
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFontProgram : GLShaderProgram(shaders("font"), GLAttributeList.VERTEX_2D_TEXTURE) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
color = RGBAColor.WHITE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFlatProgram : GLShaderProgram(shaders("flat"), GLAttributeList.VEC2F) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
|
|
||||||
init {
|
|
||||||
color = RGBAColor.WHITE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTexturedProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/texture.glsl"), internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTextured2dProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/2dtexture.glsl"), internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(GLAttributeList.Builder().push(GLType.VEC2F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTexturedColoredProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/texture.glsl"), internalFragment("shaders/fragment/texture_color.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
color = RGBAColor.WHITE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLPrograms {
|
class GLPrograms {
|
||||||
val tile = GLTileProgram()
|
val position = UberShader.Builder().build()
|
||||||
val font = GLFontProgram()
|
val positionTexture = UberShader.Builder().withTexture().build()
|
||||||
val flat = GLFlatProgram()
|
val positionColor = UberShader.Builder().withColor().build()
|
||||||
val flatColor = GLFlatColorProgram()
|
val tile = UberShader.Builder().withTexture().withHueShift().build()
|
||||||
|
val font = FontProgram()
|
||||||
val liquid = GLLiquidProgram()
|
val liquid = GLLiquidProgram()
|
||||||
val textured = GLTexturedProgram()
|
|
||||||
val textured2d = GLTextured2dProgram()
|
|
||||||
val texturedColored = GLTexturedColoredProgram()
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
|
import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER
|
||||||
|
import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributeType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
|
||||||
|
class UberShader private constructor(fragment: GLShader, vertex: GLShader, attributes: VertexAttributes) : GLShaderProgram(listOf(fragment, vertex), attributes), GLShaderProgram.Regular {
|
||||||
|
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix")
|
||||||
|
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix")
|
||||||
|
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix")
|
||||||
|
override var colorMultiplier: IStruct4f by F4Uniform("colorMultiplier")
|
||||||
|
|
||||||
|
var texture0 by IUniform("texture0", VertexAttributeType.UV !in attributes)
|
||||||
|
|
||||||
|
val builder = StreamVertexBuilder(attributes)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewMatrix = Matrix3f.identity()
|
||||||
|
worldMatrix = Matrix3f.identity()
|
||||||
|
modelMatrix = Matrix3f.identity()
|
||||||
|
colorMultiplier = RGBAColor.WHITE
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
private val directives = Object2ObjectArrayMap<String, String>()
|
||||||
|
private val attributes = ObjectArraySet<VertexAttributeType>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
attributes.add(VertexAttributeType.POSITION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var nextAttributeIndex = 1
|
||||||
|
|
||||||
|
private fun attribute(type: VertexAttributeType, name: String = type.name): Builder {
|
||||||
|
if (attributes.add(type)) {
|
||||||
|
directives[name] = nextAttributeIndex.toString()
|
||||||
|
nextAttributeIndex += type.type.width
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withTexture(): Builder {
|
||||||
|
return attribute(VertexAttributeType.UV, "TEXTURE")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withColor(): Builder {
|
||||||
|
return attribute(VertexAttributeType.COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withHueShift(): Builder {
|
||||||
|
return attribute(VertexAttributeType.HUE_SHIFT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): UberShader {
|
||||||
|
val client = StarboundClient.current()
|
||||||
|
|
||||||
|
val fragment = GLShader("#version 450\n" +
|
||||||
|
directives.entries.joinToString("\n") { "#define ${it.key}" + (if (it.value != "") " " + it.value else "") } +
|
||||||
|
fragment, GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
|
val vertex = GLShader("#version 450\n" +
|
||||||
|
directives.entries.joinToString("\n") { "#define ${it.key}" + (if (it.value != "") " " + it.value else "") } +
|
||||||
|
vertex, GL_VERTEX_SHADER)
|
||||||
|
|
||||||
|
val attributes = VertexAttributes.of(attributes)
|
||||||
|
|
||||||
|
return UberShader(fragment, vertex, attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val fragment by lazy { StarboundClient.readInternal("shaders/fragment/configurable.fsh") }
|
||||||
|
private val vertex by lazy { StarboundClient.readInternal("shaders/vertex/configurable.vsh") }
|
||||||
|
}
|
||||||
|
}
|
@ -1,89 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.vertex
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Хранит список аттрибутов для применения к Vertex Array Object
|
|
||||||
*
|
|
||||||
* Аттрибуты плотно упакованы и идут один за другим
|
|
||||||
*
|
|
||||||
* Создаётся через [GLAttributeList.Builder]
|
|
||||||
*/
|
|
||||||
class GLAttributeList(builder: Builder) {
|
|
||||||
data class Attribute(val name: String, val index: Int, val glType: GLType, val stride: Int, val offset: Long)
|
|
||||||
|
|
||||||
val attributes: List<Attribute>
|
|
||||||
val size get() = attributes.size
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Шаг данных аттрибутов, в байтах. Т.е. одна полная вершина будет занимать [stride] байт в памяти.
|
|
||||||
*/
|
|
||||||
val stride: Int
|
|
||||||
|
|
||||||
operator fun get(index: Int) = attributes[index]
|
|
||||||
|
|
||||||
init {
|
|
||||||
val buildList = ArrayList<Attribute>()
|
|
||||||
|
|
||||||
var offset = 0L
|
|
||||||
var stride = 0
|
|
||||||
|
|
||||||
for (i in builder.attributes) {
|
|
||||||
stride += i.second.byteSize
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stride = stride
|
|
||||||
|
|
||||||
for (i in builder.attributes.indices) {
|
|
||||||
val value = builder.attributes[i].second
|
|
||||||
buildList.add(Attribute(builder.attributes[i].first, i, value, stride, offset))
|
|
||||||
offset += value.byteSize
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes = ImmutableList.copyOf(buildList)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Применяет список атрибутов к заданному [VertexArrayObject] (попутно включая или отключая их через [enable])
|
|
||||||
*/
|
|
||||||
fun apply(target: VertexArrayObject, enable: Boolean) {
|
|
||||||
for (i in attributes.indices) {
|
|
||||||
val value = attributes[i]
|
|
||||||
target.attribute(i, value.glType.logicalSize, value.glType.typeIndentity, false, value.stride, value.offset)
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
target.enableAttribute(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Builder {
|
|
||||||
val attributes = ArrayList<Pair<String, GLType>>()
|
|
||||||
|
|
||||||
fun push(type: GLType): Builder {
|
|
||||||
return push("$type#${attributes.size}", type)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(vararg types: GLType): Builder {
|
|
||||||
for (type in types) push(type)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(name: String, type: GLType): Builder {
|
|
||||||
check(!attributes.any { it.first == name }) { "Already has named attribute $name!" }
|
|
||||||
attributes.add(name to type)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build() = GLAttributeList(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val VEC2F = Builder().push(GLType.VEC2F).build()
|
|
||||||
val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
|
||||||
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,15 +17,10 @@ enum class GeometryType(
|
|||||||
LINES(2, IntList.of(0, 1)),
|
LINES(2, IntList.of(0, 1)),
|
||||||
TRIANGLES(3, IntList.of(0, 1, 2)),
|
TRIANGLES(3, IntList.of(0, 1, 2)),
|
||||||
|
|
||||||
/**
|
|
||||||
* A B C B C D
|
|
||||||
*/
|
|
||||||
QUADS(4, IntList.of(0, 1, 2, 1, 2, 3)),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A B C C D A
|
* A B C C D A
|
||||||
*/
|
*/
|
||||||
QUADS_ALTERNATIVE(4, IntList.of(0, 1, 2, 2, 3, 0)),
|
QUADS(4, IntList.of(0, 1, 2, 2, 3, 0)),
|
||||||
|
|
||||||
QUADS_AS_LINES(4, IntList.of(0, 1, 0, 2, 1, 3, 2, 3)),
|
QUADS_AS_LINES(4, IntList.of(0, 1, 0, 2, 1, 3, 2, 3)),
|
||||||
QUADS_AS_LINES_WIREFRAME(4, IntList.of(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
QUADS_AS_LINES_WIREFRAME(4, IntList.of(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.vertex
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.IUVCoordinates
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.UVCoordinates
|
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
|
||||||
|
|
||||||
typealias QuadVertexTransformer = (VertexBuilder, Int) -> VertexBuilder
|
|
||||||
|
|
||||||
val EMPTY_VERTEX_TRANSFORM: QuadVertexTransformer = { it, _ -> it }
|
|
||||||
|
|
||||||
fun QuadVertexTransformer.before(other: QuadVertexTransformer): QuadVertexTransformer {
|
|
||||||
return { a, b ->
|
|
||||||
other.invoke(a, b)
|
|
||||||
this.invoke(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun QuadVertexTransformer.after(other: QuadVertexTransformer): QuadVertexTransformer {
|
|
||||||
return { a, b ->
|
|
||||||
this.invoke(a, b)
|
|
||||||
other.invoke(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object QuadTransformers {
|
|
||||||
fun uv(u0: Float,
|
|
||||||
v0: Float,
|
|
||||||
u1: Float,
|
|
||||||
v1: Float,
|
|
||||||
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
|
||||||
): QuadVertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
when (index) {
|
|
||||||
0 -> it.pushVec2f(u0, v0)
|
|
||||||
1 -> it.pushVec2f(u1, v0)
|
|
||||||
2 -> it.pushVec2f(u0, v1)
|
|
||||||
3 -> it.pushVec2f(u1, v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@transformer lambda(it, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uv(): QuadVertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
when (index) {
|
|
||||||
0 -> it.pushVec2f(0f, 0f)
|
|
||||||
1 -> it.pushVec2f(1f, 0f)
|
|
||||||
2 -> it.pushVec2f(0f, 1f)
|
|
||||||
3 -> it.pushVec2f(1f, 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@transformer it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uv(uv: IUVCoordinates): QuadVertexTransformer {
|
|
||||||
return uv(uv.u0, uv.v0, uv.u1, uv.v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uv(uv: IUVCoordinates, lambda: QuadVertexTransformer): QuadVertexTransformer {
|
|
||||||
return uv(uv.u0, uv.v0, uv.u1, uv.v1, lambda)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun vec4(x: Float, y: Float, z: Float, w: Float, after: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): QuadVertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
it.pushVec4f(x, y, z, w)
|
|
||||||
return@transformer after(it, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,8 +10,8 @@ import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
|||||||
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
||||||
*/
|
*/
|
||||||
class StreamVertexBuilder(
|
class StreamVertexBuilder(
|
||||||
attributes: GLAttributeList,
|
attributes: VertexAttributes,
|
||||||
type: GeometryType,
|
type: GeometryType? = null,
|
||||||
initialCapacity: Int = 64,
|
initialCapacity: Int = 64,
|
||||||
) {
|
) {
|
||||||
val state = StarboundClient.current()
|
val state = StarboundClient.current()
|
||||||
@ -21,15 +21,8 @@ class StreamVertexBuilder(
|
|||||||
private val ebo = BufferObject.EBO()
|
private val ebo = BufferObject.EBO()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
vao.bind()
|
vao.elementBuffer = ebo
|
||||||
vbo.bind()
|
vao.bindAttributes(vbo, attributes)
|
||||||
ebo.bind()
|
|
||||||
|
|
||||||
attributes.apply(vao, true)
|
|
||||||
|
|
||||||
vao.unbind()
|
|
||||||
vbo.unbind()
|
|
||||||
ebo.unbind()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upload(drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
fun upload(drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
||||||
@ -43,22 +36,6 @@ class StreamVertexBuilder(
|
|||||||
bind()
|
bind()
|
||||||
GL45.glDrawElements(primitives, builder.indexCount, builder.indexType, 0L)
|
GL45.glDrawElements(primitives, builder.indexCount, builder.indexType, 0L)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
}
|
unbind()
|
||||||
|
|
||||||
fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
|
||||||
builder.begin()
|
|
||||||
|
|
||||||
builder.quadRotatedZ(x, y, width, height, z, 0f, 0f, angle, transformer)
|
|
||||||
|
|
||||||
upload()
|
|
||||||
draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun singleSprite(width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
|
||||||
singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, z, angle, transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun singleSprite(width: Float, height: Float, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
|
||||||
singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, 0f, angle, transformer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
|
|
||||||
|
enum class VertexAttributeType(val type: GLType) {
|
||||||
|
POSITION(GLType.VEC2F),
|
||||||
|
COLOR(GLType.VEC4F),
|
||||||
|
UV(GLType.VEC2F),
|
||||||
|
HUE_SHIFT(GLType.FLOAT)
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
|
import java.util.Collections
|
||||||
|
import java.util.EnumMap
|
||||||
|
import java.util.EnumSet
|
||||||
|
|
||||||
|
class VertexAttributes private constructor(
|
||||||
|
val attributeList: ImmutableList<Attribute>,
|
||||||
|
val attributeMap: Map<VertexAttributeType, Attribute>,
|
||||||
|
val vertexStride: Int,
|
||||||
|
val allFlags: Long,
|
||||||
|
) {
|
||||||
|
data class Attribute(val type: VertexAttributeType, val index: Int, val relativeOffset: Int, val flag: Long)
|
||||||
|
|
||||||
|
val size get() = attributeList.size
|
||||||
|
operator fun get(index: Int): Attribute = attributeList[index]
|
||||||
|
operator fun get(index: VertexAttributeType): Attribute? = attributeMap[index]
|
||||||
|
|
||||||
|
operator fun contains(index: VertexAttributeType) = index in attributeMap
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val POSITION = of(VertexAttributeType.POSITION)
|
||||||
|
val POSITION_UV = of(VertexAttributeType.POSITION, VertexAttributeType.UV)
|
||||||
|
val POSITION_COLOR = of(VertexAttributeType.POSITION, VertexAttributeType.COLOR)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun of(vararg attributes: VertexAttributeType): VertexAttributes {
|
||||||
|
return of(ObjectArrayList.wrap(attributes))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun of(attributes: Collection<VertexAttributeType>): VertexAttributes {
|
||||||
|
val dup = EnumSet.noneOf(VertexAttributeType::class.java)
|
||||||
|
|
||||||
|
attributes.forEach {
|
||||||
|
if (!dup.add(it)) {
|
||||||
|
throw IllegalArgumentException("Duplicate vertex attribute: $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require(attributes.size < 64) { "Vertex attribute list is insanely big: ${attributes.size}; 64 is max" }
|
||||||
|
|
||||||
|
val attributeList = ImmutableList.Builder<Attribute>()
|
||||||
|
val attributeMap = EnumMap<VertexAttributeType, Attribute>(VertexAttributeType::class.java)
|
||||||
|
var allVertexFlags = 0L
|
||||||
|
|
||||||
|
var stride = 0
|
||||||
|
|
||||||
|
for (attr in attributes) {
|
||||||
|
stride += attr.type.byteSize
|
||||||
|
allVertexFlags = (allVertexFlags shl 1) or 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
var index = 0
|
||||||
|
|
||||||
|
for ((i, attr) in attributes.withIndex()) {
|
||||||
|
val constructed = Attribute(attr, index, offset, 1L shl i)
|
||||||
|
index += attr.type.width
|
||||||
|
attributeList.add(constructed)
|
||||||
|
offset += attr.type.byteSize
|
||||||
|
attributeMap[attr] = constructed
|
||||||
|
}
|
||||||
|
|
||||||
|
return VertexAttributes(attributeList.build(), Collections.unmodifiableMap(attributeMap), stride, allVertexFlags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,11 @@ import org.lwjgl.opengl.GL45
|
|||||||
import org.lwjgl.opengl.GL45.GL_UNSIGNED_INT
|
import org.lwjgl.opengl.GL45.GL_UNSIGNED_INT
|
||||||
import org.lwjgl.opengl.GL45.GL_UNSIGNED_SHORT
|
import org.lwjgl.opengl.GL45.GL_UNSIGNED_SHORT
|
||||||
import org.lwjgl.opengl.GL45.GL_UNSIGNED_BYTE
|
import org.lwjgl.opengl.GL45.GL_UNSIGNED_BYTE
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.BufferObject
|
import ru.dbotthepony.kstarbound.client.gl.BufferObject
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.api.IStruct2f
|
||||||
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
private fun interface IndexWriter {
|
private fun interface IndexWriter {
|
||||||
fun write(buffer: ByteBuffer, value: Int)
|
fun write(buffer: ByteBuffer, value: Int)
|
||||||
@ -50,11 +48,11 @@ private fun indexSize(type: Int): Int {
|
|||||||
* Загрузка в память видеокарты происходит напрямую из буферов, через метод [upload]
|
* Загрузка в память видеокарты происходит напрямую из буферов, через метод [upload]
|
||||||
*/
|
*/
|
||||||
class VertexBuilder(
|
class VertexBuilder(
|
||||||
val attributes: GLAttributeList,
|
val attributes: VertexAttributes,
|
||||||
val defaultMode: GeometryType? = null,
|
val defaultMode: GeometryType? = null,
|
||||||
initialCapacity: Int = 64
|
initialCapacity: Int = 64
|
||||||
) {
|
) {
|
||||||
constructor(attributes: GLAttributeList, initialCapacity: Int) : this(attributes, null, initialCapacity)
|
constructor(attributes: VertexAttributes, initialCapacity: Int) : this(attributes, null, initialCapacity)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(initialCapacity > 0) { "Invalid capacity: $initialCapacity" }
|
require(initialCapacity > 0) { "Invalid capacity: $initialCapacity" }
|
||||||
@ -73,14 +71,13 @@ class VertexBuilder(
|
|||||||
var indexSize: Int = indexSize(indexType)
|
var indexSize: Int = indexSize(indexType)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private var vertexMemory = ByteBuffer.allocateDirect(vertexCapacity * attributes.stride).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
private var vertexMemory: ByteBuffer = ByteBuffer.allocateDirect(vertexCapacity * attributes.vertexStride).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
||||||
private var indexMemory = ByteBuffer.allocateDirect(indexCapacity * indexSize).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
private var indexMemory: ByteBuffer = ByteBuffer.allocateDirect(indexCapacity * indexSize).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
||||||
private var indexWriter = writer(indexType)
|
private var indexWriter = writer(indexType)
|
||||||
|
|
||||||
private var inVertex = false
|
private var inVertex = false
|
||||||
private var attributeIndex = 0
|
private var vertexAttributes = 0L
|
||||||
private var elementIndexOffset = 0
|
private var elementIndexOffset = 0
|
||||||
|
|
||||||
private var elementVertices = 0
|
private var elementVertices = 0
|
||||||
|
|
||||||
var vertexCount = 0 // distinct vertices
|
var vertexCount = 0 // distinct vertices
|
||||||
@ -113,7 +110,7 @@ class VertexBuilder(
|
|||||||
val indexSize = indexSize(indexType)
|
val indexSize = indexSize(indexType)
|
||||||
|
|
||||||
val vertexPos = vertexMemory.position()
|
val vertexPos = vertexMemory.position()
|
||||||
val vertexMemory = ByteBuffer.allocateDirect(vertexCapacity * attributes.stride).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
val vertexMemory = ByteBuffer.allocateDirect(vertexCapacity * attributes.vertexStride).also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
||||||
|
|
||||||
this.vertexMemory.position(0)
|
this.vertexMemory.position(0)
|
||||||
vertexMemory.put(this.vertexMemory)
|
vertexMemory.put(this.vertexMemory)
|
||||||
@ -171,7 +168,13 @@ class VertexBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun mode(mode: GeometryType): VertexBuilder {
|
fun mode(mode: GeometryType): VertexBuilder {
|
||||||
check(!inVertex) { "Can't change buffer geometry type during vertex construction" }
|
if (inVertex) {
|
||||||
|
if (vertexAttributes != attributes.allFlags)
|
||||||
|
throw IllegalStateException("Can't change buffer geometry type during vertex construction")
|
||||||
|
else
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
|
||||||
check(elementVertices == 0) { "Can't change buffer geometry type while not having fully built current element" }
|
check(elementVertices == 0) { "Can't change buffer geometry type while not having fully built current element" }
|
||||||
this.mode = mode
|
this.mode = mode
|
||||||
ensureCapacity()
|
ensureCapacity()
|
||||||
@ -183,8 +186,8 @@ class VertexBuilder(
|
|||||||
fun begin(mode: GeometryType? = defaultMode): VertexBuilder {
|
fun begin(mode: GeometryType? = defaultMode): VertexBuilder {
|
||||||
this.mode = mode
|
this.mode = mode
|
||||||
inVertex = false
|
inVertex = false
|
||||||
attributeIndex = 0
|
|
||||||
elementIndexOffset = 0
|
elementIndexOffset = 0
|
||||||
|
vertexAttributes = 0L
|
||||||
vertexCount = 0
|
vertexCount = 0
|
||||||
indexCount = 0
|
indexCount = 0
|
||||||
elementCount = 0
|
elementCount = 0
|
||||||
@ -194,33 +197,7 @@ class VertexBuilder(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkBounds() {
|
fun upload(vertices: BufferObject.VBO, elements: BufferObject.EBO, drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
||||||
if (attributeIndex >= attributes.size) {
|
|
||||||
throw IndexOutOfBoundsException("Tried to add new attribute when already added all attributes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun expect(type: GLType): VertexBuilder {
|
|
||||||
checkBounds()
|
|
||||||
|
|
||||||
if (attributes[attributeIndex].glType != type) {
|
|
||||||
throw IllegalStateException("Expected attribute type $type, got ${attributes[attributeIndex].name}[${attributes[attributeIndex].glType}] (at position $attributeIndex)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun expect(name: String): VertexBuilder {
|
|
||||||
checkBounds()
|
|
||||||
|
|
||||||
if (attributes[attributeIndex].name != name) {
|
|
||||||
throw IllegalStateException("Expected attribute name $name, got ${attributes[attributeIndex].name}[${attributes[attributeIndex].glType}] (at position $attributeIndex)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun upload(vbo: BufferObject.VBO, ebo: BufferObject.EBO, drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
|
||||||
end()
|
end()
|
||||||
|
|
||||||
check(elementVertices == 0) { "Not fully built vertex element ($mode requires ${mode?.elements} vertex points to be present, yet last strip has only $elementVertices elements)" }
|
check(elementVertices == 0) { "Not fully built vertex element ($mode requires ${mode?.elements} vertex points to be present, yet last strip has only $elementVertices elements)" }
|
||||||
@ -231,8 +208,8 @@ class VertexBuilder(
|
|||||||
vertexMemory.position(0)
|
vertexMemory.position(0)
|
||||||
indexMemory.position(0)
|
indexMemory.position(0)
|
||||||
|
|
||||||
vbo.bufferData(vertexMemory, drawType, length = vertexPos.toLong())
|
vertices.bufferData(vertexMemory, drawType, length = vertexPos.toLong())
|
||||||
ebo.bufferData(indexMemory, drawType, length = elementPos.toLong())
|
elements.bufferData(indexMemory, drawType, length = elementPos.toLong())
|
||||||
|
|
||||||
vertexMemory.position(vertexPos)
|
vertexMemory.position(vertexPos)
|
||||||
indexMemory.position(elementPos)
|
indexMemory.position(elementPos)
|
||||||
@ -244,8 +221,11 @@ class VertexBuilder(
|
|||||||
val mode = mode!!
|
val mode = mode!!
|
||||||
inVertex = false
|
inVertex = false
|
||||||
|
|
||||||
if (attributeIndex != attributes.size) {
|
if (vertexAttributes != attributes.allFlags) {
|
||||||
throw IllegalStateException("Unfinished vertex, we are at $attributeIndex, while we have ${attributes.size} attributes")
|
val missing = ArrayList<VertexAttributeType>()
|
||||||
|
attributes.attributeList.forEach { if (it.flag and vertexAttributes == 0L) missing.add(it.type) }
|
||||||
|
|
||||||
|
throw IllegalStateException("Unfinished vertex, missing ${missing.joinToString()} attributes")
|
||||||
}
|
}
|
||||||
|
|
||||||
vertexCount++
|
vertexCount++
|
||||||
@ -271,171 +251,63 @@ class VertexBuilder(
|
|||||||
checkNotNull(mode) { "No builder mode is set" }
|
checkNotNull(mode) { "No builder mode is set" }
|
||||||
|
|
||||||
inVertex = true
|
inVertex = true
|
||||||
attributeIndex = 0
|
vertexAttributes = 0L
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pushVec4f(x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
fun vertex(x: Float, y: Float): VertexBuilder {
|
||||||
expect(GLType.VEC4F)
|
|
||||||
val memory = vertexMemory
|
|
||||||
memory.putFloat(x)
|
|
||||||
memory.putFloat(y)
|
|
||||||
memory.putFloat(z)
|
|
||||||
memory.putFloat(w)
|
|
||||||
attributeIndex++
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pushVec3f(x: Float, y: Float, z: Float): VertexBuilder {
|
|
||||||
expect(GLType.VEC3F)
|
|
||||||
val memory = vertexMemory
|
|
||||||
memory.putFloat(x)
|
|
||||||
memory.putFloat(y)
|
|
||||||
memory.putFloat(z)
|
|
||||||
attributeIndex++
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pushVec2f(x: Float, y: Float): VertexBuilder {
|
|
||||||
expect(GLType.VEC2F)
|
|
||||||
val memory = vertexMemory
|
|
||||||
memory.putFloat(x)
|
|
||||||
memory.putFloat(y)
|
|
||||||
attributeIndex++
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(value: Float): VertexBuilder {
|
|
||||||
expect(GLType.FLOAT)
|
|
||||||
vertexMemory.putFloat(value)
|
|
||||||
attributeIndex++
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun shadowLine(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder {
|
|
||||||
vertex()
|
vertex()
|
||||||
pushVec4f(x0, y0, x1, y1)
|
position(x, y)
|
||||||
pushVec2f(0f, 0f)
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
vertex()
|
private fun pushFloat(attr: VertexAttributes.Attribute, x: Float): VertexBuilder {
|
||||||
pushVec4f(x0, y0, x1, y1)
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
pushVec2f(0f, 1f)
|
vertexMemory.putFloat(x)
|
||||||
|
vertexAttributes = vertexAttributes or attr.flag
|
||||||
vertex()
|
|
||||||
pushVec4f(x0, y0, x1, y1)
|
|
||||||
pushVec2f(1f, 1f)
|
|
||||||
|
|
||||||
vertex()
|
|
||||||
pushVec4f(x0, y0, x1, y1)
|
|
||||||
pushVec2f(1f, 0f)
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dfShadowLine(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder {
|
private fun pushFloat2(attr: VertexAttributes.Attribute, x: Float, y: Float): VertexBuilder {
|
||||||
shadowLine(x0, y0, x1, y1)
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
shadowLine(x1, y1, x0, y0)
|
vertexMemory.putFloat(x)
|
||||||
return this
|
vertexMemory.putFloat(y)
|
||||||
}
|
vertexAttributes = vertexAttributes or attr.flag
|
||||||
|
|
||||||
fun shadowQuad(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder {
|
|
||||||
shadowLine(x0, y0, x1, y0)
|
|
||||||
shadowLine(x1, y0, x1, y1)
|
|
||||||
shadowLine(x1, y1, x0, y1)
|
|
||||||
shadowLine(x0, y1, x0, y0)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
// Помощники
|
|
||||||
fun quad(
|
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
|
||||||
): VertexBuilder {
|
|
||||||
check(mode?.elements == 4) { "Currently building $mode" }
|
|
||||||
|
|
||||||
lambda(vertex().pushVec2f(x0, y0), 0).end()
|
|
||||||
lambda(vertex().pushVec2f(x1, y0), 1).end()
|
|
||||||
lambda(vertex().pushVec2f(x0, y1), 2).end()
|
|
||||||
lambda(vertex().pushVec2f(x1, y1), 3).end()
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun quadRotated(
|
private fun pushFloat4(attr: VertexAttributes.Attribute, x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
||||||
x0: Float,
|
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||||
y0: Float,
|
vertexMemory.putFloat(x)
|
||||||
x1: Float,
|
vertexMemory.putFloat(y)
|
||||||
y1: Float,
|
vertexMemory.putFloat(z)
|
||||||
x: Float,
|
vertexMemory.putFloat(w)
|
||||||
y: Float,
|
vertexAttributes = vertexAttributes or attr.flag
|
||||||
angle: Double,
|
|
||||||
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
|
||||||
): VertexBuilder {
|
|
||||||
check(mode?.elements == 4) { "Currently building $mode" }
|
|
||||||
|
|
||||||
val s = sin(angle).toFloat()
|
|
||||||
val c = cos(angle).toFloat()
|
|
||||||
|
|
||||||
lambda(vertex().pushVec2f(x + x0 * c - s * y0, y + s * x0 + c * y0), 0).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x1 * c - s * y0, y + s * x1 + c * y0), 1).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x0 * c - s * y1, y + s * x0 + c * y1), 2).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x1 * c - s * y1, y + s * x1 + c * y1), 3).end()
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): VertexBuilder {
|
fun position(x: Float, y: Float): VertexBuilder {
|
||||||
return quad(
|
return pushFloat2(requireNotNull(attributes[VertexAttributeType.POSITION]) { "Vertex format does not have position attribute" }, x, y)
|
||||||
aabb.mins.x.toFloat(),
|
|
||||||
aabb.mins.y.toFloat(),
|
|
||||||
aabb.maxs.x.toFloat(),
|
|
||||||
aabb.maxs.y.toFloat(),
|
|
||||||
lambda
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun quadZ(
|
fun position(value: IStruct2f) = position(value.component1(), value.component2())
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
z: Float,
|
|
||||||
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
|
||||||
): VertexBuilder {
|
|
||||||
check(mode?.elements == 4) { "Currently building $mode" }
|
|
||||||
|
|
||||||
lambda(vertex().pushVec3f(x0, y0, z), 0).end()
|
fun uv(x: Float, y: Float): VertexBuilder {
|
||||||
lambda(vertex().pushVec3f(x1, y0, z), 1).end()
|
return pushFloat2(requireNotNull(attributes[VertexAttributeType.UV]) { "Vertex format does not have texture UV attribute" }, x, y)
|
||||||
lambda(vertex().pushVec3f(x0, y1, z), 2).end()
|
|
||||||
lambda(vertex().pushVec3f(x1, y1, z), 3).end()
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun quadRotatedZ(
|
fun uv(value: IStruct2f) = position(value.component1(), value.component2())
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
z: Float,
|
|
||||||
x: Float,
|
|
||||||
y: Float,
|
|
||||||
angle: Double,
|
|
||||||
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
|
||||||
): VertexBuilder {
|
|
||||||
check(mode?.elements == 4) { "Currently building $mode" }
|
|
||||||
|
|
||||||
val s = sin(angle).toFloat()
|
fun color(x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
||||||
val c = cos(angle).toFloat()
|
return pushFloat4(requireNotNull(attributes[VertexAttributeType.COLOR]) { "Vertex format does not have color attribute" }, x, y, z, w)
|
||||||
|
}
|
||||||
|
|
||||||
lambda(vertex().pushVec3f(x + x0 * c - s * y0, y + s * x0 + c * y0, z), 0).end()
|
fun color(value: IStruct4f) = color(value.component1(), value.component2(), value.component3(), value.component4())
|
||||||
lambda(vertex().pushVec3f(x + x1 * c - s * y0, y + s * x1 + c * y0, z), 1).end()
|
|
||||||
lambda(vertex().pushVec3f(x + x0 * c - s * y1, y + s * x0 + c * y1, z), 2).end()
|
|
||||||
lambda(vertex().pushVec3f(x + x1 * c - s * y1, y + s * x1 + c * y1, z), 3).end()
|
|
||||||
|
|
||||||
return this
|
fun hueShift(x: Float): VertexBuilder {
|
||||||
|
return pushFloat(requireNotNull(attributes[VertexAttributeType.HUE_SHIFT]) { "Vertex format does not have texture UV attribute" }, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@ import ru.dbotthepony.kbox2d.api.IDebugDraw
|
|||||||
import ru.dbotthepony.kbox2d.api.Transform
|
import ru.dbotthepony.kbox2d.api.Transform
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
@ -12,6 +16,8 @@ import kotlin.math.sin
|
|||||||
|
|
||||||
class Box2DRenderer : IDebugDraw {
|
class Box2DRenderer : IDebugDraw {
|
||||||
val state = StarboundClient.current()
|
val state = StarboundClient.current()
|
||||||
|
private val identity = Matrix3f.identity()
|
||||||
|
|
||||||
override var drawShapes: Boolean = false
|
override var drawShapes: Boolean = false
|
||||||
override var drawJoints: Boolean = false
|
override var drawJoints: Boolean = false
|
||||||
override var drawAABB: Boolean = false
|
override var drawAABB: Boolean = false
|
||||||
@ -19,25 +25,26 @@ class Box2DRenderer : IDebugDraw {
|
|||||||
override var drawPairs: Boolean = false
|
override var drawPairs: Boolean = false
|
||||||
override var drawCenterOfMess: Boolean = false
|
override var drawCenterOfMess: Boolean = false
|
||||||
|
|
||||||
|
private val builder = StreamVertexBuilder(VertexAttributes.POSITION)
|
||||||
|
|
||||||
override fun drawPolygon(vertices: List<Vector2d>, color: RGBAColor) {
|
override fun drawPolygon(vertices: List<Vector2d>, color: RGBAColor) {
|
||||||
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
val builder = state.flat2DLines
|
|
||||||
|
|
||||||
builder.builder.begin()
|
builder.builder.begin()
|
||||||
|
|
||||||
for (i in vertices.indices) {
|
for (i in vertices.indices) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
builder.builder.vertex(current.x.toFloat(), current.y.toFloat())
|
||||||
builder.builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
builder.builder.vertex(next.x.toFloat(), next.y.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.upload()
|
builder.upload()
|
||||||
|
|
||||||
state.programs.flat.use()
|
state.programs.position.use()
|
||||||
state.programs.flat.color = color
|
state.programs.position.colorMultiplier = color
|
||||||
state.programs.flat.transform = state.matrixStack.last()
|
state.programs.position.worldMatrix = state.stack.last()
|
||||||
|
state.programs.position.modelMatrix = identity
|
||||||
|
|
||||||
builder.draw(GL_LINES)
|
builder.draw(GL_LINES)
|
||||||
}
|
}
|
||||||
@ -45,25 +52,24 @@ class Box2DRenderer : IDebugDraw {
|
|||||||
private fun drawSolid(vertices: List<Vector2d>, color: RGBAColor) {
|
private fun drawSolid(vertices: List<Vector2d>, color: RGBAColor) {
|
||||||
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
val builder = state.flat2DTriangles
|
builder.builder.begin(GeometryType.TRIANGLES)
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
|
|
||||||
val zero = vertices[0]
|
val zero = vertices[0]
|
||||||
|
|
||||||
for (i in 1 until vertices.size) {
|
for (i in 1 until vertices.size) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.builder.vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat())
|
builder.builder.vertex(zero.x.toFloat(), zero.y.toFloat())
|
||||||
builder.builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
builder.builder.vertex(current.x.toFloat(), current.y.toFloat())
|
||||||
builder.builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
builder.builder.vertex(next.x.toFloat(), next.y.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.upload()
|
builder.upload()
|
||||||
|
|
||||||
state.programs.flat.use()
|
state.programs.position.use()
|
||||||
state.programs.flat.color = color
|
state.programs.position.colorMultiplier = color
|
||||||
state.programs.flat.transform = state.matrixStack.last()
|
state.programs.position.worldMatrix = state.stack.last()
|
||||||
|
state.programs.position.modelMatrix = identity
|
||||||
|
|
||||||
builder.draw(GL_TRIANGLES)
|
builder.draw(GL_TRIANGLES)
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,10 @@ import ru.dbotthepony.kstarbound.client.StarboundClient
|
|||||||
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
||||||
import ru.dbotthepony.kstarbound.client.gl.*
|
import ru.dbotthepony.kstarbound.client.gl.*
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
|
||||||
private fun breakLines(text: String): List<String> {
|
private fun breakLines(text: String): List<String> {
|
||||||
@ -91,7 +90,6 @@ class Font(
|
|||||||
color: RGBAColor = RGBAColor.WHITE,
|
color: RGBAColor = RGBAColor.WHITE,
|
||||||
|
|
||||||
scale: Float = 1f,
|
scale: Float = 1f,
|
||||||
stack: Matrix4fStack = state.matrixStack,
|
|
||||||
): TextSize {
|
): TextSize {
|
||||||
if (text.isEmpty())
|
if (text.isEmpty())
|
||||||
return TextSize(0f, 0f)
|
return TextSize(0f, 0f)
|
||||||
@ -102,20 +100,22 @@ class Font(
|
|||||||
val totalX = totalSize.width
|
val totalX = totalSize.width
|
||||||
val totalY = totalSize.height
|
val totalY = totalSize.height
|
||||||
|
|
||||||
stack.push()
|
val model = Matrix3f.identity()
|
||||||
|
|
||||||
when (alignY) {
|
when (alignY) {
|
||||||
TextAlignY.TOP -> stack.last().translateWithMultiplication(x = x, y = lineHeight * scale + y)
|
TextAlignY.TOP -> model.translate(x = x, y = lineHeight * scale + y)
|
||||||
TextAlignY.CENTER -> stack.last().translateWithMultiplication(x = x, y = lineHeight * scale - totalY * scale / 2f + y)
|
TextAlignY.CENTER -> model.translate(x = x, y = lineHeight * scale - totalY * scale / 2f + y)
|
||||||
TextAlignY.BOTTOM -> stack.last().translateWithMultiplication(x = x, y = lineHeight * scale - totalY * scale + y)
|
TextAlignY.BOTTOM -> model.translate(x = x, y = lineHeight * scale - totalY * scale + y)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scale != 1f)
|
if (scale != 1f)
|
||||||
stack.last().scale(x = scale, y = scale)
|
model.scale(x = scale, y = scale)
|
||||||
|
|
||||||
state.programs.font.use()
|
state.programs.font.use()
|
||||||
state.programs.font.color = color
|
state.programs.font.colorMultiplier = color
|
||||||
|
state.programs.font.worldMatrix = state.stack.last()
|
||||||
state.activeTexture = 0
|
state.activeTexture = 0
|
||||||
|
state.programs.font.texture = 0
|
||||||
|
|
||||||
val space = getGlyph(' ')
|
val space = getGlyph(' ')
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ class Font(
|
|||||||
|
|
||||||
for (line in text) {
|
for (line in text) {
|
||||||
if (line == "") {
|
if (line == "") {
|
||||||
stack.last().translateWithMultiplication(y = lineHeight)
|
model.translate(y = lineHeight)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,12 +134,12 @@ class Font(
|
|||||||
|
|
||||||
TextAlignX.CENTER -> {
|
TextAlignX.CENTER -> {
|
||||||
movedX = totalX / 2f - lineWidth(line, space) / 2f
|
movedX = totalX / 2f - lineWidth(line, space) / 2f
|
||||||
stack.last().translateWithMultiplication(x = movedX)
|
model.translate(x = movedX)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextAlignX.RIGHT -> {
|
TextAlignX.RIGHT -> {
|
||||||
movedX = -lineWidth(line, space)
|
movedX = -lineWidth(line, space)
|
||||||
stack.last().translateWithMultiplication(x = movedX)
|
model.translate(x = movedX)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +150,10 @@ class Font(
|
|||||||
if (chr == '\t') {
|
if (chr == '\t') {
|
||||||
if (lineGlyphs % 4 == 0) {
|
if (lineGlyphs % 4 == 0) {
|
||||||
advancedX += space.advanceX * 4
|
advancedX += space.advanceX * 4
|
||||||
stack.last().translateWithMultiplication(x = space.advanceX * 4)
|
model.translate(x = space.advanceX * 4)
|
||||||
} else {
|
} else {
|
||||||
advancedX += space.advanceX * (lineGlyphs % 4)
|
advancedX += space.advanceX * (lineGlyphs % 4)
|
||||||
stack.last().translateWithMultiplication(x = space.advanceX * (lineGlyphs % 4))
|
model.translate(x = space.advanceX * (lineGlyphs % 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
lineGlyphs += lineGlyphs % 4
|
lineGlyphs += lineGlyphs % 4
|
||||||
@ -161,17 +161,16 @@ class Font(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val glyph = getGlyph(chr)
|
val glyph = getGlyph(chr)
|
||||||
glyph.render(stack)
|
glyph.render(model)
|
||||||
lineWidth += glyph.advanceX
|
lineWidth += glyph.advanceX
|
||||||
lineGlyphs++
|
lineGlyphs++
|
||||||
}
|
}
|
||||||
|
|
||||||
advancedX = advancedX.coerceAtLeast(lineWidth)
|
advancedX = advancedX.coerceAtLeast(lineWidth)
|
||||||
stack.last().translateWithMultiplication(x = -lineWidth - movedX, y = lineHeight)
|
model.translate(x = -lineWidth - movedX, y = lineHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.vao = null
|
state.vao = null
|
||||||
stack.pop()
|
|
||||||
|
|
||||||
return TextSize(totalX * scale, totalY * scale)
|
return TextSize(totalX * scale, totalY * scale)
|
||||||
}
|
}
|
||||||
@ -188,7 +187,6 @@ class Font(
|
|||||||
color: RGBAColor = RGBAColor.WHITE,
|
color: RGBAColor = RGBAColor.WHITE,
|
||||||
|
|
||||||
scale: Float = 1f,
|
scale: Float = 1f,
|
||||||
stack: Matrix4fStack = state.matrixStack,
|
|
||||||
): TextSize {
|
): TextSize {
|
||||||
return render(
|
return render(
|
||||||
breakLines(text),
|
breakLines(text),
|
||||||
@ -197,7 +195,6 @@ class Font(
|
|||||||
alignX = alignX,
|
alignX = alignX,
|
||||||
alignY = alignY,
|
alignY = alignY,
|
||||||
scale = scale,
|
scale = scale,
|
||||||
stack = stack,
|
|
||||||
color = color,
|
color = color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -210,10 +207,10 @@ class Font(
|
|||||||
if (chr == '\t') {
|
if (chr == '\t') {
|
||||||
if (lineGlyphs % 4 == 0) {
|
if (lineGlyphs % 4 == 0) {
|
||||||
lineWidth += space.advanceX * 4
|
lineWidth += space.advanceX * 4
|
||||||
state.matrixStack.last().translateWithMultiplication(x = space.advanceX * 4)
|
state.stack.last().translate(x = space.advanceX * 4)
|
||||||
} else {
|
} else {
|
||||||
lineWidth += space.advanceX * (lineGlyphs % 4)
|
lineWidth += space.advanceX * (lineGlyphs % 4)
|
||||||
state.matrixStack.last().translateWithMultiplication(x = space.advanceX * (lineGlyphs % 4))
|
state.stack.last().translate(x = space.advanceX * (lineGlyphs % 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
lineGlyphs += lineGlyphs % 4
|
lineGlyphs += lineGlyphs % 4
|
||||||
@ -258,9 +255,7 @@ class Font(
|
|||||||
val advanceX: Float
|
val advanceX: Float
|
||||||
val advanceY: Float
|
val advanceY: Float
|
||||||
|
|
||||||
private val vbo: BufferObject.VBO? // все три указателя должны хранится во избежание утечки
|
private val vao: VertexArrayObject?
|
||||||
private val ebo: BufferObject.EBO? // все три указателя должны хранится во избежание утечки
|
|
||||||
private val vao: VertexArrayObject? // все три указателя должны хранится во избежание утечки
|
|
||||||
|
|
||||||
private val indexCount: Int
|
private val indexCount: Int
|
||||||
|
|
||||||
@ -301,54 +296,48 @@ class Font(
|
|||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
|
||||||
|
|
||||||
vao = state.newVAO()
|
vao = state.newVAO()
|
||||||
ebo = state.newEBO()
|
val ebo = state.newEBO()
|
||||||
vbo = state.newVBO()
|
val vbo = state.newVBO()
|
||||||
|
|
||||||
vao.bind()
|
vao.elementBuffer = ebo
|
||||||
ebo.bind()
|
vao.bindAttributes(vbo, VertexAttributes.POSITION_UV)
|
||||||
vbo.bind()
|
|
||||||
|
|
||||||
val builder = VertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, GeometryType.QUADS)
|
val builder = VertexBuilder(VertexAttributes.POSITION_UV, GeometryType.QUADS)
|
||||||
|
|
||||||
|
builder.vertex(0f, 0f).uv(0f, 0f)
|
||||||
|
builder.vertex(width, 0f).uv(1f, 0f)
|
||||||
|
builder.vertex(width, height).uv(1f, 1f)
|
||||||
|
builder.vertex(0f, height).uv(0f, 1f)
|
||||||
|
|
||||||
builder.quad(0f, 0f, width, height, QuadTransformers.uv())
|
|
||||||
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
||||||
builder.attributes.apply(vao, true)
|
|
||||||
indexCount = builder.indexCount
|
indexCount = builder.indexCount
|
||||||
|
|
||||||
elementIndexType = builder.indexType
|
elementIndexType = builder.indexType
|
||||||
|
|
||||||
vao.unbind()
|
|
||||||
ebo.unbind()
|
|
||||||
vbo.unbind()
|
|
||||||
} else {
|
} else {
|
||||||
isEmpty = true
|
isEmpty = true
|
||||||
indexCount = 0
|
indexCount = 0
|
||||||
elementIndexType = 0
|
elementIndexType = 0
|
||||||
|
|
||||||
vao = null
|
vao = null
|
||||||
vbo = null
|
|
||||||
ebo = null
|
|
||||||
|
|
||||||
texture = null
|
texture = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(stack: Matrix4fStack) {
|
fun render(model: Matrix3f) {
|
||||||
if (isEmpty) {
|
if (isEmpty) {
|
||||||
stack.last().translateWithMultiplication(advanceX)
|
model.translate(advanceX)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vao!!.bind()
|
vao!!.bind()
|
||||||
|
|
||||||
stack.last().translateWithMultiplication(bearingX, -bearingY)
|
model.translate(bearingX, -bearingY)
|
||||||
|
|
||||||
texture!!.bind()
|
texture!!.bind()
|
||||||
state.programs.font.transform = stack.last()
|
state.programs.font.modelMatrix = model
|
||||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
|
||||||
stack.last().translateWithMultiplication(advanceX - bearingX, bearingY)
|
model.translate(advanceX - bearingX, bearingY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,32 +2,27 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix3fStack
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Позволяет вызывать отрисовщики в определённой (послойной) последовательности
|
* Позволяет вызывать отрисовщики в определённой (послойной) последовательности
|
||||||
*/
|
*/
|
||||||
class LayeredRenderer {
|
class LayeredRenderer {
|
||||||
private val layers = Long2ObjectAVLTreeMap<ArrayList<(Matrix4fStack) -> Unit>>()
|
private val layers = Long2ObjectAVLTreeMap<ArrayList<() -> Unit>>()
|
||||||
private val layersHash = Long2ObjectOpenHashMap<ArrayList<(Matrix4fStack) -> Unit>>()
|
|
||||||
|
|
||||||
fun add(layer: Long, renderer: (Matrix4fStack) -> Unit) {
|
fun add(layer: Long, renderer: () -> Unit) {
|
||||||
var list = layersHash[layer]
|
layers.computeIfAbsent(layer, Function { ArrayList() }).add(renderer)
|
||||||
|
|
||||||
if (list == null) {
|
|
||||||
list = ArrayList()
|
|
||||||
layers[layer] = list
|
|
||||||
layersHash[layer] = list
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(renderer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(stack: Matrix4fStack) {
|
fun render() {
|
||||||
for (list in layers.values) {
|
for (list in layers.values) {
|
||||||
for (renderer in list) {
|
for (renderer in list) {
|
||||||
renderer.invoke(stack)
|
renderer.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layers.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,25 +18,22 @@ class Mesh() {
|
|||||||
val vbo = state.newVBO()
|
val vbo = state.newVBO()
|
||||||
val ebo = state.newEBO()
|
val ebo = state.newEBO()
|
||||||
|
|
||||||
|
init {
|
||||||
|
vao.elementBuffer = ebo
|
||||||
|
}
|
||||||
|
|
||||||
var indexCount = 0
|
var indexCount = 0
|
||||||
private set
|
private set
|
||||||
var indexType = 0
|
var indexType = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun load(builder: VertexBuilder, mode: Int = GL45.GL_DYNAMIC_DRAW) {
|
fun load(builder: VertexBuilder, mode: Int = GL45.GL_DYNAMIC_DRAW) {
|
||||||
vao.bind()
|
|
||||||
vbo.bind()
|
|
||||||
ebo.bind()
|
|
||||||
|
|
||||||
builder.upload(vbo, ebo, mode)
|
builder.upload(vbo, ebo, mode)
|
||||||
builder.attributes.apply(vao, true)
|
|
||||||
|
vao.bindAttributes(vbo, builder.attributes)
|
||||||
|
|
||||||
indexCount = builder.indexCount
|
indexCount = builder.indexCount
|
||||||
indexType = builder.indexType
|
indexType = builder.indexType
|
||||||
|
|
||||||
vao.unbind()
|
|
||||||
vbo.unbind()
|
|
||||||
ebo.unbind()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render() {
|
fun render() {
|
||||||
@ -48,8 +45,8 @@ class Mesh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class ConfiguredMesh<T : GLShaderProgram>(val config: RenderConfig<T>, val mesh: Mesh = Mesh()) {
|
data class ConfiguredMesh<T : GLShaderProgram>(val config: RenderConfig<T>, val mesh: Mesh = Mesh()) {
|
||||||
fun render(transform: Matrix4f = config.client.matrixStack.last()) {
|
fun render() {
|
||||||
config.setup(transform)
|
config.setup()
|
||||||
mesh.render()
|
mesh.render()
|
||||||
config.uninstall()
|
config.uninstall()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ abstract class RenderConfig<out T : GLShaderProgram>(val program: T) {
|
|||||||
val client get() = program.client
|
val client get() = program.client
|
||||||
open val initialBuilderCapacity: Int get() = 64
|
open val initialBuilderCapacity: Int get() = 64
|
||||||
|
|
||||||
open fun setup(transform: Matrix4f = client.matrixStack.last()) {
|
open fun setup() {
|
||||||
program.use()
|
program.use()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,13 +6,13 @@ import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
|||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
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.shader.UberShader
|
||||||
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.*
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
@ -27,6 +27,7 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
private val background = HashMap<GLTexture2D, Config>()
|
private val background = HashMap<GLTexture2D, Config>()
|
||||||
private val matCache = HashMap<String, TileRenderer>()
|
private val matCache = HashMap<String, TileRenderer>()
|
||||||
private val modCache = HashMap<String, TileRenderer>()
|
private val modCache = HashMap<String, TileRenderer>()
|
||||||
|
private val identity = Matrix3f.identity()
|
||||||
|
|
||||||
fun getMaterialRenderer(defName: String): TileRenderer {
|
fun getMaterialRenderer(defName: String): TileRenderer {
|
||||||
return matCache.computeIfAbsent(defName) {
|
return matCache.computeIfAbsent(defName) {
|
||||||
@ -42,31 +43,32 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig<GLTileProgram>(client.programs.tile) {
|
private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig<UberShader>(client.programs.tile) {
|
||||||
override val initialBuilderCapacity: Int
|
override val initialBuilderCapacity: Int
|
||||||
get() = 1024
|
get() = 1024
|
||||||
|
|
||||||
override fun setup(transform: Matrix4f) {
|
override fun setup() {
|
||||||
super.setup(transform)
|
super.setup()
|
||||||
client.activeTexture = 0
|
client.activeTexture = 0
|
||||||
client.depthTest = false
|
client.depthTest = false
|
||||||
program.texture = 0
|
program.texture0 = 0
|
||||||
texture.bind()
|
texture.bind()
|
||||||
texture.textureMagFilter = GL_NEAREST
|
texture.textureMagFilter = GL_NEAREST
|
||||||
texture.textureMinFilter = GL_NEAREST
|
texture.textureMinFilter = GL_NEAREST
|
||||||
|
|
||||||
program.transform = transform
|
program.worldMatrix = client.stack.last()
|
||||||
program.color = color
|
program.modelMatrix = identity
|
||||||
|
program.colorMultiplier = color
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun uninstall() {}
|
override fun uninstall() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foreground(texture: GLTexture2D): RenderConfig<GLTileProgram> {
|
fun foreground(texture: GLTexture2D): RenderConfig<UberShader> {
|
||||||
return foreground.computeIfAbsent(texture) { Config(it, FOREGROUND_COLOR) }
|
return foreground.computeIfAbsent(texture) { Config(it, FOREGROUND_COLOR) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun background(texture: GLTexture2D): RenderConfig<GLTileProgram> {
|
fun background(texture: GLTexture2D): RenderConfig<UberShader> {
|
||||||
return background.computeIfAbsent(texture) { Config(it, BACKGROUND_COLOR) }
|
return background.computeIfAbsent(texture) { Config(it, BACKGROUND_COLOR) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +148,13 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
val (u0, v0) = texture!!.pixelToUV(mins)
|
val (u0, v0) = texture!!.pixelToUV(mins)
|
||||||
val (u1, v1) = texture.pixelToUV(maxs)
|
val (u1, v1) = texture.pixelToUV(maxs)
|
||||||
|
|
||||||
builder.quadZ(a, b, c, d, Z_LEVEL, QuadTransformers.uv(u0, v1, u1, v0).after { it, _ -> it.push(if (isModifier) self.modifierHueShift else self.hueShift) })
|
val hue = if (isModifier) self.modifierHueShift else self.hueShift
|
||||||
|
|
||||||
|
// flip uv since in-world coordinates are flipped relative to screen
|
||||||
|
builder.vertex(a, b).uv(u0, v1).hueShift(hue)
|
||||||
|
builder.vertex(c, b).uv(u1, v1).hueShift(hue)
|
||||||
|
builder.vertex(c, d).uv(u1, v0).hueShift(hue)
|
||||||
|
builder.vertex(a, d).uv(u0, v0).hueShift(hue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tesselatePiece(
|
private fun tesselatePiece(
|
||||||
@ -222,7 +230,6 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val Z_LEVEL = 10f
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,6 @@ class ItemRenderer(client: StarboundClient, entity: ItemEntity, chunk: ClientChu
|
|||||||
private val def = entity.def
|
private val def = entity.def
|
||||||
|
|
||||||
override fun render(stack: Matrix4fStack) {
|
override fun render(stack: Matrix4fStack) {
|
||||||
client.programs.textured.use()
|
|
||||||
client.programs.textured.transform = stack.last()
|
|
||||||
client.activeTexture = 0
|
|
||||||
client.programs.textured.texture = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,10 @@ class ClientWorld(
|
|||||||
val state = view.getCell(x, y)
|
val state = view.getCell(x, y)
|
||||||
|
|
||||||
if (state?.liquid?.def === type) {
|
if (state?.liquid?.def === type) {
|
||||||
builder.quad(x.toFloat(), y.toFloat(), x + 1f, y + state!!.liquid.level)
|
builder.vertex(x.toFloat(), y.toFloat())
|
||||||
|
builder.vertex(x.toFloat() + 1f, y.toFloat())
|
||||||
|
builder.vertex(x.toFloat() + 1f, y.toFloat() + 1f)
|
||||||
|
builder.vertex(x.toFloat(), y.toFloat() + 1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,37 +156,37 @@ class ClientWorld(
|
|||||||
|
|
||||||
for ((baked, zLevel) in background.bakedMeshes) {
|
for ((baked, zLevel) in background.bakedMeshes) {
|
||||||
layers.add(zLevel + RenderLayer.BackgroundTile.index) {
|
layers.add(zLevel + RenderLayer.BackgroundTile.index) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
client.stack.push().last().translate(renderOrigin.x, renderOrigin.y)
|
||||||
baked.render(it.last())
|
baked.render()
|
||||||
it.pop()
|
client.stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((baked, zLevel) in foreground.bakedMeshes) {
|
for ((baked, zLevel) in foreground.bakedMeshes) {
|
||||||
layers.add(zLevel + RenderLayer.ForegroundTile.index) {
|
layers.add(zLevel + RenderLayer.ForegroundTile.index) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
client.stack.push().last().translate(renderOrigin.x, renderOrigin.y)
|
||||||
baked.render(it.last())
|
baked.render()
|
||||||
it.pop()
|
client.stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liquidMesh.isNotEmpty()) {
|
/*if (liquidMesh.isNotEmpty()) {
|
||||||
layers.add(RenderLayer.Liquid.index) {
|
layers.add(RenderLayer.Liquid.index) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
client.stack.push().last().translate(renderOrigin.x, renderOrigin.y)
|
||||||
|
|
||||||
val program = client.programs.liquid
|
val program = client.programs.liquid
|
||||||
|
|
||||||
program.use()
|
program.use()
|
||||||
program.transform = it.last()
|
program.transform = client.stack.last()
|
||||||
|
|
||||||
for ((mesh, color) in liquidMesh) {
|
for ((mesh, color) in liquidMesh) {
|
||||||
program.baselineColor = color
|
program.baselineColor = color
|
||||||
mesh.render()
|
mesh.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
it.pop()
|
client.stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,24 +269,21 @@ class ClientWorld(
|
|||||||
for (obj in objects) {
|
for (obj in objects) {
|
||||||
if (obj.pos.x in client.viewportCellX .. client.viewportCellX + client.viewportCellWidth && obj.pos.y in client.viewportCellY .. client.viewportCellY + client.viewportCellHeight) {
|
if (obj.pos.x in client.viewportCellX .. client.viewportCellX + client.viewportCellWidth && obj.pos.y in client.viewportCellY .. client.viewportCellY + client.viewportCellHeight) {
|
||||||
//layers.add(RenderLayer.Object.index) {
|
//layers.add(RenderLayer.Object.index) {
|
||||||
layers.add(obj.orientation?.renderLayer ?: continue) { m ->
|
layers.add(obj.orientation?.renderLayer ?: continue) {
|
||||||
client.quadWireframe {
|
client.quadWireframe {
|
||||||
it.quad(
|
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat())
|
||||||
obj.pos.x.toFloat(),
|
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat())
|
||||||
obj.pos.y.toFloat(),
|
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat() + 1f)
|
||||||
obj.pos.x + 1f,
|
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat() + 1f)
|
||||||
obj.pos.y + 1f,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.drawables.forEach {
|
obj.drawables.forEach {
|
||||||
val (x, y) = obj.imagePosition
|
val (x, y) = obj.imagePosition
|
||||||
|
|
||||||
it.render(
|
client.stack.push().last()
|
||||||
client,
|
.translate(obj.pos.x.toFloat() + x / PIXELS_IN_STARBOUND_UNITf, obj.pos.y.toFloat() + y / PIXELS_IN_STARBOUND_UNITf)
|
||||||
x = obj.pos.x.toFloat() + x / PIXELS_IN_STARBOUND_UNITf,
|
it.render(client)
|
||||||
y = obj.pos.y.toFloat() + y / PIXELS_IN_STARBOUND_UNITf
|
client.stack.pop()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,18 +10,23 @@ import com.google.gson.stream.JsonWriter
|
|||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
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.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.io.json.consumeNull
|
import ru.dbotthepony.kstarbound.io.json.consumeNull
|
||||||
import ru.dbotthepony.kstarbound.math.LineF
|
import ru.dbotthepony.kstarbound.math.LineF
|
||||||
|
import ru.dbotthepony.kstarbound.util.Either
|
||||||
import ru.dbotthepony.kstarbound.util.contains
|
import ru.dbotthepony.kstarbound.util.contains
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
import ru.dbotthepony.kvector.vector.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.Vector3f
|
import ru.dbotthepony.kvector.vector.Vector3f
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
|
sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbright: Boolean) {
|
||||||
|
@JsonFactory
|
||||||
|
data class Transformations(val centered: Boolean = false, val rotation: Float = 0f, val mirrored: Boolean = false, val scale: Either<Float, Vector2f> = Either.left(1f))
|
||||||
|
|
||||||
class Line(
|
class Line(
|
||||||
val line: LineF,
|
val line: LineF,
|
||||||
val width: Float,
|
val width: Float,
|
||||||
@ -29,7 +34,11 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
color: RGBAColor = RGBAColor.WHITE,
|
color: RGBAColor = RGBAColor.WHITE,
|
||||||
fullbright: Boolean = false
|
fullbright: Boolean = false
|
||||||
) : Drawable(position, color, fullbright) {
|
) : Drawable(position, color, fullbright) {
|
||||||
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun render(client: StarboundClient, x: Float, y: Float) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun flop(): Drawable {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,15 +49,18 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
color: RGBAColor = RGBAColor.WHITE,
|
color: RGBAColor = RGBAColor.WHITE,
|
||||||
fullbright: Boolean = false
|
fullbright: Boolean = false
|
||||||
) : Drawable(position, color, fullbright) {
|
) : Drawable(position, color, fullbright) {
|
||||||
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun render(client: StarboundClient, x: Float, y: Float) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun flop(): Drawable {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Image(
|
class Image(
|
||||||
val path: SpriteReference,
|
val path: SpriteReference,
|
||||||
val transform: Matrix3f,
|
val transform: Either<Matrix3f, Transformations>,
|
||||||
val centered: Boolean,
|
|
||||||
position: Vector2f = Vector2f.ZERO,
|
position: Vector2f = Vector2f.ZERO,
|
||||||
color: RGBAColor = RGBAColor.WHITE,
|
color: RGBAColor = RGBAColor.WHITE,
|
||||||
fullbright: Boolean = false
|
fullbright: Boolean = false
|
||||||
@ -60,34 +72,73 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
return Image(newPath, transform, centered, position, color, fullbright)
|
return Image(newPath, transform, position, color, fullbright)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun flop(): Drawable {
|
||||||
|
return Image(path, transform.flatMap({ it.copy().scale(-1f, 1f) }, { it.copy(mirrored = !it.mirrored) }), position, color, fullbright)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(client: StarboundClient, x: Float, y: Float) {
|
||||||
val sprite = path.sprite ?: return
|
val sprite = path.sprite ?: return
|
||||||
val texture = client.loadTexture(path.imagePath.value!!)
|
val texture = client.loadTexture(path.imagePath.value!!)
|
||||||
|
val program = client.programs.positionTexture
|
||||||
|
|
||||||
if (centered) {
|
program.modelMatrix = transform.map({ it }, {
|
||||||
client.quadTexture(texture) {
|
val mat = Matrix3f.identity()
|
||||||
it.quad(x - (sprite.width / PIXELS_IN_STARBOUND_UNITf) * 0.5f, y - (sprite.height / PIXELS_IN_STARBOUND_UNITf) * 0.5f, x + sprite.width / PIXELS_IN_STARBOUND_UNITf * 0.5f, y + sprite.height / PIXELS_IN_STARBOUND_UNITf * 0.5f, QuadTransformers.uv(sprite))
|
|
||||||
|
it.scale.map({ mat.scale(it / PIXELS_IN_STARBOUND_UNITf, it / PIXELS_IN_STARBOUND_UNITf) }, { mat.scale(it / PIXELS_IN_STARBOUND_UNITf) })
|
||||||
|
|
||||||
|
if (it.centered) {
|
||||||
|
mat.translate(sprite.width / -2f, sprite.height / -2f)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
client.quadTexture(texture) {
|
if (it.rotation != 0f) {
|
||||||
it.quad(x, y, x + sprite.width / PIXELS_IN_STARBOUND_UNITf, y + sprite.height / PIXELS_IN_STARBOUND_UNITf, QuadTransformers.uv(sprite))
|
mat.rotateAroundZ(it.rotation)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (it.mirrored) {
|
||||||
|
mat.translate(sprite.width.toFloat(), 0f)
|
||||||
|
mat.scale(-1f, 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
mat
|
||||||
|
})
|
||||||
|
|
||||||
|
program.worldMatrix = client.stack.last()
|
||||||
|
program.use()
|
||||||
|
program.texture0 = 0
|
||||||
|
client.activeTexture = 0
|
||||||
|
texture.bind()
|
||||||
|
|
||||||
|
program.builder.builder.begin(GeometryType.QUADS)
|
||||||
|
program.builder.builder.vertex(x, y).uv(sprite.u0, sprite.v0)
|
||||||
|
program.builder.builder.vertex(x + sprite.width, y).uv(sprite.u1, sprite.v0)
|
||||||
|
program.builder.builder.vertex(x + sprite.width, y + sprite.height).uv(sprite.u1, sprite.v1)
|
||||||
|
program.builder.builder.vertex(x, y + sprite.height).uv(sprite.u0, sprite.v1)
|
||||||
|
program.builder.upload()
|
||||||
|
program.builder.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Empty(position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false) : Drawable(position, color, fullbright) {
|
class Empty(position: Vector2f = Vector2f.ZERO, color: RGBAColor = RGBAColor.WHITE, fullbright: Boolean = false) : Drawable(position, color, fullbright) {
|
||||||
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {}
|
override fun render(client: StarboundClient, x: Float, y: Float) {}
|
||||||
|
|
||||||
|
override fun flop(): Drawable {
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun with(values: (String) -> String?): Drawable {
|
open fun with(values: (String) -> String?): Drawable {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun render(client: StarboundClient = StarboundClient.current(), stack: Matrix4fStack = client.matrixStack, x: Float = 0f, y: Float = 0f)
|
abstract fun render(client: StarboundClient = StarboundClient.current(), x: Float = 0f, y: Float = 0f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mirror along X axis
|
||||||
|
*/
|
||||||
|
abstract fun flop(): Drawable
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Empty()
|
val EMPTY = Empty()
|
||||||
@ -102,6 +153,7 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
private val colors = gson.getAdapter(RGBAColor::class.java)
|
private val colors = gson.getAdapter(RGBAColor::class.java)
|
||||||
private val images = gson.getAdapter(SpriteReference::class.java)
|
private val images = gson.getAdapter(SpriteReference::class.java)
|
||||||
private val vertices = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, Vector2f::class.java)) as TypeAdapter<ImmutableList<Vector2f>>
|
private val vertices = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, Vector2f::class.java)) as TypeAdapter<ImmutableList<Vector2f>>
|
||||||
|
private val transformations = gson.getAdapter(Transformations::class.java)
|
||||||
|
|
||||||
override fun write(out: JsonWriter?, value: Drawable?) {
|
override fun write(out: JsonWriter?, value: Drawable?) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
@ -123,9 +175,9 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
return Poly(vertices.fromJsonTree(value["poly"]), position, color, fullbright)
|
return Poly(vertices.fromJsonTree(value["poly"]), position, color, fullbright)
|
||||||
} else if ("image" in value) {
|
} else if ("image" in value) {
|
||||||
val image = images.fromJsonTree(value["image"])
|
val image = images.fromJsonTree(value["image"])
|
||||||
val mat = Matrix3f.identity()
|
|
||||||
|
|
||||||
if ("transformation" in value) {
|
if ("transformation" in value) {
|
||||||
|
val mat = Matrix3f.identity()
|
||||||
val array = value["transformation"].asJsonArray
|
val array = value["transformation"].asJsonArray
|
||||||
|
|
||||||
// original starbound use GLM, which reflects OpenGL, which in turn make matrices row-major
|
// original starbound use GLM, which reflects OpenGL, which in turn make matrices row-major
|
||||||
@ -144,26 +196,25 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
mat.r20 = row2.x
|
mat.r20 = row2.x
|
||||||
mat.r21 = row2.y
|
mat.r21 = row2.y
|
||||||
mat.r22 = row2.z
|
mat.r22 = row2.z
|
||||||
|
|
||||||
|
return Image(
|
||||||
|
image,
|
||||||
|
Either.left(mat),
|
||||||
|
position,
|
||||||
|
color,
|
||||||
|
fullbright
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
if ("rotation" in value) {
|
return Image(
|
||||||
LOGGER.warn("Rotation is not supported yet (required by ${image.raw})")
|
image,
|
||||||
}
|
Either.right(transformations.fromJsonTree(value)),
|
||||||
|
position,
|
||||||
if ("mirrored" in value && value["mirrored"].asBoolean) {
|
color,
|
||||||
mat.scale(-1f, -1f)
|
fullbright
|
||||||
}
|
)
|
||||||
|
|
||||||
if ("scale" in value) {
|
|
||||||
if (value["scale"].isJsonArray) {
|
|
||||||
mat.scale(vectors.fromJsonTree(value["scale"]))
|
|
||||||
} else {
|
|
||||||
val scale = value["scale"].asFloat
|
|
||||||
mat.scale(scale, scale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Image(image, mat, value["centered"]?.asBoolean ?: false, position, color, fullbright)
|
|
||||||
} else {
|
} else {
|
||||||
return Empty(position, color, fullbright)
|
return Empty(position, color, fullbright)
|
||||||
}
|
}
|
||||||
|
@ -130,10 +130,22 @@ data class ObjectOrientation(
|
|||||||
|
|
||||||
if ("imageLayers" in obj) {
|
if ("imageLayers" in obj) {
|
||||||
for (value in obj["imageLayers"].asJsonArray) {
|
for (value in obj["imageLayers"].asJsonArray) {
|
||||||
drawables.add(this.drawables.fromJsonTree(value))
|
var result = this.drawables.fromJsonTree(value)
|
||||||
|
|
||||||
|
if (flipImages) {
|
||||||
|
result = result.flop()
|
||||||
|
}
|
||||||
|
|
||||||
|
drawables.add(result)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
drawables.add(this.drawables.fromJsonTree(obj))
|
var result = this.drawables.fromJsonTree(obj)
|
||||||
|
|
||||||
|
if (flipImages) {
|
||||||
|
result = result.flop()
|
||||||
|
}
|
||||||
|
|
||||||
|
drawables.add(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
val imagePosition = (obj["imagePosition"]?.let { vectors.fromJsonTree(it) } ?: Vector2f.ZERO) / PIXELS_IN_STARBOUND_UNITf
|
val imagePosition = (obj["imagePosition"]?.let { vectors.fromJsonTree(it) } ?: Vector2f.ZERO) / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
@ -7,59 +7,35 @@ import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter
|
|||||||
*
|
*
|
||||||
* JSON адаптер реализуется через [EitherTypeAdapter]
|
* JSON адаптер реализуется через [EitherTypeAdapter]
|
||||||
*/
|
*/
|
||||||
sealed class Either<L, R> {
|
class Either<L, R> private constructor(val left: KOptional<L>, val right: KOptional<R>) {
|
||||||
class Left<L, R>(val value: L) : Either<L, R>() {
|
val isLeft: Boolean get() = left.isPresent
|
||||||
override val isLeft: Boolean
|
val isRight: Boolean get() = right.isPresent
|
||||||
get() = true
|
|
||||||
override val isRight: Boolean
|
|
||||||
get() = false
|
|
||||||
|
|
||||||
override fun <T> map(left: (L) -> T, right: (R) -> T): T {
|
inline fun <T> map(left: (L) -> T, right: (R) -> T): T {
|
||||||
return left.invoke(this.value)
|
this.left.ifPresent {
|
||||||
|
return left.invoke(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun left(): L {
|
return right.invoke(this.right.value)
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun right(): R {
|
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Right<L, R>(val value: R) : Either<L, R>() {
|
inline fun <NL, NR> flatMap(left: (L) -> NL, right: (R) -> NR): Either<NL, NR> {
|
||||||
override val isLeft: Boolean
|
this.left.ifPresent {
|
||||||
get() = false
|
return left(left.invoke(it))
|
||||||
override val isRight: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun <T> map(left: (L) -> T, right: (R) -> T): T {
|
|
||||||
return right.invoke(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun left(): L {
|
return right(right.invoke(this.right.value))
|
||||||
throw NoSuchElementException()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun right(): R {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val isLeft: Boolean
|
|
||||||
abstract val isRight: Boolean
|
|
||||||
|
|
||||||
abstract fun <T> map(left: (L) -> T, right: (R) -> T): T
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws NoSuchElementException
|
* @throws NoSuchElementException
|
||||||
*/
|
*/
|
||||||
abstract fun left(): L
|
fun left(): L = left.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws NoSuchElementException
|
* @throws NoSuchElementException
|
||||||
*/
|
*/
|
||||||
abstract fun right(): R
|
fun right(): R = right.value
|
||||||
|
|
||||||
inline fun leftOrElse(orElse: () -> L): L {
|
inline fun leftOrElse(orElse: () -> L): L {
|
||||||
if (isLeft)
|
if (isLeft)
|
||||||
@ -77,11 +53,11 @@ sealed class Either<L, R> {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <L, R> left(value: L): Either<L, R> {
|
fun <L, R> left(value: L): Either<L, R> {
|
||||||
return Left(value)
|
return Either(KOptional.of(value), KOptional.empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <L, R> right(value: R): Either<L, R> {
|
fun <L, R> right(value: R): Either<L, R> {
|
||||||
return Right(value)
|
return Either(KOptional.empty(), KOptional.of(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.google.gson.JsonPrimitive
|
|||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
||||||
|
import java.lang.ref.Reference
|
||||||
|
|
||||||
operator fun JsonObject.set(key: String, value: JsonElement?) { add(key, value) }
|
operator fun JsonObject.set(key: String, value: JsonElement?) { add(key, value) }
|
||||||
operator fun JsonObject.set(key: String, value: String) { add(key, JsonPrimitive(value)) }
|
operator fun JsonObject.set(key: String, value: String) { add(key, JsonPrimitive(value)) }
|
||||||
@ -147,3 +148,17 @@ inline fun <reified T : JsonElement> JsonObject.get(key: String, orElse: () -> T
|
|||||||
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T> MutableIterable<Reference<T>>.forEachValid(block: (T) -> Unit) {
|
||||||
|
val i = iterator()
|
||||||
|
|
||||||
|
for (v in i) {
|
||||||
|
val get = v.get()
|
||||||
|
|
||||||
|
if (get == null) {
|
||||||
|
i.remove()
|
||||||
|
} else {
|
||||||
|
block.invoke(get)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ import java.lang.ref.WeakReference
|
|||||||
import java.util.concurrent.locks.LockSupport
|
import java.util.concurrent.locks.LockSupport
|
||||||
|
|
||||||
// hand-rolled interner, which has similar performance to ConcurrentHashMap
|
// hand-rolled interner, which has similar performance to ConcurrentHashMap
|
||||||
// (given there is no strong congestion, in which case it performs somewhere above Caffeine interner),
|
// (given there is no strong congestion, otherwise it performs somewhere above Caffeine interner),
|
||||||
// while yielding significantly better memory utilization than both
|
// while yielding significantly better memory utilization than both
|
||||||
class HashTableInterner<T : Any>(private val segmentBits: Int) : Interner<T> {
|
class HashTableInterner<T : Any>(private val segmentBits: Int) : Interner<T> {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -10,6 +10,9 @@ fun <T> KOptional(value: T) = KOptional.of(value)
|
|||||||
* in more elegant solution than handling nullable Optionals
|
* in more elegant solution than handling nullable Optionals
|
||||||
*/
|
*/
|
||||||
class KOptional<T> private constructor(private val _value: T, val isPresent: Boolean) {
|
class KOptional<T> private constructor(private val _value: T, val isPresent: Boolean) {
|
||||||
|
/**
|
||||||
|
* @throws NoSuchElementException
|
||||||
|
*/
|
||||||
val value: T get() {
|
val value: T get() {
|
||||||
if (isPresent) {
|
if (isPresent) {
|
||||||
return _value
|
return _value
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
uniform vec4 color;
|
|
||||||
out vec4 color_out;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
color_out = color;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 pos;
|
|
||||||
uniform mat4 transform;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = transform * vec4(pos, 0.5, 1.0);
|
|
||||||
}
|
|
59
src/main/resources/shaders/fragment/configurable.fsh
Normal file
59
src/main/resources/shaders/fragment/configurable.fsh
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
uniform vec4 colorMultiplier;
|
||||||
|
out vec4 colorResult;
|
||||||
|
|
||||||
|
#ifdef TEXTURE
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
in vec2 uvOut;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
in vec4 vertexColor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HUE_SHIFT
|
||||||
|
in float hueShiftOut;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// https://gist.github.com/983/e170a24ae8eba2cd174f
|
||||||
|
// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
|
||||||
|
// https://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl
|
||||||
|
// All components are in the range [0…1], including hue.
|
||||||
|
vec3 rgb2hsv(vec3 c)
|
||||||
|
{
|
||||||
|
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||||
|
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||||
|
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||||
|
|
||||||
|
float d = q.x - min(q.w, q.y);
|
||||||
|
float e = 1.0e-10;
|
||||||
|
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All components are in the range [0…1], including hue.
|
||||||
|
vec3 hsv2rgb(vec3 c)
|
||||||
|
{
|
||||||
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||||
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
colorResult = vec4(1, 1, 1, 1);
|
||||||
|
|
||||||
|
#ifdef TEXTURE
|
||||||
|
colorResult *= texture(texture0, uvOut);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HUE_SHIFT
|
||||||
|
vec3 hsv2 = rgb2hsv(colorResult.rgb);
|
||||||
|
hsv2[0] += hueShiftOut / 360;
|
||||||
|
colorResult = vec4(hsv2rgb(hsv2), colorResult.a);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
colorResult *= vertexColor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
colorResult *= colorMultiplier;
|
||||||
|
}
|
13
src/main/resources/shaders/fragment/font.fsh
Normal file
13
src/main/resources/shaders/fragment/font.fsh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
|
||||||
|
uniform vec4 colorMultiplier;
|
||||||
|
out vec4 colorResult;
|
||||||
|
|
||||||
|
in vec2 uvOut;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
colorResult = colorMultiplier * texture(texture0, uvOut).r;
|
||||||
|
}
|
@ -6,9 +6,10 @@ layout (location = 1) in vec2 uvCoords;
|
|||||||
|
|
||||||
out vec2 oUVCoords;
|
out vec2 oUVCoords;
|
||||||
|
|
||||||
uniform mat4 transform;
|
uniform mat3 transform;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = transform * vec4(vertexPos, 0.0, 1.0);
|
vec3 result = transform * vec3(vertexPos, 1.0);
|
||||||
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
oUVCoords = uvCoords;
|
oUVCoords = uvCoords;
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
uniform sampler2D texture0;
|
|
||||||
uniform vec4 color;
|
|
||||||
|
|
||||||
in vec2 uv_out;
|
|
||||||
in float hsv_vertex;
|
|
||||||
out vec4 color_out;
|
|
||||||
|
|
||||||
// https://gist.github.com/983/e170a24ae8eba2cd174f
|
|
||||||
vec3 rgb2hsv(vec3 c)
|
|
||||||
{
|
|
||||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
|
||||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
|
||||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
|
||||||
|
|
||||||
float d = q.x - min(q.w, q.y);
|
|
||||||
float e = 1.0e-10;
|
|
||||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 hsv2rgb(vec3 c)
|
|
||||||
{
|
|
||||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
||||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
||||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texel = texture(texture0, uv_out);
|
|
||||||
|
|
||||||
vec3 hsv2 = rgb2hsv(vec3(texel[0], texel[1], texel[2]));
|
|
||||||
hsv2[0] += hsv_vertex / 360;
|
|
||||||
vec4 rgb2 = vec4(hsv2rgb(hsv2), texel[3]);
|
|
||||||
|
|
||||||
color_out = color * rgb2;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
layout (location = 0) in vec3 pos;
|
|
||||||
layout (location = 1) in vec2 uv_in;
|
|
||||||
layout (location = 2) in float hsv_in;
|
|
||||||
|
|
||||||
out vec2 uv_out;
|
|
||||||
out float hsv_vertex;
|
|
||||||
uniform mat4 transform;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uv_out = uv_in;
|
|
||||||
hsv_vertex = hsv_in;
|
|
||||||
gl_Position = transform * vec4(pos, 1.0);
|
|
||||||
}
|
|
38
src/main/resources/shaders/vertex/configurable.vsh
Normal file
38
src/main/resources/shaders/vertex/configurable.vsh
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
layout (location = 0) in vec2 pos;
|
||||||
|
|
||||||
|
#ifdef TEXTURE
|
||||||
|
layout (location = TEXTURE) in vec2 uvIn;
|
||||||
|
out vec2 uvOut;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
layout (location = COLOR) in vec4 colorIn;
|
||||||
|
out vec4 vertexColor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HUE_SHIFT
|
||||||
|
layout (location = HUE_SHIFT) in float hueShiftIn;
|
||||||
|
out float hueShiftOut;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform mat3 viewMatrix; // projection (viewport) + camera
|
||||||
|
uniform mat3 worldMatrix; // matrix stack
|
||||||
|
uniform mat3 modelMatrix; // local transformations
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||||
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
|
|
||||||
|
#ifdef HUE_SHIFT
|
||||||
|
hueShiftOut = hueShiftIn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
vertexColor = colorIn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TEXTURE
|
||||||
|
uvOut = uvIn;
|
||||||
|
#endif
|
||||||
|
}
|
18
src/main/resources/shaders/vertex/font.vsh
Normal file
18
src/main/resources/shaders/vertex/font.vsh
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 pos;
|
||||||
|
layout (location = 1) in vec2 uvData;
|
||||||
|
|
||||||
|
uniform mat3 viewMatrix; // projection (viewport)
|
||||||
|
uniform mat3 worldMatrix; // world position
|
||||||
|
uniform mat3 modelMatrix; // mesh/local transformations
|
||||||
|
|
||||||
|
out vec2 uvOut;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||||
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
|
|
||||||
|
uvOut = uvData;
|
||||||
|
}
|
@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
layout (location = 0) in vec2 pos;
|
layout (location = 0) in vec2 pos;
|
||||||
|
|
||||||
uniform mat3 viewMatrix; // camera
|
uniform mat3 viewMatrix; // projection (viewport) + camera
|
||||||
uniform mat3 worldMatrix; // world position
|
uniform mat3 worldMatrix; // matrix stack
|
||||||
uniform mat3 modelMatrix; // mesh/local transformations
|
uniform mat3 modelMatrix; // local transformations
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(viewMatrix * worldMatrix * modelMatrix * vec3(pos, 0.0), 1.0);
|
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||||
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,15 @@
|
|||||||
layout (location = 0) in vec2 pos;
|
layout (location = 0) in vec2 pos;
|
||||||
layout (location = 1) in vec4 color;
|
layout (location = 1) in vec4 color;
|
||||||
|
|
||||||
uniform mat3 viewMatrix; // camera
|
uniform mat3 viewMatrix; // projection (viewport) + camera
|
||||||
uniform mat3 worldMatrix; // world position
|
uniform mat3 worldMatrix; // matrix stack
|
||||||
uniform mat3 modelMatrix; // mesh/local transformations
|
uniform mat3 modelMatrix; // local transformations
|
||||||
|
|
||||||
out vec4 vertexColor;
|
out vec4 vertexColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(viewMatrix * worldMatrix * modelMatrix * vec3(pos, 0.0), 1.0);
|
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||||
|
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||||
|
|
||||||
vertexColor = color;
|
vertexColor = color;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user