Shadows test

This commit is contained in:
DBotThePony 2022-09-14 00:49:50 +07:00
parent 3cc8d54ede
commit 0614158a9c
Signed by: DBot
GPG Key ID: DCC23B5715498507
31 changed files with 855 additions and 172 deletions

View File

@ -3,16 +3,24 @@ package ru.dbotthepony.kstarbound
import org.apache.logging.log4j.LogManager
import org.lwjgl.Version
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
import org.lwjgl.opengl.GL11.GL_LINES
import org.lwjgl.opengl.GL11.GL_RGBA
import org.lwjgl.opengl.GL46
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.io.*
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.File
import java.util.zip.Inflater
import kotlin.math.roundToInt
private val LOGGER = LogManager.getLogger()
@ -28,9 +36,9 @@ fun main() {
Starbound.addPakPath(File("J:\\Steam\\steamapps\\common\\Starbound\\assets\\packed.pak"))
//Starbound.addPakPath(File("packed.pak"))
Starbound.initializeGame { finished, replaceStatus, status ->
/*Starbound.initializeGame { finished, replaceStatus, status ->
client.putDebugLog(status, replaceStatus)
}
}*/
client.onTermination {
Starbound.terminateLoading = true
@ -163,13 +171,90 @@ fun main() {
//ent.position += Vector2d(y = 14.0, x = -10.0)
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
client.camera.pos.x = 578f
client.camera.pos.y = 695f
//client.camera.pos.x = 578f
//client.camera.pos.y = 695f
client.onDrawGUI {
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
client.gl.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
client.gl.font.render("${client.camera.pos}", y = 140f, scale = 0.25f)
client.gl.font.render("${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
}
val lightmap = GLFrameBuffer(client.gl)
lightmap.reattachTexture(client.viewportWidth, client.viewportHeight, GL_RGBA)
client.onViewportChanged { width, height -> lightmap.reattachTexture(width, height, GL_RGBA) }
client.onPostDrawWorld {
lightmap.bind()
GL46.glClear(GL46.GL_COLOR_BUFFER_BIT or GL46.GL_DEPTH_BUFFER_BIT)
//client.gl.programs.colorQuad.clearAlpha()
client.gl.programs.light.use()
client.gl.programs.light.transform.set(client.gl.matrixStack.last)
// client.gl.programs.light.transform.set(Matrix4f.IDENTITY)
client.gl.programs.light.baselineColor.set(Color.WHITE)
var builder = client.gl.programs.light.builder
val (rX, rY) = client.screenToWorld(client.mouseCoordinatesF)
builder.begin()
//val size = 20f / client.settings.scale
val size = 10f
builder.quad(rX - size, rY - size, rX + size, rY + size, QuadTransformers.uv())
builder.upload()
//client.gl.blendFunc = BlendFunc(
// BlendFunc.Func.DST_ALPHA,
// BlendFunc.Func.ONE_MINUS_DST_ALPHA
//)
builder.draw()
client.gl.blendFunc = BlendFunc.ALPHA_TEST
client.gl.programs.lightOccluder.use()
client.gl.programs.lightOccluder.transform.set(client.gl.matrixStack.last)
builder = client.gl.programs.lightOccluder.builder
builder.begin()
val lightPosition = client.screenToWorld(client.mouseCoordinatesF)
builder.quad(0f, 0f, 2f, 2f)
builder.quad(-6f, 0f, -2f, 2f)
client.gl.programs.lightOccluder.lightPosition.set(lightPosition)
builder.upload()
client.gl.blendFunc = BlendFunc.ONLY_ALPHA
builder.draw(GL_LINES)
client.gl.blendFunc = BlendFunc.ALPHA_TEST
lightmap.unbind()
client.gl.activeTexture = 0
client.gl.texture2D = lightmap.texture
client.gl.programs.textureQuad.run(0)
client.gl.programs.flat.use()
client.gl.programs.flat.color.set(Color.BLACK)
client.gl.programs.flat.transform.set(client.gl.matrixStack.last)
builder = client.gl.programs.flat.builder
builder.begin()
builder.quad(0f, 0f, 2f, 2f)
builder.quad(-6f, 0f, -2f, 2f)
builder.upload()
builder.draw()
}
client.onPreDrawWorld {
@ -186,9 +271,9 @@ fun main() {
client.input.addScrollCallback { _, x, y ->
if (y > 0.0) {
client.settings.scale *= y.toFloat() * 2f
client.settings.zoom *= y.toFloat() * 2f
} else if (y < 0.0) {
client.settings.scale /= -y.toFloat() * 2f
client.settings.zoom /= -y.toFloat() * 2f
}
}
@ -199,11 +284,11 @@ fun main() {
//client.camera.pos.x = ent.position.x.toFloat()
//client.camera.pos.y = ent.position.y.toFloat()
client.camera.pos.x += if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.x += if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.x += if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f
client.camera.pos.x += if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f
client.camera.pos.y += if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.y += if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.scale else 0f
client.camera.pos.y += if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f
client.camera.pos.y += if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
import ru.dbotthepony.kstarbound.client.render.EntityRenderer
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer

View File

@ -6,7 +6,7 @@ data class ClientSettings(
*
* Масштаб в единицу означает что один Starbound Unit будет равен 8 пикселям на экране
*/
var scale: Float = 2f,
var zoom: Float = 2f,
var debugCollisions: Boolean = false,
)

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.client
import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
import ru.dbotthepony.kstarbound.defs.ParallaxPrototype

View File

@ -10,16 +10,20 @@ import org.lwjgl.system.MemoryUtil
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.input.UserInput
import ru.dbotthepony.kstarbound.client.render.Camera
import ru.dbotthepony.kstarbound.client.render.TextAlignX
import ru.dbotthepony.kstarbound.client.render.TextAlignY
import ru.dbotthepony.kstarbound.util.formatBytesShort
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
import java.nio.ByteBuffer
import java.nio.ByteOrder
class StarboundClient : AutoCloseable {
val window: Long
@ -35,10 +39,16 @@ class StarboundClient : AutoCloseable {
var viewportHeight = 600
private set
var viewportMatrixGUI = updateViewportMatrixA()
/**
* Матрица преобразования экранных координат (в пикселях) в нормализованные координаты
*/
var viewportMatrixScreen = updateViewportMatrixScreen()
private set
var viewportMatrixGame = updateViewportMatrixB()
/**
* Матрица преобразования мировых координат в нормализованные координаты
*/
var viewportMatrixWorld = updateViewportMatrixWorld()
private set
private val startupTextList = ArrayList<String>()
@ -58,12 +68,69 @@ class StarboundClient : AutoCloseable {
finishStartupRendering = System.currentTimeMillis() + 4000L
}
private fun updateViewportMatrixA(): Matrix4f {
return Matrix4f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f)
private fun updateViewportMatrixScreen(): Matrix4f {
return Matrix4f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f).translate(Vector3f(z = 2f)).toMatrix4f()
}
private fun updateViewportMatrixB(): Matrix4f {
return Matrix4f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 1f, 100f)
private fun updateViewportMatrixWorld(): Matrix4f {
return Matrix4f.orthoDirect(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 1f, 100f).toMatrix4f()
}
private val xMousePos = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
private val yMousePos = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
val mouseCoordinates: Vector2d get() {
xMousePos.position(0)
yMousePos.position(0)
GLFW.glfwGetCursorPos(window, xMousePos, yMousePos)
xMousePos.position(0)
yMousePos.position(0)
return Vector2d(xMousePos.get(), yMousePos.get())
}
val mouseCoordinatesF: Vector2f get() {
xMousePos.position(0)
yMousePos.position(0)
GLFW.glfwGetCursorPos(window, xMousePos, yMousePos)
xMousePos.position(0)
yMousePos.position(0)
return Vector2f(xMousePos.get().toFloat(), yMousePos.get().toFloat())
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(x: Double, y: Double): Vector2d {
val relativeX = camera.pos.x - (viewportWidth / 2.0 - x) / PIXELS_IN_STARBOUND_UNIT / settings.zoom
val relativeY = (viewportHeight / 2.0 - y) / PIXELS_IN_STARBOUND_UNIT / settings.zoom + camera.pos.y
return Vector2d(relativeX, relativeY)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(value: Vector2d): Vector2d {
return screenToWorld(value.x, value.y)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(x: Float, y: Float): Vector2f {
val relativeX = camera.pos.x - (viewportWidth / 2f - x) / PIXELS_IN_STARBOUND_UNITf / settings.zoom
val relativeY = (viewportHeight / 2f - y) / PIXELS_IN_STARBOUND_UNITf / settings.zoom + camera.pos.y
return Vector2f(relativeX, relativeY)
}
/**
* Преобразует экранные координаты в мировые
*/
fun screenToWorld(value: Vector2f): Vector2f {
return screenToWorld(value.x, value.y)
}
init {
@ -90,9 +157,13 @@ class StarboundClient : AutoCloseable {
GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h ->
viewportWidth = w
viewportHeight = h
viewportMatrixGUI = updateViewportMatrixA()
viewportMatrixGame = updateViewportMatrixB()
viewportMatrixScreen = updateViewportMatrixScreen()
viewportMatrixWorld = updateViewportMatrixWorld()
glViewport(0, 0, w, h)
for (callback in onViewportChanged) {
callback.invoke(w, h)
}
}
val stack = MemoryStack.stackPush()
@ -134,7 +205,7 @@ class StarboundClient : AutoCloseable {
gl.clearColor = Color.SLATE_GREY
gl.blend = true
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
gl.blendFunc = BlendFunc.ALPHA_TEST
}
var frameRenderTime = 0.0
@ -160,6 +231,12 @@ class StarboundClient : AutoCloseable {
val settings = ClientSettings()
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
fun onViewportChanged(callback: (width: Int, height: Int) -> Unit) {
onViewportChanged.add(callback)
}
private val onDrawGUI = ArrayList<() -> Unit>()
fun onDrawGUI(lambda: () -> Unit) {
@ -198,11 +275,11 @@ class StarboundClient : AutoCloseable {
world?.think(frameRenderTime)
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
gl.matrixStack.clear(viewportMatrixGame.toMutableMatrix4f())
gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f())
gl.matrixStack.push()
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
.scale(x = settings.scale * PIXELS_IN_STARBOUND_UNITf, y = settings.scale * 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) // перемещаем вид к камере
for (lambda in onPreDrawWorld) {
@ -217,8 +294,8 @@ class StarboundClient : AutoCloseable {
world?.render(
AABB.rectangle(
camera.pos.toDoubleVector(),
viewportWidth / settings.scale / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.scale / PIXELS_IN_STARBOUND_UNIT))
viewportWidth / settings.zoom / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT))
for (lambda in onPostDrawWorld) {
lambda.invoke()
@ -226,7 +303,7 @@ class StarboundClient : AutoCloseable {
gl.matrixStack.pop()
gl.matrixStack.clear(viewportMatrixGUI.toMutableMatrix4f().translate(Vector3f(z = 2f)))
gl.matrixStack.clear(viewportMatrixScreen.toMutableMatrix4f())
val thisTime = System.currentTimeMillis()

View File

@ -0,0 +1,93 @@
package ru.dbotthepony.kstarbound.client.gl
import org.lwjgl.opengl.GL30.GL_COLOR_ATTACHMENT0
import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER
import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_COMPLETE
import org.lwjgl.opengl.GL30.GL_LINEAR
import org.lwjgl.opengl.GL30.GL_RGB
import org.lwjgl.opengl.GL30.GL_TEXTURE
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D
import org.lwjgl.opengl.GL30.glCheckFramebufferStatus
import org.lwjgl.opengl.GL30.glFramebufferTexture2D
import org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus
import org.lwjgl.opengl.GL45.glNamedFramebufferTexture
import org.lwjgl.opengl.GL46
class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
init {
state.ensureSameThread()
}
val pointer = GL46.glGenFramebuffers()
init {
checkForGLError()
}
val isComplete: Boolean get() {
state.ensureSameThread()
return glCheckNamedFramebufferStatus(pointer, GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE
}
var texture: GLTexture2D? = null
private set
fun attachTexture(width: Int, height: Int, format: Int = GL_RGB) {
if (texture != null) {
throw IllegalStateException("Already has texture attached")
}
val texture = GLTexture2D(state, "framebuffer_$pointer")
texture.allocate(format, width, height)
val old = state.framebuffer
bind()
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.pointer, 0)
checkForGLError()
this.texture = texture
texture.textureMinFilter = GL_LINEAR
state.framebuffer = old
}
fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) {
texture?.close()
texture = null
attachTexture(width, height, format)
}
fun bind() {
state.framebuffer = this
}
fun bindWrite() {
state.writeFramebuffer = this
}
fun bindRead() {
state.readFramebuffer = this
}
fun unbind() {
if (state.writeFramebuffer == this) {
state.writeFramebuffer = null
}
if (state.readFramebuffer == this) {
state.readFramebuffer = null
}
}
private val cleanable = state.registerCleanable(this, GL46::glDeleteFramebuffers, "Framebuffer", pointer)
var isValid = true
private set
override fun close() {
state.ensureSameThread()
if (!isValid)
return
cleanable.cleanManual()
isValid = false
}
}

View File

@ -2,9 +2,11 @@ package ru.dbotthepony.kstarbound.client.gl
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL14
import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.freetype.FreeType
import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException
import ru.dbotthepony.kstarbound.client.gl.program.GLPrograms
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
@ -12,7 +14,8 @@ import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgra
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
import ru.dbotthepony.kstarbound.client.render.Font
import ru.dbotthepony.kstarbound.client.render.TileRenderers
@ -22,7 +25,10 @@ import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import java.io.FileNotFoundException
import java.lang.ref.Cleaner
import java.util.*
import java.util.concurrent.ThreadFactory
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.reflect.KProperty
private class GLStateSwitchTracker(private val enum: Int, private var value: Boolean = false) {
@ -47,7 +53,7 @@ private class GLStateSwitchTracker(private val enum: Int, private var value: Boo
}
}
private class GLStateGenericTracker<T>(private var value: T, private val lambda: (T) -> Unit) {
private class GLStateGenericTracker<T>(private var value: T, private val callback: (T) -> Unit) {
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): T {
return value
}
@ -58,12 +64,69 @@ private class GLStateGenericTracker<T>(private var value: T, private val lambda:
if (value == this.value)
return
lambda.invoke(value)
callback.invoke(value)
checkForGLError()
this.value = value
}
}
@Suppress("unused")
data class BlendFunc(
val sourceColor: Func,
val destinationColor: Func,
val sourceAlpha: Func,
val destinationAlpha: Func
) {
constructor() : this(
Func.ONE,
Func.ZERO,
Func.ONE,
Func.ZERO
)
constructor(
source: Func, destination: Func
) : this(
source,
destination,
source,
destination
)
enum class Func(val enum: Int) {
ZERO(GL_ZERO),
ONE(GL_ONE),
SRC_COLOR(GL_SRC_COLOR),
ONE_MINUS_SRC_COLOR(GL_ONE_MINUS_SRC_COLOR),
SRC_ALPHA(GL_SRC_ALPHA),
ONE_MINUS_SRC_ALPHA(GL_ONE_MINUS_SRC_ALPHA),
DST_ALPHA(GL_DST_ALPHA),
ONE_MINUS_DST_ALPHA(GL_ONE_MINUS_DST_ALPHA),
DST_COLOR(GL_DST_COLOR),
ONE_MINUS_DST_COLOR(GL_ONE_MINUS_DST_COLOR),
SRC_ALPHA_SATURATE(GL_SRC_ALPHA_SATURATE);
}
companion object {
val DEFAULT = BlendFunc()
val ALPHA_TEST = BlendFunc(Func.SRC_ALPHA, Func.ONE_MINUS_SRC_ALPHA)
val ONLY_ALPHA = BlendFunc(
Func.ZERO,
Func.ONE,
Func.ONE,
Func.ZERO
)
val ONLY_COLOR = BlendFunc(
Func.ONE,
Func.ZERO,
Func.ZERO,
Func.ONE
)
}
}
interface GLCleanable : Cleaner.Cleanable {
/**
* Выставляет флаг на то, что объект был удалён вручную и вызывает clean()
@ -75,7 +138,14 @@ interface GLStreamBuilderList {
val small: StreamVertexBuilder
}
@Suppress("PropertyName", "unused")
class GLStateTracker {
private fun isMe(state: GLStateTracker?) {
if (state != null && state != this) {
throw InvalidArgumentException("Provided object does not belong to $this state tracker (belongs to $state)")
}
}
init {
check(TRACKERS.get() == null) { "Already has state tracker existing at this thread!" }
TRACKERS.set(this)
@ -87,7 +157,8 @@ class GLStateTracker {
GL.createCapabilities()
}
private var cleanerHits = ArrayList<() -> Unit>()
private val cleanerHits = ArrayList<() -> Unit>()
private val cleaner = Cleaner.create(object : ThreadFactory {
override fun newThread(r: Runnable): Thread {
val thread = Thread(r, "OpenGL Object Cleaner@" + System.identityHashCode(this))
@ -101,11 +172,13 @@ class GLStateTracker {
val cleanable = cleaner.register(ref) {
if (!cleanManual)
LOGGER.error("{} with ID {} got leaked.", name, nativeRef)
LOGGER.error("{} with ID {} was GC'd by JVM, but it should have been removed manually.", name, nativeRef)
cleanerHits.add {
fn(nativeRef)
checkForGLError()
synchronized(cleanerHits) {
cleanerHits.add {
fn(nativeRef)
checkForGLError()
}
}
}
@ -120,11 +193,12 @@ class GLStateTracker {
}
fun cleanup() {
val copy = cleanerHits
cleanerHits = ArrayList()
synchronized(cleanerHits) {
for (lambda in cleanerHits) {
lambda.invoke()
}
for (lambda in copy) {
lambda.invoke()
cleanerHits.clear()
}
}
@ -135,6 +209,7 @@ class GLStateTracker {
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) {
@ -152,6 +227,7 @@ class GLStateTracker {
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) {
@ -169,6 +245,7 @@ class GLStateTracker {
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) {
@ -181,6 +258,56 @@ class GLStateTracker {
checkForGLError()
}
var readFramebuffer: GLFrameBuffer? = null
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)
checkForGLError()
return
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, value.pointer)
checkForGLError()
}
var writeFramebuffer: GLFrameBuffer? = null
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
checkForGLError()
return
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, value.pointer)
checkForGLError()
}
var framebuffer: GLFrameBuffer?
get() {
val readFramebuffer = readFramebuffer
val writeFramebuffer = writeFramebuffer
if (readFramebuffer == writeFramebuffer) {
return writeFramebuffer
}
return null
}
set(value) {
readFramebuffer = value
writeFramebuffer = value
}
var program: GLShaderProgram? = null
private set
@ -188,6 +315,7 @@ class GLStateTracker {
set(value) {
ensureSameThread()
if (field === value) return
isMe(value?.state)
field = value
if (value == null) return
glBindTexture(GL_TEXTURE_2D, value.pointer)
@ -210,6 +338,10 @@ class GLStateTracker {
glClearColor(r, g, b, a)
}
var blendFunc by GLStateGenericTracker(BlendFunc()) {
glBlendFuncSeparate(it.sourceColor.enum, it.destinationColor.enum, it.sourceAlpha.enum, it.destinationAlpha.enum)
}
init {
glActiveTexture(GL_TEXTURE0)
checkForGLError()
@ -362,42 +494,42 @@ class GLStateTracker {
val flat2DLines = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.LINES, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.LINES, 1024)
}
}
val flat2DTriangles = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.TRIANGLES, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.TRIANGLES, 1024)
}
}
val flat2DQuads = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS, 1024)
}
}
val flat2DTexturedQuads = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, VertexType.QUADS, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS, 1024)
}
}
val flat2DQuadLines = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES, 1024)
}
}
val flat2DQuadWireframe = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 1024)
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 1024)
}
}
val quadWireframe by lazy {
StreamVertexBuilder(this, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 16384)
StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 16384)
}
val matrixStack = Matrix4fStack()

View File

@ -27,6 +27,7 @@ class GLTexturePropertyTracker(private val flag: Int, var value: Int) {
}
}
@Suppress("SameParameterValue")
class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : AutoCloseable {
val pointer = glGenTextures()
@ -63,18 +64,20 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
private var mipsWarning = 2
var textureMinFilter by GLTexturePropertyTracker(GL_TEXTURE_MIN_FILTER, GL_LINEAR)
var textureMinFilter by GLTexturePropertyTracker(GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
var textureMagFilter by GLTexturePropertyTracker(GL_TEXTURE_MAG_FILTER, GL_LINEAR)
var textureWrapS by GLTexturePropertyTracker(GL_TEXTURE_WRAP_S, GL_REPEAT)
var textureWrapT by GLTexturePropertyTracker(GL_TEXTURE_WRAP_T, GL_REPEAT)
fun bind(): GLTexture2D {
if (mipsWarning == 1) {
LOGGER.warn("(Likely) Trying to use texture {} before generated it's mips, this probably won't work!", this)
mipsWarning = 0
} else if (mipsWarning == 2) {
mipsWarning = 1
if (textureMinFilter != GL_LINEAR && textureMinFilter != GL_NEAREST) {
if (mipsWarning == 1) {
LOGGER.warn("(Likely) Trying to use texture {} before generated it's mips, this probably won't work!", this)
mipsWarning = 0
} else if (mipsWarning == 2) {
mipsWarning = 1
}
}
state.texture2D = this
@ -101,6 +104,23 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
fun pixelToUV(pos: Vector2i) = pixelToUV(pos.x, pos.y)
fun allocate(mipmap: Int, loadedFormat: Int, width: Int, height: Int): GLTexture2D {
bind()
require(width > 0) { "Invalid width $width" }
require(height > 0) { "Invalid height $height" }
this.width = width
this.height = height
glTexImage2D(GL_TEXTURE_2D, mipmap, loadedFormat, width, height, 0, loadedFormat, GL_UNSIGNED_BYTE, 0L)
checkForGLError()
uploaded = true
return this
}
fun allocate(loadedFormat: Int, width: Int, height: Int): GLTexture2D {
return allocate(0, loadedFormat, width, height)
}
private fun upload(mipmap: Int, loadedFormat: Int, width: Int, height: Int, bufferFormat: Int, dataFormat: Int, data: IntArray): GLTexture2D {
bind()

View File

@ -5,9 +5,10 @@ import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
private fun loadShaders(
vertex: Collection<String>,
fragment: Collection<String>
fragment: Collection<String>,
geometry: Collection<String>
): Array<GLShader> {
val result = arrayOfNulls<GLShader>(vertex.size + fragment.size)
val result = arrayOfNulls<GLShader>(vertex.size + fragment.size + geometry.size)
var i = 0
for (name in vertex) {
@ -18,13 +19,18 @@ private fun loadShaders(
result[i++] = GLShader.internalFragment("shaders/$name.fsh")
}
for (name in geometry) {
result[i++] = GLShader.internalGeometry("shaders/$name.gsh")
}
return result as Array<GLShader>
}
open class GLInternalProgram(
state: GLStateTracker,
vertex: Collection<String>,
fragment: Collection<String>
) : GLShaderProgram(state, *loadShaders(vertex, fragment)) {
fragment: Collection<String>,
geometry: Collection<String> = listOf()
) : GLShaderProgram(state, *loadShaders(vertex, fragment, geometry)) {
constructor(state: GLStateTracker, name: String) : this(state, listOf(name), listOf(name))
}

View File

@ -75,7 +75,7 @@ open class GLShaderProgram(val state: GLStateTracker, vararg shaders: GLShader)
glGetProgramiv(pointer, GL_LINK_STATUS, success)
if (success[0] == 0) {
throw ShaderLinkException(glGetShaderInfoLog(pointer))
throw ShaderLinkException(glGetProgramInfoLog(pointer))
}
glGetError()

View File

@ -1,13 +1,16 @@
package ru.dbotthepony.kstarbound.client.gl.program
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.GLType
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableColorableProgram
import ru.dbotthepony.kstarbound.client.gl.shader.GLTransformableProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kvector.vector.Color
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
@ -50,7 +53,130 @@ class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid"
val transform = this["transform"]!!
val builder by lazy {
StreamVertexBuilder(state, FORMAT, VertexType.QUADS, 16384)
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384)
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
}
}
class GLLightProgram(state: GLStateTracker) : GLInternalProgram(state, "light") {
init {
link()
}
val baselineColor = this["baselineColor"]!!
val transform = this["transform"]!!
val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 32)
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
}
}
class GLLightOccluderProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("light_occluder"), listOf("light_occluder"), listOf("light_occluder")) {
init {
link()
}
// val baselineColor = this["baselineColor"]!!
val transform = this["transform"]!!
val lightPosition = this["lightPosition"]!!
val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS_AS_LINES, 32)
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
}
}
class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad") {
init {
link()
}
val color = this["color"]!!
private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
builder.begin()
builder.quad(-1f, -1f, 1f, 1f)
builder.upload()
builder
}
fun clearAlpha() {
use()
color.set(ALPHA)
val old = state.blend
val oldFunc = state.blendFunc
state.blend = true
state.blendFunc = BlendFunc.ONLY_ALPHA
builder.draw()
state.blend = old
state.blendFunc = oldFunc
}
fun clearColor(color: Color = Color.WHITE) {
use()
this.color.set(color)
val old = state.blend
state.blend = false
builder.draw()
state.blend = old
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
val ALPHA = Color(0f, 0f, 0f, 1f)
}
}
class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex") {
init {
link()
}
val texture = this["texture0"]!!
private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
builder.begin()
builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv())
builder.upload()
builder
}
fun run(texture: Int = 0) {
use()
this.texture.set(texture)
builder.draw()
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
}
}
class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) {
val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1024)
}
companion object {
@ -61,6 +187,11 @@ class GLLiquidProgram(state: GLStateTracker) : GLInternalProgram(state, "liquid"
class GLPrograms(val state: GLStateTracker) {
val tile by SimpleProgram("tile", ::GLTransformableColorableProgram)
val font by SimpleProgram("font", ::GLTransformableColorableProgram)
val flat by SimpleProgram("flat", ::GLTransformableColorableProgram)
val flat by SimpleProgram("flat", ::GLFlatProgram)
val liquid by lazy { GLLiquidProgram(state) }
val light by lazy { GLLightProgram(state) }
val lightOccluder by lazy { GLLightOccluderProgram(state) }
val colorQuad by lazy { GLColorQuadProgram(state) }
val textureQuad by lazy { GLTextureQuadProgram(state) }
}

View File

@ -56,5 +56,6 @@ class GLShader(
fun internalVertex(file: String) = GLShader(readInternal(file), GL_VERTEX_SHADER)
fun internalFragment(file: String) = GLShader(readInternal(file), GL_FRAGMENT_SHADER)
fun internalGeometry(file: String) = GLShader(readInternal(file), GL_GEOMETRY_SHADER)
}
}

View File

@ -4,6 +4,7 @@ import org.lwjgl.opengl.GL41.*
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram
import ru.dbotthepony.kvector.api.IFloatMatrix
import ru.dbotthepony.kvector.api.IStruct2f
import ru.dbotthepony.kvector.api.IStruct3f
import ru.dbotthepony.kvector.api.IStruct4f
import java.nio.ByteBuffer
@ -19,6 +20,14 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
return this
}
fun set(value: IStruct2f): GLUniformLocation {
program.state.ensureSameThread()
val (v0, v1) = value
glProgramUniform2f(program.pointer, pointer, v0, v1)
checkForGLError()
return this
}
fun set(value: IStruct3f): GLUniformLocation {
program.state.ensureSameThread()
val (v0, v1, v2) = value

View File

@ -26,9 +26,9 @@ private fun put(type: Int, memory: OutputStream, value: Int) {
* Класс для построения геометрии для загрузки в память видеокарты.
*/
@Suppress("unchecked_cast")
abstract class AbstractVertexBuilder<T : AbstractVertexBuilder<T>>(
abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
val attributes: GLAttributeList,
val type: VertexType,
val type: GeometryType,
) {
protected abstract val vertexMemory: OutputStream
protected abstract val elementMemory: OutputStream
@ -165,97 +165,98 @@ abstract class AbstractVertexBuilder<T : AbstractVertexBuilder<T>>(
attributeIndex++
return this as T
}
// Помощники
fun quad(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
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 as T
}
fun quadRotated(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
x: Float,
y: Float,
angle: Double,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
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 as T
}
fun quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): T {
return quad(
aabb.mins.x.toFloat(),
aabb.mins.y.toFloat(),
aabb.maxs.x.toFloat(),
aabb.maxs.y.toFloat(),
lambda
)
}
fun quadZ(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
z: Float,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
lambda(vertex().pushVec3f(x0, y0, z), 0).end()
lambda(vertex().pushVec3f(x1, y0, z), 1).end()
lambda(vertex().pushVec3f(x0, y1, z), 2).end()
lambda(vertex().pushVec3f(x1, y1, z), 3).end()
return this as T
}
fun quadRotatedZ(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
z: Float,
x: Float,
y: Float,
angle: Double,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
val s = sin(angle).toFloat()
val c = cos(angle).toFloat()
lambda(vertex().pushVec3f(x + x0 * c - s * y0, y + s * x0 + c * y0, z), 0).end()
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 as T
}
}
// Помощники
fun <T : AbstractVertexBuilder<T>> T.quad(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
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
}
fun <T : AbstractVertexBuilder<T>> T.quadRotated(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
x: Float,
y: Float,
angle: Double,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
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
}
fun <T : AbstractVertexBuilder<T>> T.quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): T {
return quad(
aabb.mins.x.toFloat(),
aabb.mins.y.toFloat(),
aabb.maxs.x.toFloat(),
aabb.maxs.y.toFloat(),
lambda
)
}
fun <T : AbstractVertexBuilder<T>> T.quadZ(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
z: Float,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
lambda(vertex().pushVec3f(x0, y0, z), 0).end()
lambda(vertex().pushVec3f(x1, y0, z), 1).end()
lambda(vertex().pushVec3f(x0, y1, z), 2).end()
lambda(vertex().pushVec3f(x1, y1, z), 3).end()
return this
}
fun <T : AbstractVertexBuilder<T>> T.quadRotatedZ(
x0: Float,
y0: Float,
x1: Float,
y1: Float,
z: Float,
x: Float,
y: Float,
angle: Double,
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
): T {
check(type.elements == 4) { "Currently building $type" }
val s = sin(angle).toFloat()
val c = cos(angle).toFloat()
lambda(vertex().pushVec3f(x + x0 * c - s * y0, y + s * x0 + c * y0, z), 0).end()
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
}

View File

@ -6,24 +6,24 @@ import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream
open class DirectVertexBuilder<T : DirectVertexBuilder<T>>(
attributes: GLAttributeList,
type: VertexType,
type: GeometryType,
val maxElements: Int,
) : AbstractVertexBuilder<DirectVertexBuilder<T>>(attributes, type) {
val maxIndexCount = maxElements * type.indicies.size
val maxVertexCount = maxElements * type.elements
override val vertexMemory = ByteBufferOutputStream.directLE(maxVertexCount)
override val elementMemory = ByteBufferOutputStream.directLE(maxIndexCount)
override val elementIndexType: Int = when (maxIndexCount) {
final override val elementIndexType: Int = when (maxIndexCount) {
// api performance issue 102: glDrawElements uses element index type 'GL_UNSIGNED_BYTE' that is not optimal for the current hardware configuration; consider using 'GL_UNSIGNED_SHORT' instead
// in 0 .. 255 -> GL_UNSIGNED_BYTE
in 0 .. 65535 -> GL46.GL_UNSIGNED_SHORT
else -> GL46.GL_UNSIGNED_INT
}
final override val vertexMemory = ByteBufferOutputStream.directLE(maxVertexCount * attributes.stride)
final override val elementMemory = ByteBufferOutputStream.directLE(maxIndexCount * (if (elementIndexType == GL46.GL_UNSIGNED_SHORT) 2 else 4))
override fun ensureIndexCapacity() {
if (vertexCount >= maxVertexCount) {
if (vertexCount > maxVertexCount) {
throw IndexOutOfBoundsException("Vertex count overflow (can hold max of $maxElements elements, that's $maxVertexCount vertexes)")
}
}

View File

@ -1,9 +1,9 @@
package ru.dbotthepony.kstarbound.client.gl.vertex
enum class VertexType(val elements: Int, val indicies: IntArray) {
enum class GeometryType(val elements: Int, val indicies: IntArray) {
LINES(2, intArrayOf(0, 1)),
TRIANGLES(3, intArrayOf(0, 1, 2)),
QUADS(4, intArrayOf(0, 1, 2, 1, 2, 3)),
QUADS_AS_LINES(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3)),
QUADS_AS_LINES_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
}
}

View File

@ -17,7 +17,7 @@ import java.nio.ByteBuffer
*/
class HeapVertexBuilder(
attributes: GLAttributeList,
type: VertexType,
type: GeometryType,
) : AbstractVertexBuilder<HeapVertexBuilder>(attributes, type) {
override val vertexMemory = FastByteArrayOutputStream()
override val elementMemory = FastByteArrayOutputStream()

View File

@ -11,7 +11,7 @@ import java.io.Closeable
class StreamVertexBuilder(
val state: GLStateTracker,
attributes: GLAttributeList,
type: VertexType,
type: GeometryType,
maxElements: Int,
) : DirectVertexBuilder<StreamVertexBuilder>(attributes, type, maxElements), Closeable {
private val vao = state.newVAO()

View File

@ -5,6 +5,7 @@ import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.ClientChunk
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
import ru.dbotthepony.kvector.matrix.Matrix4fStack

View File

@ -9,7 +9,8 @@ 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.HeapVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kvector.matrix.Matrix4fStack
import ru.dbotthepony.kvector.vector.Color
@ -307,7 +308,7 @@ class Font(
ebo.bind()
vbo.bind()
val builder = HeapVertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, VertexType.QUADS)
val builder = HeapVertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, GeometryType.QUADS)
builder.quad(0f, 0f, width, height, QuadTransformers.uv())
builder.upload(vbo, ebo, GL_STATIC_DRAW)

View File

@ -168,7 +168,7 @@ private enum class TileRenderTesselateResult {
HALT
}
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, VertexType.QUADS)
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, GeometryType.QUADS)
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
override fun test(thisTile: ITileState, otherTile: ITileState): Boolean {

View File

@ -8,6 +8,5 @@ uniform sampler2D _texture;
uniform vec4 _color;
void main() {
float sampled = texture(_texture, _uv_out).r;
_color_out = vec4(_color.x * sampled, _color.y * sampled, _color.z * sampled, _color.w * sampled);
_color_out = _color * texture(_texture, _uv_out).r;
}

View File

@ -0,0 +1,11 @@
#version 460
in vec2 oUVCoords;
out vec4 resultColor;
uniform vec4 baselineColor;
void main() {
resultColor = vec4(baselineColor) * smoothstep(0.0, 1.0, 1.0 - sqrt(pow(oUVCoords.x - 0.5, 2.0) + pow(oUVCoords.y - 0.5, 2.0)) * 2.0);
}

View File

@ -0,0 +1,14 @@
#version 460
layout (location = 0) in vec2 vertexPos;
layout (location = 1) in vec2 uvCoords;
out vec2 oUVCoords;
uniform mat4 transform;
void main() {
gl_Position = transform * vec4(vertexPos, 0.0, 1.0);
oUVCoords = uvCoords;
}

View File

@ -0,0 +1,10 @@
#version 460
uniform vec2 lightPosition;
out vec4 resultColor;
void main() {
resultColor = vec4(0.0, 0.0, 0.0, 0.0);
}

View File

@ -0,0 +1,36 @@
#version 460
layout (lines) in;
layout (triangle_strip, max_vertices = 5) out;
uniform mat4 transform;
uniform vec2 lightPosition;
in vec2 originalPos[];
void main() {
vec4 a = gl_in[0].gl_Position;
vec4 b = gl_in[1].gl_Position;
vec2 aInv = originalPos[0];
vec2 bInv = originalPos[1];
gl_Position = b;
EmitVertex();
gl_Position = a;
EmitVertex();
gl_Position = transform * vec4(aInv + (aInv - lightPosition) * 10000, 0, 1);
EmitVertex();
gl_Position = transform * vec4(bInv + (bInv - lightPosition) * 10000, 0, 1);
EmitVertex();
gl_Position = b;
EmitVertex();
EndPrimitive();
}

View File

@ -0,0 +1,13 @@
#version 460
layout (location = 0) in vec2 vertexPos;
uniform mat4 transform;
out vec2 originalPos;
void main() {
gl_Position = transform * vec4(vertexPos, 0.0, 1.0);
originalPos = vertexPos;
}

View File

@ -0,0 +1,10 @@
#version 460
out vec4 resultColor;
uniform vec4 color;
void main() {
resultColor = color;
}

View File

@ -0,0 +1,8 @@
#version 460
layout (location = 0) in vec2 vertexPos;
void main() {
gl_Position = vec4(vertexPos, 0.0, 1.0);
}

View File

@ -0,0 +1,11 @@
#version 460
out vec4 resultColor;
in vec2 uvPos;
uniform sampler2D texture0;
void main() {
resultColor = texture(texture0, uvPos);
}

View File

@ -0,0 +1,12 @@
#version 460
layout (location = 0) in vec2 vertexPos;
layout (location = 1) in vec2 inUVPos;
out vec2 uvPos;
void main() {
gl_Position = vec4(vertexPos, 0.0, 1.0);
uvPos = inUVPos;
}