Remove unused graphics stuff, merge GLStateTracker with StarboundClient
This commit is contained in:
parent
ef52700ff2
commit
3ad0e78c10
@ -82,7 +82,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.5.0")
|
implementation("ru.dbotthepony:kvector:2.6.0")
|
||||||
|
|
||||||
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
||||||
}
|
}
|
||||||
|
@ -157,10 +157,10 @@ fun main() {
|
|||||||
//client.camera.pos = Vector2f(0f, 0f)
|
//client.camera.pos = Vector2f(0f, 0f)
|
||||||
|
|
||||||
client.onDrawGUI {
|
client.onDrawGUI {
|
||||||
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
|
client.font.render("${ent.position}", y = 100f, scale = 0.25f)
|
||||||
client.gl.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
|
client.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
|
||||||
client.gl.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
|
client.font.render("Camera: ${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
|
||||||
client.gl.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos.toDoubleVector())}", y = 160f, scale = 0.25f)
|
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos.toDoubleVector())}", y = 160f, scale = 0.25f)
|
||||||
}
|
}
|
||||||
|
|
||||||
client.onPreDrawWorld {
|
client.onPreDrawWorld {
|
||||||
@ -219,10 +219,10 @@ fun main() {
|
|||||||
lightRenderer.renderOutputAdditive()
|
lightRenderer.renderOutputAdditive()
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
client.gl.box2dRenderer.drawShapes = false
|
client.box2dRenderer.drawShapes = false
|
||||||
client.gl.box2dRenderer.drawPairs = false
|
client.box2dRenderer.drawPairs = false
|
||||||
client.gl.box2dRenderer.drawAABB = false
|
client.box2dRenderer.drawAABB = false
|
||||||
client.gl.box2dRenderer.drawJoints = false
|
client.box2dRenderer.drawJoints = false
|
||||||
|
|
||||||
//ent.spawn()
|
//ent.spawn()
|
||||||
|
|
||||||
|
@ -7,6 +7,4 @@ data class ClientSettings(
|
|||||||
* Масштаб в единицу означает что один Starbound Unit будет равен 8 пикселям на экране
|
* Масштаб в единицу означает что один Starbound Unit будет равен 8 пикселям на экране
|
||||||
*/
|
*/
|
||||||
var zoom: Float = 2f,
|
var zoom: Float = 2f,
|
||||||
|
|
||||||
var debugCollisions: Boolean = false,
|
|
||||||
)
|
)
|
||||||
|
@ -1,34 +1,62 @@
|
|||||||
package ru.dbotthepony.kstarbound.client
|
package ru.dbotthepony.kstarbound.client
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.BufferUtils
|
import org.lwjgl.BufferUtils
|
||||||
import org.lwjgl.glfw.Callbacks
|
import org.lwjgl.glfw.Callbacks
|
||||||
import org.lwjgl.glfw.GLFW
|
import org.lwjgl.glfw.GLFW
|
||||||
import org.lwjgl.glfw.GLFWErrorCallback
|
import org.lwjgl.glfw.GLFWErrorCallback
|
||||||
|
import org.lwjgl.opengl.GL
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
|
import org.lwjgl.opengl.GLCapabilities
|
||||||
import org.lwjgl.system.MemoryStack
|
import org.lwjgl.system.MemoryStack
|
||||||
import org.lwjgl.system.MemoryUtil
|
import org.lwjgl.system.MemoryUtil
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
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.BlendFunc
|
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.ScissorRect
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VBOType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.properties.GLStateFuncTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.properties.GLStateGenericTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.properties.GLStateIntTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.properties.GLStateSwitchTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.properties.TexturesTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLShader
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.input.UserInput
|
import ru.dbotthepony.kstarbound.client.input.UserInput
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.Camera
|
import ru.dbotthepony.kstarbound.client.render.Camera
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.Font
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||||
import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
||||||
|
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.JVMTimeSource
|
import ru.dbotthepony.kstarbound.util.JVMTimeSource
|
||||||
import ru.dbotthepony.kstarbound.util.PausableTimeSource
|
|
||||||
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.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
||||||
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
|
||||||
@ -36,17 +64,45 @@ 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 ru.dbotthepony.kvector.vector.Vector3f
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.ref.Cleaner
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
import java.time.Duration
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.locks.LockSupport
|
import java.util.concurrent.locks.LockSupport
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class StarboundClient : Closeable {
|
class StarboundClient : Closeable {
|
||||||
val time = PausableTimeSource(JVMTimeSource.INSTANCE)
|
|
||||||
val window: Long
|
val window: Long
|
||||||
val camera = Camera(this)
|
val camera = Camera(this)
|
||||||
val input = UserInput()
|
val input = UserInput()
|
||||||
val gl: GLStateTracker
|
val thread: Thread = Thread.currentThread()
|
||||||
|
val capabilities: GLCapabilities
|
||||||
|
|
||||||
|
var viewportX: Int = 0
|
||||||
|
private set
|
||||||
|
var viewportY: Int = 0
|
||||||
|
private set
|
||||||
|
var viewportWidth: Int = 0
|
||||||
|
private set
|
||||||
|
var viewportHeight: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var viewportCellX = 0
|
||||||
|
private set
|
||||||
|
var viewportCellY = 0
|
||||||
|
private set
|
||||||
|
var viewportCellWidth = 0
|
||||||
|
private set
|
||||||
|
var viewportCellHeight = 0
|
||||||
|
private set
|
||||||
|
var viewportRectangle = AABB.rectangle(Vector2d.ZERO, 0.0, 0.0)
|
||||||
|
private set
|
||||||
|
|
||||||
|
var fullbright = true
|
||||||
|
|
||||||
var gameTerminated = false
|
var gameTerminated = false
|
||||||
private set
|
private set
|
||||||
@ -65,9 +121,519 @@ class StarboundClient : Closeable {
|
|||||||
private set
|
private set
|
||||||
get() = Matrix4f.unmodifiable(field)
|
get() = Matrix4f.unmodifiable(field)
|
||||||
|
|
||||||
|
var isRenderingGame = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
private val scissorStack = LinkedList<ScissorRect>()
|
||||||
|
private val cleanerBacklog = ArrayList<() -> Unit>()
|
||||||
|
private val onDrawGUI = ArrayList<() -> Unit>()
|
||||||
|
private val onPreDrawWorld = ArrayList<(LayeredRenderer) -> Unit>()
|
||||||
|
private val onPostDrawWorld = ArrayList<() -> Unit>()
|
||||||
|
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
||||||
|
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
||||||
|
private val terminateCallbacks = ArrayList<() -> Unit>()
|
||||||
private val startupTextList = ArrayList<String>()
|
private val startupTextList = ArrayList<String>()
|
||||||
private var finishStartupRendering = System.currentTimeMillis() + 4000L
|
private var finishStartupRendering = System.currentTimeMillis() + 4000L
|
||||||
|
|
||||||
|
private val cleaner = Cleaner.create { r ->
|
||||||
|
val thread = Thread(r, "OpenGL Cleaner for '${thread.name}'")
|
||||||
|
thread.priority = 2
|
||||||
|
thread
|
||||||
|
}
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var objectsCleaned = 0L
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var gcHits = 0L
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
check(CLIENTS.get() == null) { "Already has OpenGL context existing at ${Thread.currentThread()}!" }
|
||||||
|
CLIENTS.set(this)
|
||||||
|
|
||||||
|
lock.lock()
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!glfwInitialized) {
|
||||||
|
check(GLFW.glfwInit()) { "Unable to initialize GLFW" }
|
||||||
|
glfwInitialized = true
|
||||||
|
|
||||||
|
GLFWErrorCallback.create { error, description ->
|
||||||
|
LOGGER.error("LWJGL error {}: {}", error, description)
|
||||||
|
}.set()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFW.glfwDefaultWindowHints()
|
||||||
|
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE)
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE)
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4)
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 6)
|
||||||
|
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE)
|
||||||
|
|
||||||
|
window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
|
||||||
|
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
|
||||||
|
startupTextList.add("Created GLFW window")
|
||||||
|
|
||||||
|
input.installCallback(window)
|
||||||
|
|
||||||
|
GLFW.glfwMakeContextCurrent(window)
|
||||||
|
|
||||||
|
// This line is critical for LWJGL's interoperation with GLFW's
|
||||||
|
// OpenGL context, or any context that is managed externally.
|
||||||
|
// LWJGL detects the context that is current in the current thread,
|
||||||
|
// creates the GLCapabilities instance and makes the OpenGL
|
||||||
|
// bindings available for use.
|
||||||
|
capabilities = GL.createCapabilities()
|
||||||
|
|
||||||
|
GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h ->
|
||||||
|
if (w == 0 || h == 0) {
|
||||||
|
isRenderingGame = false
|
||||||
|
} else {
|
||||||
|
isRenderingGame = true
|
||||||
|
setViewport(0, 0, w, h)
|
||||||
|
viewportMatrixScreen = updateViewportMatrixScreen()
|
||||||
|
viewportMatrixWorld = updateViewportMatrixWorld()
|
||||||
|
|
||||||
|
for (callback in onViewportChanged) {
|
||||||
|
callback.invoke(w, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val stack = MemoryStack.stackPush()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val pWidth = stack.mallocInt(1)
|
||||||
|
val pHeight = stack.mallocInt(1)
|
||||||
|
|
||||||
|
GLFW.glfwGetWindowSize(window, pWidth, pHeight)
|
||||||
|
|
||||||
|
val vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor())!!
|
||||||
|
|
||||||
|
GLFW.glfwSetWindowPos(
|
||||||
|
window,
|
||||||
|
(vidmode.width() - pWidth[0]) / 2,
|
||||||
|
(vidmode.height() - pHeight[0]) / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
setViewport(0, 0, pWidth[0], pHeight[0])
|
||||||
|
viewportMatrixScreen = updateViewportMatrixScreen()
|
||||||
|
viewportMatrixWorld = updateViewportMatrixWorld()
|
||||||
|
} finally {
|
||||||
|
stack.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// vsync
|
||||||
|
GLFW.glfwSwapInterval(0)
|
||||||
|
|
||||||
|
GLFW.glfwShowWindow(window)
|
||||||
|
putDebugLog("Initialized GLFW window")
|
||||||
|
}
|
||||||
|
|
||||||
|
val programs = GLPrograms()
|
||||||
|
|
||||||
|
val flat2DLines by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.LINES) }
|
||||||
|
val flat2DTriangles by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.TRIANGLES) }
|
||||||
|
val flat2DTexturedQuads by lazy { StreamVertexBuilder(GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS) }
|
||||||
|
val quadWireframe by lazy { StreamVertexBuilder(GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME) }
|
||||||
|
|
||||||
|
// минимальное время хранения 5 минут и...
|
||||||
|
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(Duration.ofMinutes(5))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// ...бесконечное хранение пока кто-то все ещё использует текстуру
|
||||||
|
private val named2DTextures1: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
||||||
|
.weakValues()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val missingTexture: GLTexture2D by lazy {
|
||||||
|
newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips().also {
|
||||||
|
it.textureMinFilter = GL_NEAREST
|
||||||
|
it.textureMagFilter = GL_NEAREST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val missingTexturePath = "/assetmissing.png"
|
||||||
|
|
||||||
|
val matrixStack = Matrix4fStack()
|
||||||
|
val freeType = FreeType()
|
||||||
|
val font = Font()
|
||||||
|
val box2dRenderer = Box2DRenderer()
|
||||||
|
|
||||||
|
fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
|
||||||
|
val cleanable = cleaner.register(ref) {
|
||||||
|
objectsCleaned++
|
||||||
|
|
||||||
|
if (isSameThread()) {
|
||||||
|
fn(nativeRef)
|
||||||
|
checkForGLError()
|
||||||
|
} else {
|
||||||
|
gcHits++
|
||||||
|
|
||||||
|
synchronized(cleanerBacklog) {
|
||||||
|
cleanerBacklog.add {
|
||||||
|
fn(nativeRef)
|
||||||
|
checkForGLError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanable
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanup() {
|
||||||
|
synchronized(cleanerBacklog) {
|
||||||
|
for (lambda in cleanerBacklog) {
|
||||||
|
lambda.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanerBacklog.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var blend by GLStateSwitchTracker(GL_BLEND)
|
||||||
|
var scissor by GLStateSwitchTracker(GL_SCISSOR_TEST)
|
||||||
|
|
||||||
|
var cull by GLStateSwitchTracker(GL_CULL_FACE)
|
||||||
|
var cullMode by GLStateFuncTracker(::glCullFace, GL_BACK)
|
||||||
|
|
||||||
|
var textureUnpackAlignment by GLStateIntTracker(::glPixelStorei, GL_UNPACK_ALIGNMENT, 4)
|
||||||
|
|
||||||
|
var scissorRect by GLStateGenericTracker(ScissorRect(0, 0, 0, 0)) {
|
||||||
|
// require(it.x >= 0) { "Invalid X ${it.x}"}
|
||||||
|
// require(it.y >= 0) { "Invalid Y ${it.y}"}
|
||||||
|
|
||||||
|
require(it.width >= 0) { "Invalid width ${it.width}"}
|
||||||
|
require(it.height >= 0) { "Invalid height ${it.height}"}
|
||||||
|
|
||||||
|
glScissor(it.x, it.y, it.width, it.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
||||||
|
|
||||||
|
var VBO: VertexBufferObject? = null
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (field !== value) {
|
||||||
|
isMe(value?.client)
|
||||||
|
require(value?.isArray != false) { "Provided buffer object is not of Array type" }
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, value?.pointer ?: 0)
|
||||||
|
checkForGLError("Setting Vertex Buffer Object")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var EBO: VertexBufferObject? = null
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (field !== value) {
|
||||||
|
isMe(value?.client)
|
||||||
|
require(value?.isElementArray != false) { "Provided buffer object is not of Array type" }
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, value?.pointer ?: 0)
|
||||||
|
checkForGLError("Setting Element Buffer Object")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var VAO: VertexArrayObject? = null
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (field !== value) {
|
||||||
|
isMe(value?.client)
|
||||||
|
glBindVertexArray(value?.pointer ?: 0)
|
||||||
|
checkForGLError("Setting Vertex Array Object")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var readFramebuffer: GLFrameBuffer? = null
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
if (field === value) return
|
||||||
|
isMe(value?.client)
|
||||||
|
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?.client)
|
||||||
|
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
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (value !== field) {
|
||||||
|
isMe(value?.client)
|
||||||
|
glUseProgram(value?.pointer ?: 0)
|
||||||
|
checkForGLError("Setting shader program")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeTexture = 0
|
||||||
|
set(value) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (field != value) {
|
||||||
|
require(value >= 0) { "Invalid texture block $value" }
|
||||||
|
require(value < 80) { "Too big texture block index $value, OpenGL 4.6 guarantee only 80!" }
|
||||||
|
glActiveTexture(GL_TEXTURE0 + value)
|
||||||
|
checkForGLError()
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var texture2D: GLTexture2D? by TexturesTracker(80)
|
||||||
|
|
||||||
|
var clearColor by GLStateGenericTracker<IStruct4f>(RGBAColor.WHITE) {
|
||||||
|
val (r, g, b, a) = it
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setViewport(x: Int, y: Int, width: Int, height: Int) {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
if (viewportX != x || viewportY != y || viewportWidth != width || viewportHeight != height) {
|
||||||
|
glViewport(x, y, width, height)
|
||||||
|
checkForGLError("Setting viewport")
|
||||||
|
viewportX = x
|
||||||
|
viewportY = y
|
||||||
|
viewportWidth = width
|
||||||
|
viewportHeight = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pushScissorRect(x: Float, y: Float, width: Float, height: Float) {
|
||||||
|
return pushScissorRect(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
fun pushScissorRect(x: Int, y: Int, width: Int, height: Int) {
|
||||||
|
var x = x
|
||||||
|
var y = y
|
||||||
|
var width = width
|
||||||
|
var height = height
|
||||||
|
|
||||||
|
val peek = scissorStack.lastOrNull()
|
||||||
|
|
||||||
|
if (peek != null) {
|
||||||
|
x = x.coerceAtLeast(peek.x)
|
||||||
|
y = y.coerceAtLeast(peek.y)
|
||||||
|
width = width.coerceAtMost(peek.width)
|
||||||
|
height = height.coerceAtMost(peek.height)
|
||||||
|
|
||||||
|
if (peek.x == x && peek.y == y && peek.width == width && peek.height == height) {
|
||||||
|
scissorStack.add(peek)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val rect = ScissorRect(x, y, width, height)
|
||||||
|
scissorStack.add(rect)
|
||||||
|
scissorRect = rect
|
||||||
|
scissor = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun popScissorRect() {
|
||||||
|
scissorStack.removeLast()
|
||||||
|
|
||||||
|
val peek = scissorStack.lastOrNull()
|
||||||
|
|
||||||
|
if (peek == null) {
|
||||||
|
scissor = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val y = viewportHeight - peek.y - peek.height
|
||||||
|
scissorRect = ScissorRect(peek.x, y, peek.width, peek.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentScissorRect get() = scissorStack.lastOrNull()
|
||||||
|
|
||||||
|
fun ensureSameThread() {
|
||||||
|
if (thread !== Thread.currentThread()) {
|
||||||
|
throw IllegalAccessException("Trying to access $this outside of $thread!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSameThread() = thread === Thread.currentThread()
|
||||||
|
fun newTexture(name: String = "<unknown>") = GLTexture2D(name)
|
||||||
|
|
||||||
|
fun loadTexture(path: String): GLTexture2D {
|
||||||
|
ensureSameThread()
|
||||||
|
|
||||||
|
return named2DTextures0.get(path) {
|
||||||
|
named2DTextures1.get(it) {
|
||||||
|
val data = Image.get(it)
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
LOGGER.error("Texture {} is missing! Falling back to {}", it, missingTexturePath)
|
||||||
|
missingTexture
|
||||||
|
} else {
|
||||||
|
newTexture(it).upload(data).also {
|
||||||
|
it.textureMinFilter = GL_NEAREST
|
||||||
|
it.textureMagFilter = GL_NEAREST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(obj: VertexBufferObject): VertexBufferObject {
|
||||||
|
if (obj.type == VBOType.ARRAY)
|
||||||
|
VBO = obj
|
||||||
|
else
|
||||||
|
EBO = obj
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unbind(obj: VertexBufferObject): VertexBufferObject {
|
||||||
|
if (obj.type == VBOType.ARRAY)
|
||||||
|
if (obj == VBO)
|
||||||
|
VBO = null
|
||||||
|
else
|
||||||
|
if (obj == EBO)
|
||||||
|
EBO = null
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind(obj: VertexArrayObject): VertexArrayObject {
|
||||||
|
VAO = obj
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unbind(obj: VertexArrayObject): VertexArrayObject {
|
||||||
|
if (obj == VAO)
|
||||||
|
VAO = null
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newVBO() = VertexBufferObject.vbo()
|
||||||
|
fun newEBO() = VertexBufferObject.ebo()
|
||||||
|
fun newVAO() = VertexArrayObject()
|
||||||
|
|
||||||
|
inline fun quadWireframe(color: RGBAColor = RGBAColor.WHITE, lambda: (VertexBuilder) -> Unit) {
|
||||||
|
val builder = quadWireframe
|
||||||
|
|
||||||
|
builder.builder.begin()
|
||||||
|
lambda.invoke(builder.builder)
|
||||||
|
builder.upload()
|
||||||
|
|
||||||
|
programs.flat.use()
|
||||||
|
programs.flat.color = color
|
||||||
|
programs.flat.transform = matrixStack.last()
|
||||||
|
|
||||||
|
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 fragment(file: File) = GLShader(file, GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
|
fun vertex(contents: String) = GLShader(contents, GL_VERTEX_SHADER)
|
||||||
|
fun fragment(contents: String) = GLShader(contents, GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
fun putDebugLog(text: String, replace: Boolean = false) {
|
fun putDebugLog(text: String, replace: Boolean = false) {
|
||||||
if (replace) {
|
if (replace) {
|
||||||
if (startupTextList.isEmpty()) {
|
if (startupTextList.isEmpty()) {
|
||||||
@ -82,6 +648,12 @@ class StarboundClient : Closeable {
|
|||||||
finishStartupRendering = System.currentTimeMillis() + 4000L
|
finishStartupRendering = System.currentTimeMillis() + 4000L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isMe(state: StarboundClient?) {
|
||||||
|
if (state != null && state != this) {
|
||||||
|
throw InvalidArgumentException("Provided object does not belong to $this state tracker (belongs to $state)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateViewportMatrixScreen(): Matrix4f {
|
private fun updateViewportMatrixScreen(): Matrix4f {
|
||||||
return Matrix4f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f).translate(Vector3f(z = 2f))
|
return Matrix4f.ortho(0f, viewportWidth.toFloat(), 0f, viewportHeight.toFloat(), 0.1f, 100f).translate(Vector3f(z = 2f))
|
||||||
}
|
}
|
||||||
@ -157,96 +729,21 @@ class StarboundClient : Closeable {
|
|||||||
return Vector2f(relativeX, relativeY)
|
return Vector2f(relativeX, relativeY)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isRenderingGame = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
init {
|
|
||||||
GLFWErrorCallback.create { error, description ->
|
|
||||||
LOGGER.error("LWJGL error {}: {}", error, description)
|
|
||||||
}.set()
|
|
||||||
|
|
||||||
check(GLFW.glfwInit()) { "Unable to initialize GLFW" }
|
|
||||||
|
|
||||||
GLFW.glfwDefaultWindowHints()
|
|
||||||
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE)
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE)
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4)
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 6)
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE)
|
|
||||||
|
|
||||||
window = GLFW.glfwCreateWindow(800, 600, "KStarbound", MemoryUtil.NULL, MemoryUtil.NULL)
|
|
||||||
require(window != MemoryUtil.NULL) { "Unable to create GLFW window" }
|
|
||||||
startupTextList.add("Created GLFW window")
|
|
||||||
|
|
||||||
input.installCallback(window)
|
|
||||||
|
|
||||||
GLFW.glfwMakeContextCurrent(window)
|
|
||||||
gl = GLStateTracker(this)
|
|
||||||
|
|
||||||
GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h ->
|
|
||||||
if (w == 0 || h == 0) {
|
|
||||||
isRenderingGame = false
|
|
||||||
} else {
|
|
||||||
isRenderingGame = true
|
|
||||||
gl.setViewport(0, 0, w, h)
|
|
||||||
viewportMatrixScreen = updateViewportMatrixScreen()
|
|
||||||
viewportMatrixWorld = updateViewportMatrixWorld()
|
|
||||||
|
|
||||||
for (callback in onViewportChanged) {
|
|
||||||
callback.invoke(w, h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val stack = MemoryStack.stackPush()
|
|
||||||
|
|
||||||
try {
|
|
||||||
val pWidth = stack.mallocInt(1)
|
|
||||||
val pHeight = stack.mallocInt(1)
|
|
||||||
|
|
||||||
GLFW.glfwGetWindowSize(window, pWidth, pHeight)
|
|
||||||
|
|
||||||
val vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor())!!
|
|
||||||
|
|
||||||
GLFW.glfwSetWindowPos(
|
|
||||||
window,
|
|
||||||
(vidmode.width() - pWidth[0]) / 2,
|
|
||||||
(vidmode.height() - pHeight[0]) / 2
|
|
||||||
)
|
|
||||||
|
|
||||||
gl.setViewport(0, 0, pWidth[0], pHeight[0])
|
|
||||||
viewportMatrixScreen = updateViewportMatrixScreen()
|
|
||||||
viewportMatrixWorld = updateViewportMatrixWorld()
|
|
||||||
} finally {
|
|
||||||
stack.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// vsync
|
|
||||||
GLFW.glfwSwapInterval(0)
|
|
||||||
|
|
||||||
GLFW.glfwShowWindow(window)
|
|
||||||
putDebugLog("Initialized GLFW window")
|
|
||||||
}
|
|
||||||
|
|
||||||
val viewportWidth by gl::viewportWidth
|
|
||||||
val viewportHeight by gl::viewportHeight
|
|
||||||
|
|
||||||
val tileRenderers = TileRenderers(this)
|
val tileRenderers = TileRenderers(this)
|
||||||
|
|
||||||
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
var world: ClientWorld? = ClientWorld(this, 0L, Vector2i(3000, 2000), true)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
putDebugLog("Initialized OpenGL context")
|
putDebugLog("Initialized OpenGL context")
|
||||||
gl.clearColor = RGBAColor.SLATE_GRAY
|
clearColor = RGBAColor.SLATE_GRAY
|
||||||
|
|
||||||
gl.blend = true
|
blend = true
|
||||||
gl.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
||||||
}
|
}
|
||||||
|
|
||||||
var frameRenderTime = 0.0
|
var frameRenderTime = 0.0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private var lastRender = JVMTimeSource.INSTANCE.seconds
|
||||||
val framesPerSecond get() = if (frameRenderTime == 0.0) 1.0 else 1.0 / frameRenderTime
|
val framesPerSecond get() = if (frameRenderTime == 0.0) 1.0 else 1.0 / frameRenderTime
|
||||||
|
|
||||||
private val frameRenderTimes = DoubleArray(60) { 1.0 }
|
private val frameRenderTimes = DoubleArray(60) { 1.0 }
|
||||||
@ -267,17 +764,6 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
val settings = ClientSettings()
|
val settings = ClientSettings()
|
||||||
|
|
||||||
var viewportCellX = 0
|
|
||||||
private set
|
|
||||||
var viewportCellY = 0
|
|
||||||
private set
|
|
||||||
var viewportCellWidth = 0
|
|
||||||
private set
|
|
||||||
var viewportCellHeight = 0
|
|
||||||
private set
|
|
||||||
var viewportRectangle = AABB.rectangle(Vector2d.ZERO, 0.0, 0.0)
|
|
||||||
private set
|
|
||||||
|
|
||||||
val viewportCells: ICellAccess = object : ICellAccess {
|
val viewportCells: ICellAccess = object : ICellAccess {
|
||||||
override fun getCell(x: Int, y: Int): IChunkCell? {
|
override fun getCell(x: Int, y: Int): IChunkCell? {
|
||||||
return world?.getCell(x + viewportCellX, y + viewportCellY)
|
return world?.getCell(x + viewportCellX, y + viewportCellY)
|
||||||
@ -291,11 +777,9 @@ class StarboundClient : Closeable {
|
|||||||
var viewportLighting = LightCalculator(viewportCells, viewportCellWidth, viewportCellHeight)
|
var viewportLighting = LightCalculator(viewportCells, viewportCellWidth, viewportCellHeight)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val viewportLightingTexture = gl.newTexture("Viewport Lighting")
|
val viewportLightingTexture = newTexture("Viewport Lighting")
|
||||||
private var viewportLightingMem: ByteBuffer? = null
|
private var viewportLightingMem: ByteBuffer? = null
|
||||||
|
|
||||||
var fullbright = true
|
|
||||||
|
|
||||||
fun updateViewportParams() {
|
fun updateViewportParams() {
|
||||||
viewportRectangle = AABB.rectangle(
|
viewportRectangle = AABB.rectangle(
|
||||||
camera.pos.toDoubleVector(),
|
camera.pos.toDoubleVector(),
|
||||||
@ -319,12 +803,6 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val onDrawGUI = ArrayList<() -> Unit>()
|
|
||||||
private val onPreDrawWorld = ArrayList<(LayeredRenderer) -> Unit>()
|
|
||||||
private val onPostDrawWorld = ArrayList<() -> Unit>()
|
|
||||||
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
|
||||||
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
|
||||||
|
|
||||||
fun onViewportChanged(callback: (width: Int, height: Int) -> Unit) {
|
fun onViewportChanged(callback: (width: Int, height: Int) -> Unit) {
|
||||||
onViewportChanged.add(callback)
|
onViewportChanged.add(callback)
|
||||||
}
|
}
|
||||||
@ -345,10 +823,8 @@ class StarboundClient : Closeable {
|
|||||||
onPostDrawWorldOnce.add(lambda)
|
onPostDrawWorldOnce.add(lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastRender = JVMTimeSource.INSTANCE.seconds
|
|
||||||
|
|
||||||
fun renderFrame(): Boolean {
|
fun renderFrame(): Boolean {
|
||||||
gl.ensureSameThread()
|
ensureSameThread()
|
||||||
|
|
||||||
val diff = JVMTimeSource.INSTANCE.seconds - lastRender
|
val diff = JVMTimeSource.INSTANCE.seconds - lastRender
|
||||||
|
|
||||||
@ -367,7 +843,7 @@ class StarboundClient : Closeable {
|
|||||||
val world = world
|
val world = world
|
||||||
|
|
||||||
if (!isRenderingGame) {
|
if (!isRenderingGame) {
|
||||||
gl.cleanup()
|
cleanup()
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
|
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
@ -385,11 +861,11 @@ class StarboundClient : Closeable {
|
|||||||
if (Starbound.initialized)
|
if (Starbound.initialized)
|
||||||
world.think()
|
world.think()
|
||||||
|
|
||||||
gl.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)
|
||||||
gl.matrixStack.clear(viewportMatrixWorld)
|
matrixStack.clear(viewportMatrixWorld)
|
||||||
|
|
||||||
gl.matrixStack.push().last()
|
matrixStack.push().last()
|
||||||
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
|
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 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) // перемещаем вид к камере
|
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
|
||||||
@ -409,7 +885,7 @@ class StarboundClient : Closeable {
|
|||||||
layers = layers,
|
layers = layers,
|
||||||
size = viewportRectangle)
|
size = viewportRectangle)
|
||||||
|
|
||||||
layers.render(gl.matrixStack)
|
layers.render(matrixStack)
|
||||||
|
|
||||||
val viewportLightingMem = viewportLightingMem
|
val viewportLightingMem = viewportLightingMem
|
||||||
|
|
||||||
@ -420,8 +896,8 @@ class StarboundClient : Closeable {
|
|||||||
viewportLighting.calculate(viewportLightingMem, viewportLighting.width.coerceAtMost(4096), viewportLighting.height.coerceAtMost(4096))
|
viewportLighting.calculate(viewportLightingMem, viewportLighting.width.coerceAtMost(4096), viewportLighting.height.coerceAtMost(4096))
|
||||||
viewportLightingMem.position(0)
|
viewportLightingMem.position(0)
|
||||||
|
|
||||||
val old = gl.textureUnpackAlignment
|
val old = textureUnpackAlignment
|
||||||
gl.textureUnpackAlignment = if (viewportLighting.width.coerceAtMost(4096) % 4 == 0) 4 else 1
|
textureUnpackAlignment = if (viewportLighting.width.coerceAtMost(4096) % 4 == 0) 4 else 1
|
||||||
|
|
||||||
viewportLightingTexture.upload(
|
viewportLightingTexture.upload(
|
||||||
GL_RGB,
|
GL_RGB,
|
||||||
@ -432,16 +908,16 @@ class StarboundClient : Closeable {
|
|||||||
viewportLightingMem
|
viewportLightingMem
|
||||||
)
|
)
|
||||||
|
|
||||||
gl.textureUnpackAlignment = old
|
textureUnpackAlignment = old
|
||||||
|
|
||||||
viewportLightingTexture.textureMinFilter = GL_LINEAR
|
viewportLightingTexture.textureMinFilter = GL_LINEAR
|
||||||
//viewportLightingTexture.textureMagFilter = GL_NEAREST
|
//viewportLightingTexture.textureMagFilter = GL_NEAREST
|
||||||
|
|
||||||
//viewportLightingTexture.generateMips()
|
//viewportLightingTexture.generateMips()
|
||||||
|
|
||||||
gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
||||||
|
|
||||||
gl.quadTexture(viewportLightingTexture) {
|
quadTexture(viewportLightingTexture) {
|
||||||
it.quad(
|
it.quad(
|
||||||
(viewportCellX).toFloat(),
|
(viewportCellX).toFloat(),
|
||||||
(viewportCellY).toFloat(),
|
(viewportCellY).toFloat(),
|
||||||
@ -451,7 +927,7 @@ class StarboundClient : Closeable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
||||||
}
|
}
|
||||||
|
|
||||||
world.physics.debugDraw()
|
world.physics.debugDraw()
|
||||||
@ -460,10 +936,10 @@ class StarboundClient : Closeable {
|
|||||||
lambda.invoke()
|
lambda.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrixStack.pop()
|
matrixStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrixStack.clear(viewportMatrixScreen)
|
matrixStack.clear(viewportMatrixScreen)
|
||||||
|
|
||||||
val thisTime = System.currentTimeMillis()
|
val thisTime = System.currentTimeMillis()
|
||||||
|
|
||||||
@ -474,20 +950,20 @@ class StarboundClient : Closeable {
|
|||||||
alpha = (finishStartupRendering - thisTime) / 1000f
|
alpha = (finishStartupRendering - thisTime) / 1000f
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrixStack.push()
|
matrixStack.push()
|
||||||
gl.matrixStack.last().translateWithMultiplication(y = viewportHeight.toFloat())
|
matrixStack.last().translateWithMultiplication(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 = gl.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))
|
||||||
gl.matrixStack.last().translateWithMultiplication(y = -size.height * 1.2f)
|
matrixStack.last().translateWithMultiplication(y = -size.height * 1.2f)
|
||||||
|
|
||||||
if (shade > 120) {
|
if (shade > 120) {
|
||||||
shade -= 10
|
shade -= 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.matrixStack.pop()
|
matrixStack.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (fn in onDrawGUI) {
|
for (fn in onDrawGUI) {
|
||||||
@ -496,8 +972,8 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
val runtime = Runtime.getRuntime()
|
val runtime = Runtime.getRuntime()
|
||||||
|
|
||||||
gl.font.render("FPS: ${(averageFramesPerSecond * 100f).toInt() / 100f}", scale = 0.4f)
|
font.render("FPS: ${(averageFramesPerSecond * 100f).toInt() / 100f}", scale = 0.4f)
|
||||||
gl.font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = gl.font.lineHeight * 0.5f, scale = 0.4f)
|
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 0.5f, scale = 0.4f)
|
||||||
|
|
||||||
GLFW.glfwSwapBuffers(window)
|
GLFW.glfwSwapBuffers(window)
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
@ -505,13 +981,11 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
camera.think(Starbound.TICK_TIME_ADVANCE)
|
camera.think(Starbound.TICK_TIME_ADVANCE)
|
||||||
|
|
||||||
gl.cleanup()
|
cleanup()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val terminateCallbacks = ArrayList<() -> Unit>()
|
|
||||||
|
|
||||||
fun onTermination(lambda: () -> Unit) {
|
fun onTermination(lambda: () -> Unit) {
|
||||||
terminateCallbacks.add(lambda)
|
terminateCallbacks.add(lambda)
|
||||||
}
|
}
|
||||||
@ -537,5 +1011,25 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger(StarboundClient::class.java)
|
private val LOGGER = LogManager.getLogger(StarboundClient::class.java)
|
||||||
|
private val CLIENTS = ThreadLocal<StarboundClient>()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun current() = checkNotNull(CLIENTS.get()) { "No client registered to current thread (${Thread.currentThread()})" }
|
||||||
|
@JvmStatic
|
||||||
|
fun currentOrNull(): StarboundClient? = CLIENTS.get()
|
||||||
|
|
||||||
|
private val lock = ReentrantLock()
|
||||||
|
@Volatile
|
||||||
|
private var glfwInitialized = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun readInternal(file: String): String {
|
||||||
|
return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader()
|
||||||
|
.let {
|
||||||
|
val read = it.readText()
|
||||||
|
it.close()
|
||||||
|
read
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,23 @@ import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER
|
|||||||
import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_COMPLETE
|
import org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_COMPLETE
|
||||||
import org.lwjgl.opengl.GL30.GL_LINEAR
|
import org.lwjgl.opengl.GL30.GL_LINEAR
|
||||||
import org.lwjgl.opengl.GL30.GL_RGB
|
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.GL_TEXTURE_2D
|
||||||
import org.lwjgl.opengl.GL30.glCheckFramebufferStatus
|
|
||||||
import org.lwjgl.opengl.GL30.glFramebufferTexture2D
|
import org.lwjgl.opengl.GL30.glFramebufferTexture2D
|
||||||
import org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus
|
import org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus
|
||||||
import org.lwjgl.opengl.GL45.glNamedFramebufferTexture
|
|
||||||
import org.lwjgl.opengl.GL46
|
import org.lwjgl.opengl.GL46
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
|
||||||
class GLFrameBuffer(val state: GLStateTracker) {
|
class GLFrameBuffer {
|
||||||
init {
|
val client = StarboundClient.current()
|
||||||
state.ensureSameThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointer = GL46.glGenFramebuffers()
|
val pointer = GL46.glGenFramebuffers()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError("Creating framebuffer")
|
checkForGLError("Creating framebuffer")
|
||||||
state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer)
|
client.registerCleanable(this, GL46::glDeleteFramebuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isComplete: Boolean get() {
|
val isComplete: Boolean get() {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
return glCheckNamedFramebufferStatus(pointer, GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE
|
return glCheckNamedFramebufferStatus(pointer, GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,16 +33,16 @@ class GLFrameBuffer(val state: GLStateTracker) {
|
|||||||
throw IllegalStateException("Already has texture attached")
|
throw IllegalStateException("Already has texture attached")
|
||||||
}
|
}
|
||||||
|
|
||||||
val texture = GLTexture2D(state, "framebuffer_$pointer")
|
val texture = GLTexture2D("framebuffer_$pointer")
|
||||||
texture.allocate(format, width, height)
|
texture.allocate(format, width, height)
|
||||||
|
|
||||||
val old = state.framebuffer
|
val old = client.framebuffer
|
||||||
bind()
|
bind()
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.pointer, 0)
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.pointer, 0)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
this.texture = texture
|
this.texture = texture
|
||||||
texture.textureMinFilter = GL_LINEAR
|
texture.textureMinFilter = GL_LINEAR
|
||||||
state.framebuffer = old
|
client.framebuffer = old
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) {
|
fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) {
|
||||||
@ -56,24 +51,24 @@ class GLFrameBuffer(val state: GLStateTracker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bind() {
|
fun bind() {
|
||||||
state.framebuffer = this
|
client.framebuffer = this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindWrite() {
|
fun bindWrite() {
|
||||||
state.writeFramebuffer = this
|
client.writeFramebuffer = this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindRead() {
|
fun bindRead() {
|
||||||
state.readFramebuffer = this
|
client.readFramebuffer = this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind() {
|
fun unbind() {
|
||||||
if (state.writeFramebuffer == this) {
|
if (client.writeFramebuffer == this) {
|
||||||
state.writeFramebuffer = null
|
client.writeFramebuffer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.readFramebuffer == this) {
|
if (client.readFramebuffer == this) {
|
||||||
state.readFramebuffer = null
|
client.readFramebuffer = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,640 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Cache
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine
|
|
||||||
import org.apache.logging.log4j.LogManager
|
|
||||||
import org.lwjgl.opengl.GL
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
|
||||||
import org.lwjgl.opengl.GLCapabilities
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.FreeType
|
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLPrograms
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.ShaderCompilationException
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.Font
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
|
||||||
import ru.dbotthepony.kvector.api.IStruct4f
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
|
||||||
import java.io.File
|
|
||||||
import java.lang.ref.Cleaner
|
|
||||||
import java.time.Duration
|
|
||||||
import java.util.*
|
|
||||||
import java.util.function.IntConsumer
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
|
|
||||||
private class GLStateSwitchTracker(private val enum: Int, private var value: Boolean = false) {
|
|
||||||
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): Boolean {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun setValue(glStateTracker: GLStateTracker, property: KProperty<*>, value: Boolean) {
|
|
||||||
glStateTracker.ensureSameThread()
|
|
||||||
|
|
||||||
if (value == this.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
glEnable(enum)
|
|
||||||
} else {
|
|
||||||
glDisable(enum)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForGLError()
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class GLStateIntTracker(private val fn: Function, private val enum: Int, private var value: Int) {
|
|
||||||
fun interface Function {
|
|
||||||
fun invoke(enum: Int, value: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): Int {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun setValue(glStateTracker: GLStateTracker, property: KProperty<*>, value: Int) {
|
|
||||||
glStateTracker.ensureSameThread()
|
|
||||||
|
|
||||||
if (value == this.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
fn.invoke(enum, value)
|
|
||||||
checkForGLError()
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class GLStateFuncTracker(private val glFunc: IntConsumer, private var value: Int) {
|
|
||||||
operator fun getValue(glStateTracker: GLStateTracker, property: KProperty<*>): Int {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun setValue(glStateTracker: GLStateTracker, property: KProperty<*>, value: Int) {
|
|
||||||
glStateTracker.ensureSameThread()
|
|
||||||
|
|
||||||
if (value == this.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
glFunc.accept(value)
|
|
||||||
checkForGLError()
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class GLStateGenericTracker<T>(private var value: T, private val callback: (T) -> Unit) : ReadWriteProperty<GLStateTracker, T> {
|
|
||||||
override fun getValue(thisRef: GLStateTracker, property: KProperty<*>): T {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: GLStateTracker, property: KProperty<*>, value: T) {
|
|
||||||
thisRef.ensureSameThread()
|
|
||||||
|
|
||||||
if (value == this.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
callback.invoke(value)
|
|
||||||
checkForGLError()
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TexturesTracker(maxValue: Int) : ReadWriteProperty<GLStateTracker, GLTexture2D?> {
|
|
||||||
private val values = arrayOfNulls<GLTexture2D>(maxValue)
|
|
||||||
|
|
||||||
override fun getValue(thisRef: GLStateTracker, property: KProperty<*>): GLTexture2D? {
|
|
||||||
return values[thisRef.activeTexture]
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: GLStateTracker, property: KProperty<*>, value: GLTexture2D?) {
|
|
||||||
thisRef.ensureSameThread()
|
|
||||||
|
|
||||||
require(value == null || thisRef === value.state) { "$value does not belong to $thisRef" }
|
|
||||||
|
|
||||||
if (values[thisRef.activeTexture] === value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
values[thisRef.activeTexture] = value
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0)
|
|
||||||
checkForGLError()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, value.pointer)
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("PropertyName", "unused")
|
|
||||||
class GLStateTracker(val client: StarboundClient) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This line is critical for LWJGL's interoperation with GLFW's
|
|
||||||
// OpenGL context, or any context that is managed externally.
|
|
||||||
// LWJGL detects the context that is current in the current thread,
|
|
||||||
// creates the GLCapabilities instance and makes the OpenGL
|
|
||||||
// bindings available for use.
|
|
||||||
val capabilities: GLCapabilities = GL.createCapabilities()
|
|
||||||
|
|
||||||
val programs = GLPrograms(this)
|
|
||||||
|
|
||||||
val flat2DLines by lazy { StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.LINES) }
|
|
||||||
val flat2DTriangles by lazy { StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.TRIANGLES) }
|
|
||||||
val flat2DTexturedQuads by lazy { StreamVertexBuilder(this, GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS) }
|
|
||||||
val quadWireframe by lazy { StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME) }
|
|
||||||
|
|
||||||
val matrixStack = Matrix4fStack()
|
|
||||||
val freeType = FreeType()
|
|
||||||
val font = Font(this)
|
|
||||||
val thread: Thread = Thread.currentThread()
|
|
||||||
val box2dRenderer = Box2DRenderer(this)
|
|
||||||
|
|
||||||
private val scissorStack = LinkedList<ScissorRect>()
|
|
||||||
private val cleanerBacklog = ArrayList<() -> Unit>()
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
var objectsCleaned = 0L
|
|
||||||
private set
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
var gcHits = 0L
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val cleaner = Cleaner.create { r ->
|
|
||||||
val thread = Thread(r, "OpenGL Cleaner for '${thread.name}'")
|
|
||||||
thread.priority = 2
|
|
||||||
thread
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
|
|
||||||
val cleanable = cleaner.register(ref) {
|
|
||||||
objectsCleaned++
|
|
||||||
|
|
||||||
if (isSameThread()) {
|
|
||||||
fn(nativeRef)
|
|
||||||
checkForGLError()
|
|
||||||
} else {
|
|
||||||
gcHits++
|
|
||||||
|
|
||||||
synchronized(cleanerBacklog) {
|
|
||||||
cleanerBacklog.add {
|
|
||||||
fn(nativeRef)
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cleanable
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cleanup() {
|
|
||||||
synchronized(cleanerBacklog) {
|
|
||||||
for (lambda in cleanerBacklog) {
|
|
||||||
lambda.invoke()
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanerBacklog.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var blend by GLStateSwitchTracker(GL_BLEND)
|
|
||||||
var scissor by GLStateSwitchTracker(GL_SCISSOR_TEST)
|
|
||||||
|
|
||||||
var cull by GLStateSwitchTracker(GL_CULL_FACE)
|
|
||||||
var cullMode by GLStateFuncTracker(::glCullFace, GL_BACK)
|
|
||||||
|
|
||||||
var textureUnpackAlignment by GLStateIntTracker(::glPixelStorei, GL_UNPACK_ALIGNMENT, 4)
|
|
||||||
|
|
||||||
var scissorRect by GLStateGenericTracker(ScissorRect(0, 0, 0, 0)) {
|
|
||||||
// require(it.x >= 0) { "Invalid X ${it.x}"}
|
|
||||||
// require(it.y >= 0) { "Invalid Y ${it.y}"}
|
|
||||||
|
|
||||||
require(it.width >= 0) { "Invalid width ${it.width}"}
|
|
||||||
require(it.height >= 0) { "Invalid height ${it.height}"}
|
|
||||||
|
|
||||||
glScissor(it.x, it.y, it.width, it.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
|
||||||
|
|
||||||
var VBO: VertexBufferObject? = null
|
|
||||||
set(value) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (field !== value) {
|
|
||||||
isMe(value?.state)
|
|
||||||
require(value?.isArray != false) { "Provided buffer object is not of Array type" }
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, value?.pointer ?: 0)
|
|
||||||
checkForGLError("Setting Vertex Buffer Object")
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var EBO: VertexBufferObject? = null
|
|
||||||
set(value) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (field !== value) {
|
|
||||||
isMe(value?.state)
|
|
||||||
require(value?.isElementArray != false) { "Provided buffer object is not of Array type" }
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, value?.pointer ?: 0)
|
|
||||||
checkForGLError("Setting Element Buffer Object")
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var VAO: VertexArrayObject? = null
|
|
||||||
set(value) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (field !== value) {
|
|
||||||
isMe(value?.state)
|
|
||||||
glBindVertexArray(value?.pointer ?: 0)
|
|
||||||
checkForGLError("Setting Vertex Array Object")
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
set(value) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (value !== field) {
|
|
||||||
isMe(value?.state)
|
|
||||||
glUseProgram(value?.pointer ?: 0)
|
|
||||||
checkForGLError("Setting shader program")
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var activeTexture = 0
|
|
||||||
set(value) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (field != value) {
|
|
||||||
require(value >= 0) { "Invalid texture block $value" }
|
|
||||||
require(value < 80) { "Too big texture block index $value, OpenGL 4.6 guarantee only 80!" }
|
|
||||||
glActiveTexture(GL_TEXTURE0 + value)
|
|
||||||
checkForGLError()
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var texture2D: GLTexture2D? by TexturesTracker(80)
|
|
||||||
|
|
||||||
var clearColor by GLStateGenericTracker<IStruct4f>(RGBAColor.WHITE) {
|
|
||||||
val (r, g, b, a) = it
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewportX: Int = 0
|
|
||||||
private set
|
|
||||||
var viewportY: Int = 0
|
|
||||||
private set
|
|
||||||
var viewportWidth: Int = 0
|
|
||||||
private set
|
|
||||||
var viewportHeight: Int = 0
|
|
||||||
private set
|
|
||||||
|
|
||||||
fun setViewport(x: Int, y: Int, width: Int, height: Int) {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
if (viewportX != x || viewportY != y || viewportWidth != width || viewportHeight != height) {
|
|
||||||
glViewport(x, y, width, height)
|
|
||||||
checkForGLError("Setting viewport")
|
|
||||||
viewportX = x
|
|
||||||
viewportY = y
|
|
||||||
viewportWidth = width
|
|
||||||
viewportHeight = height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pushScissorRect(x: Float, y: Float, width: Float, height: Float) {
|
|
||||||
return pushScissorRect(x.roundToInt(), y.roundToInt(), width.roundToInt(), height.roundToInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
|
||||||
fun pushScissorRect(x: Int, y: Int, width: Int, height: Int) {
|
|
||||||
var x = x
|
|
||||||
var y = y
|
|
||||||
var width = width
|
|
||||||
var height = height
|
|
||||||
|
|
||||||
val peek = scissorStack.lastOrNull()
|
|
||||||
|
|
||||||
if (peek != null) {
|
|
||||||
x = x.coerceAtLeast(peek.x)
|
|
||||||
y = y.coerceAtLeast(peek.y)
|
|
||||||
width = width.coerceAtMost(peek.width)
|
|
||||||
height = height.coerceAtMost(peek.height)
|
|
||||||
|
|
||||||
if (peek.x == x && peek.y == y && peek.width == width && peek.height == height) {
|
|
||||||
scissorStack.add(peek)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val rect = ScissorRect(x, y, width, height)
|
|
||||||
scissorStack.add(rect)
|
|
||||||
scissorRect = rect
|
|
||||||
scissor = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun popScissorRect() {
|
|
||||||
scissorStack.removeLast()
|
|
||||||
|
|
||||||
val peek = scissorStack.lastOrNull()
|
|
||||||
|
|
||||||
if (peek == null) {
|
|
||||||
scissor = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val y = viewportHeight - peek.y - peek.height
|
|
||||||
scissorRect = ScissorRect(peek.x, y, peek.width, peek.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentScissorRect get() = scissorStack.lastOrNull()
|
|
||||||
|
|
||||||
fun ensureSameThread() {
|
|
||||||
if (thread !== Thread.currentThread()) {
|
|
||||||
throw IllegalAccessException("Trying to access $this outside of $thread!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isSameThread() = thread === Thread.currentThread()
|
|
||||||
|
|
||||||
fun newVBO(type: VBOType = VBOType.ARRAY): VertexBufferObject {
|
|
||||||
return VertexBufferObject(this, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun newEBO() = newVBO(VBOType.ELEMENT_ARRAY)
|
|
||||||
fun newVAO() = VertexArrayObject(this)
|
|
||||||
fun newTexture(name: String = "<unknown>") = GLTexture2D(this, name)
|
|
||||||
|
|
||||||
// минимальное время хранения 5 минут и...
|
|
||||||
private val named2DTextures0: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
|
||||||
.expireAfterAccess(Duration.ofMinutes(5))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
// ...бесконечное хранение пока кто-то все ещё использует текстуру
|
|
||||||
private val named2DTextures1: Cache<String, GLTexture2D> = Caffeine.newBuilder()
|
|
||||||
.weakValues()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val missingTexture: GLTexture2D by lazy {
|
|
||||||
newTexture(missingTexturePath).upload(Starbound.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips().also {
|
|
||||||
it.textureMinFilter = GL_NEAREST
|
|
||||||
it.textureMagFilter = GL_NEAREST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val missingTexturePath = "/assetmissing.png"
|
|
||||||
|
|
||||||
fun loadTexture(path: String): GLTexture2D {
|
|
||||||
ensureSameThread()
|
|
||||||
|
|
||||||
return named2DTextures0.get(path) {
|
|
||||||
named2DTextures1.get(it) {
|
|
||||||
val data = Image.get(it)
|
|
||||||
|
|
||||||
if (data == null) {
|
|
||||||
LOGGER.error("Texture {} is missing! Falling back to {}", it, missingTexturePath)
|
|
||||||
missingTexture
|
|
||||||
} else {
|
|
||||||
newTexture(it).upload(data).also {
|
|
||||||
it.textureMinFilter = GL_NEAREST
|
|
||||||
it.textureMagFilter = GL_NEAREST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind(obj: VertexBufferObject): VertexBufferObject {
|
|
||||||
if (obj.type == VBOType.ARRAY)
|
|
||||||
VBO = obj
|
|
||||||
else
|
|
||||||
EBO = obj
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unbind(obj: VertexBufferObject): VertexBufferObject {
|
|
||||||
if (obj.type == VBOType.ARRAY)
|
|
||||||
if (obj == VBO)
|
|
||||||
VBO = null
|
|
||||||
else
|
|
||||||
if (obj == EBO)
|
|
||||||
EBO = null
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind(obj: VertexArrayObject): VertexArrayObject {
|
|
||||||
VAO = obj
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unbind(obj: VertexArrayObject): VertexArrayObject {
|
|
||||||
if (obj == VAO)
|
|
||||||
VAO = null
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun quadWireframe(color: RGBAColor = RGBAColor.WHITE, lambda: (VertexBuilder) -> Unit) {
|
|
||||||
val builder = quadWireframe
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
lambda.invoke(builder.builder)
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
programs.flat.use()
|
|
||||||
programs.flat.color = color
|
|
||||||
programs.flat.transform = matrixStack.last()
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class Shader(body: String, type: Int) {
|
|
||||||
constructor(body: File, type: Int) : this(body.also { require(it.exists()) { "Shader file does not exists: $body" } }.readText(), type)
|
|
||||||
|
|
||||||
init {
|
|
||||||
ensureSameThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointer = glCreateShader(type)
|
|
||||||
|
|
||||||
init {
|
|
||||||
checkForGLError()
|
|
||||||
registerCleanable(this, ::glDeleteShader, pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (body == "") {
|
|
||||||
throw IllegalArgumentException("Shader source is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
glShaderSource(pointer, body)
|
|
||||||
glCompileShader(pointer)
|
|
||||||
|
|
||||||
val result = intArrayOf(0)
|
|
||||||
glGetShaderiv(pointer, GL_COMPILE_STATUS, result)
|
|
||||||
|
|
||||||
if (result[0] == 0) {
|
|
||||||
throw ShaderCompilationException(glGetShaderInfoLog(pointer))
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun vertex(file: File) = Shader(file, GL_VERTEX_SHADER)
|
|
||||||
fun fragment(file: File) = Shader(file, GL_FRAGMENT_SHADER)
|
|
||||||
|
|
||||||
fun vertex(contents: String) = Shader(contents, GL_VERTEX_SHADER)
|
|
||||||
fun fragment(contents: String) = Shader(contents, GL_FRAGMENT_SHADER)
|
|
||||||
|
|
||||||
fun internalVertex(file: String) = Shader(readInternal(file), GL_VERTEX_SHADER)
|
|
||||||
fun internalFragment(file: String) = Shader(readInternal(file), GL_FRAGMENT_SHADER)
|
|
||||||
fun internalGeometry(file: String) = Shader(readInternal(file), GL_GEOMETRY_SHADER)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOGGER = LogManager.getLogger(GLStateTracker::class.java)
|
|
||||||
private val TRACKERS = ThreadLocal<GLStateTracker>()
|
|
||||||
|
|
||||||
fun current() = checkNotNull(TRACKERS.get()) { "Current thread has no OpenGL State attached" }
|
|
||||||
fun currentOrNull(): GLStateTracker? = TRACKERS.get()
|
|
||||||
|
|
||||||
private fun readInternal(file: String): String {
|
|
||||||
return ClassLoader.getSystemClassLoader().getResourceAsStream(file)!!.bufferedReader()
|
|
||||||
.let {
|
|
||||||
val read = it.readText()
|
|
||||||
it.close()
|
|
||||||
read
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.client.gl
|
|||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import org.lwjgl.stb.STBImage
|
import org.lwjgl.stb.STBImage
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -20,7 +21,7 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value:
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun setValue(thisRef: GLTexture2D, property: KProperty<*>, value: Int) {
|
operator fun setValue(thisRef: GLTexture2D, property: KProperty<*>, value: Int) {
|
||||||
thisRef.state.ensureSameThread()
|
thisRef.client.ensureSameThread()
|
||||||
if (this.value == value) return
|
if (this.value == value) return
|
||||||
this.value = value
|
this.value = value
|
||||||
glTextureParameteri(thisRef.pointer, flag, value)
|
glTextureParameteri(thisRef.pointer, flag, value)
|
||||||
@ -29,16 +30,13 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
class GLTexture2D(val name: String = "<unknown>") {
|
||||||
init {
|
val client = StarboundClient.current()
|
||||||
state.ensureSameThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointer = glGenTextures()
|
val pointer = glGenTextures()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
state.registerCleanable(this, ::glDeleteTextures, pointer)
|
client.registerCleanable(this, ::glDeleteTextures, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
var width = 0
|
var width = 0
|
||||||
@ -74,12 +72,12 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
var textureWrapT by GLTexturePropertyTracker(GL_TEXTURE_WRAP_T, GL_REPEAT)
|
var textureWrapT by GLTexturePropertyTracker(GL_TEXTURE_WRAP_T, GL_REPEAT)
|
||||||
|
|
||||||
fun bind(): GLTexture2D {
|
fun bind(): GLTexture2D {
|
||||||
state.texture2D = this
|
client.texture2D = this
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMips(): GLTexture2D {
|
fun generateMips(): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glGenerateTextureMipmap(pointer)
|
glGenerateTextureMipmap(pointer)
|
||||||
checkForGLError("Generating texture mipmaps")
|
checkForGLError("Generating texture mipmaps")
|
||||||
return this
|
return this
|
||||||
@ -150,7 +148,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upload(path: File, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
fun upload(path: File, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (!path.exists()) {
|
if (!path.exists()) {
|
||||||
throw FileNotFoundException("${path.absolutePath} does not exist")
|
throw FileNotFoundException("${path.absolutePath} does not exist")
|
||||||
@ -176,7 +174,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upload(path: File): GLTexture2D {
|
fun upload(path: File): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (!path.exists()) {
|
if (!path.exists()) {
|
||||||
throw FileNotFoundException("${path.absolutePath} does not exist")
|
throw FileNotFoundException("${path.absolutePath} does not exist")
|
||||||
@ -210,7 +208,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upload(buff: ByteBuffer, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
fun upload(buff: ByteBuffer, memoryFormat: Int, bufferFormat: Int): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val getwidth = intArrayOf(0)
|
val getwidth = intArrayOf(0)
|
||||||
val getheight = intArrayOf(0)
|
val getheight = intArrayOf(0)
|
||||||
@ -228,7 +226,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upload(buff: ByteBuffer): GLTexture2D {
|
fun upload(buff: ByteBuffer): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val getwidth = intArrayOf(0)
|
val getwidth = intArrayOf(0)
|
||||||
val getheight = intArrayOf(0)
|
val getheight = intArrayOf(0)
|
||||||
@ -254,7 +252,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upload(data: Image): GLTexture2D {
|
fun upload(data: Image): GLTexture2D {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val bufferFormat = when (val numChannels = data.amountOfChannels) {
|
val bufferFormat = when (val numChannels = data.amountOfChannels) {
|
||||||
1 -> GL_R
|
1 -> GL_R
|
||||||
|
@ -16,4 +16,4 @@ data class ScissorRect(val x: Int, val y: Int, val width: Int, val height: Int)
|
|||||||
this.x + this.width, this.y + this.height,
|
this.x + this.width, this.y + this.height,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,34 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
package ru.dbotthepony.kstarbound.client.gl
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
|
||||||
class VertexArrayObject(val state: GLStateTracker) {
|
class VertexArrayObject {
|
||||||
|
val client = StarboundClient.current()
|
||||||
val pointer = glGenVertexArrays()
|
val pointer = glGenVertexArrays()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
state.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
client.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(): VertexArrayObject {
|
fun bind(): VertexArrayObject {
|
||||||
return state.bind(this)
|
return client.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind(): VertexArrayObject {
|
fun unbind(): VertexArrayObject {
|
||||||
return state.unbind(this)
|
return client.unbind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attribute(position: Int, size: Int, type: Int, normalize: Boolean, stride: Int, offset: Long = 0L): VertexArrayObject {
|
fun attribute(position: Int, size: Int, type: Int, normalize: Boolean, stride: Int, offset: Long = 0L): VertexArrayObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glVertexAttribPointer(position, size, type, normalize, stride, offset)
|
glVertexAttribPointer(position, size, type, normalize, stride, offset)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enableAttribute(position: Int): VertexArrayObject {
|
fun enableAttribute(position: Int): VertexArrayObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glEnableVertexArrayAttrib(pointer, position)
|
glEnableVertexArrayAttrib(pointer, position)
|
||||||
//glEnableVertexAttribArray(position)
|
//glEnableVertexAttribArray(position)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client.gl
|
|||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import org.lwjgl.system.MemoryUtil
|
import org.lwjgl.system.MemoryUtil
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
enum class VBOType(val glType: Int) {
|
enum class VBOType(val glType: Int) {
|
||||||
@ -9,36 +10,37 @@ enum class VBOType(val glType: Int) {
|
|||||||
ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER),
|
ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER),
|
||||||
}
|
}
|
||||||
|
|
||||||
class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.ARRAY) {
|
class VertexBufferObject private constructor(val type: VBOType) {
|
||||||
|
val client = StarboundClient.current()
|
||||||
val pointer = glGenBuffers()
|
val pointer = glGenBuffers()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError("Creating Vertex Buffer Object")
|
checkForGLError("Creating Vertex Buffer Object")
|
||||||
state.registerCleanable(this, ::glDeleteBuffers, pointer)
|
client.registerCleanable(this, ::glDeleteBuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isArray get() = type == VBOType.ARRAY
|
val isArray get() = type == VBOType.ARRAY
|
||||||
val isElementArray get() = type == VBOType.ELEMENT_ARRAY
|
val isElementArray get() = type == VBOType.ELEMENT_ARRAY
|
||||||
|
|
||||||
fun bind(): VertexBufferObject {
|
fun bind(): VertexBufferObject {
|
||||||
state.bind(this)
|
client.bind(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind(): VertexBufferObject {
|
fun unbind(): VertexBufferObject {
|
||||||
state.unbind(this)
|
client.unbind(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: ByteBuffer, usage: Int): VertexBufferObject {
|
fun bufferData(data: ByteBuffer, usage: Int): VertexBufferObject {
|
||||||
state.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): VertexBufferObject {
|
fun bufferData(data: ByteBuffer, usage: Int, length: Long): VertexBufferObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (length > data.remaining().toLong()) {
|
if (length > data.remaining().toLong()) {
|
||||||
throw IndexOutOfBoundsException("Tried to upload $data into $pointer with offset at ${data.position()} with length of $length, but that is longer than remaining data length of ${data.remaining()}!")
|
throw IndexOutOfBoundsException("Tried to upload $data into $pointer with offset at ${data.position()} with length of $length, but that is longer than remaining data length of ${data.remaining()}!")
|
||||||
@ -51,30 +53,35 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: IntArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: IntArray, usage: Int): VertexBufferObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: FloatArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: FloatArray, usage: Int): VertexBufferObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: DoubleArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: DoubleArray, usage: Int): VertexBufferObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: LongArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: LongArray, usage: Int): VertexBufferObject {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun vbo() = VertexBufferObject(VBOType.ARRAY)
|
||||||
|
fun ebo() = VertexBufferObject(VBOType.ELEMENT_ARRAY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.properties
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import java.util.function.IntConsumer
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class GLStateFuncTracker(private val glFunc: IntConsumer, private var value: Int) {
|
||||||
|
operator fun getValue(glStateTracker: StarboundClient, property: KProperty<*>): Int {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun setValue(glStateTracker: StarboundClient, property: KProperty<*>, value: Int) {
|
||||||
|
glStateTracker.ensureSameThread()
|
||||||
|
|
||||||
|
if (value == this.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
glFunc.accept(value)
|
||||||
|
checkForGLError()
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.properties
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class GLStateGenericTracker<T>(private var value: T, private val callback: (T) -> Unit) : ReadWriteProperty<StarboundClient, T> {
|
||||||
|
override fun getValue(thisRef: StarboundClient, property: KProperty<*>): T {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: StarboundClient, property: KProperty<*>, value: T) {
|
||||||
|
thisRef.ensureSameThread()
|
||||||
|
|
||||||
|
if (value == this.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
callback.invoke(value)
|
||||||
|
checkForGLError()
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.properties
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class GLStateIntTracker(private val fn: Function, private val enum: Int, private var value: Int) {
|
||||||
|
fun interface Function {
|
||||||
|
fun invoke(enum: Int, value: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun getValue(glStateTracker: StarboundClient, property: KProperty<*>): Int {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun setValue(glStateTracker: StarboundClient, property: KProperty<*>, value: Int) {
|
||||||
|
glStateTracker.ensureSameThread()
|
||||||
|
|
||||||
|
if (value == this.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
fn.invoke(enum, value)
|
||||||
|
checkForGLError()
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.properties
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11.glDisable
|
||||||
|
import org.lwjgl.opengl.GL11.glEnable
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class GLStateSwitchTracker(private val enum: Int, private var value: Boolean = false) {
|
||||||
|
operator fun getValue(glStateTracker: StarboundClient, property: KProperty<*>): Boolean {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun setValue(glStateTracker: StarboundClient, property: KProperty<*>, value: Boolean) {
|
||||||
|
glStateTracker.ensureSameThread()
|
||||||
|
|
||||||
|
if (value == this.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
glEnable(enum)
|
||||||
|
} else {
|
||||||
|
glDisable(enum)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForGLError()
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.properties
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11.GL_TEXTURE_2D
|
||||||
|
import org.lwjgl.opengl.GL11.glBindTexture
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class TexturesTracker(maxValue: Int) : ReadWriteProperty<StarboundClient, GLTexture2D?> {
|
||||||
|
private val values = arrayOfNulls<GLTexture2D>(maxValue)
|
||||||
|
|
||||||
|
override fun getValue(thisRef: StarboundClient, property: KProperty<*>): GLTexture2D? {
|
||||||
|
return values[thisRef.activeTexture]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: StarboundClient, property: KProperty<*>, value: GLTexture2D?) {
|
||||||
|
thisRef.ensureSameThread()
|
||||||
|
|
||||||
|
require(value == null || thisRef === value.client) { "$value does not belong to $thisRef" }
|
||||||
|
|
||||||
|
if (values[thisRef.activeTexture] === value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values[thisRef.activeTexture] = value
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0)
|
||||||
|
checkForGLError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, value.pointer)
|
||||||
|
checkForGLError()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL20.GL_COMPILE_STATUS
|
||||||
|
import org.lwjgl.opengl.GL20.glCompileShader
|
||||||
|
import org.lwjgl.opengl.GL20.glCreateShader
|
||||||
|
import org.lwjgl.opengl.GL20.glDeleteShader
|
||||||
|
import org.lwjgl.opengl.GL20.glGetShaderInfoLog
|
||||||
|
import org.lwjgl.opengl.GL20.glGetShaderiv
|
||||||
|
import org.lwjgl.opengl.GL20.glShaderSource
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class GLShader(body: String, type: Int) {
|
||||||
|
constructor(body: File, type: Int) : this(body.also { require(it.exists()) { "Shader file does not exists: $body" } }.readText(), type)
|
||||||
|
val client = StarboundClient.current()
|
||||||
|
val pointer = glCreateShader(type)
|
||||||
|
|
||||||
|
init {
|
||||||
|
checkForGLError()
|
||||||
|
client.registerCleanable(this, ::glDeleteShader, pointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (body == "") {
|
||||||
|
throw IllegalArgumentException("Shader source is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
glShaderSource(pointer, body)
|
||||||
|
glCompileShader(pointer)
|
||||||
|
|
||||||
|
val result = intArrayOf(0)
|
||||||
|
glGetShaderiv(pointer, GL_COMPILE_STATUS, result)
|
||||||
|
|
||||||
|
if (result[0] == 0) {
|
||||||
|
throw ShaderCompilationException(glGetShaderInfoLog(pointer))
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForGLError()
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanFunction
|
|||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
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.GLAttributeList
|
||||||
import ru.dbotthepony.kvector.api.IStruct2f
|
import ru.dbotthepony.kvector.api.IStruct2f
|
||||||
@ -24,19 +24,16 @@ import kotlin.properties.ReadWriteProperty
|
|||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
open class GLShaderProgram(
|
open class GLShaderProgram(
|
||||||
val state: GLStateTracker,
|
shaders: Iterable<GLShader>,
|
||||||
shaders: Iterable<GLStateTracker.Shader>,
|
|
||||||
val attributes: GLAttributeList
|
val attributes: GLAttributeList
|
||||||
) {
|
) {
|
||||||
init {
|
val client = StarboundClient.current()
|
||||||
state.ensureSameThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointer = glCreateProgram()
|
val pointer = glCreateProgram()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError("Creating shader program")
|
checkForGLError("Creating shader program")
|
||||||
state.registerCleanable(this, ::glDeleteProgram, pointer)
|
client.registerCleanable(this, ::glDeleteProgram, pointer)
|
||||||
|
|
||||||
for (shader in shaders) {
|
for (shader in shaders) {
|
||||||
glAttachShader(pointer, shader.pointer)
|
glAttachShader(pointer, shader.pointer)
|
||||||
@ -56,7 +53,7 @@ open class GLShaderProgram(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun use(): GLShaderProgram {
|
fun use(): GLShaderProgram {
|
||||||
state.program = this
|
client.program = this
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +61,7 @@ open class GLShaderProgram(
|
|||||||
private val uniformsExist = Object2BooleanArrayMap<String>()
|
private val uniformsExist = Object2BooleanArrayMap<String>()
|
||||||
|
|
||||||
fun isUniformPresent(name: String): Boolean {
|
fun isUniformPresent(name: String): Boolean {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
return uniformsExist.computeIfAbsent(name, Object2BooleanFunction { glGetUniformLocation(pointer, name) != -1 })
|
return uniformsExist.computeIfAbsent(name, Object2BooleanFunction { glGetUniformLocation(pointer, name) != -1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +75,7 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
abstract inner class Uniform<V : Any>(val name: String) : ReadWriteProperty<Any?, V> {
|
abstract inner class Uniform<V : Any>(val name: String) : ReadWriteProperty<Any?, V> {
|
||||||
init {
|
init {
|
||||||
state.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]})" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +107,7 @@ open class GLShaderProgram(
|
|||||||
inner class FUniform(name: String) : Uniform<Float>(name) {
|
inner class FUniform(name: String) : Uniform<Float>(name) {
|
||||||
override var value: Float = 0f
|
override var value: Float = 0f
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
glProgramUniform1f(pointer, location, value)
|
glProgramUniform1f(pointer, location, value)
|
||||||
@ -135,7 +132,7 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
override var value: IStruct2f = Vector2f.ZERO
|
override var value: IStruct2f = Vector2f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val (v0, v1) = value
|
val (v0, v1) = value
|
||||||
|
|
||||||
@ -157,7 +154,7 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
override var value: IStruct3f = Vector3f.ZERO
|
override var value: IStruct3f = Vector3f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val (v0, v1, v2) = value
|
val (v0, v1, v2) = value
|
||||||
|
|
||||||
@ -179,7 +176,7 @@ open class GLShaderProgram(
|
|||||||
inner class F3x3Uniform(name: String) : Uniform<Matrix3f>(name) {
|
inner class F3x3Uniform(name: String) : Uniform<Matrix3f>(name) {
|
||||||
override var value: Matrix3f = Matrix3f.zero()
|
override var value: Matrix3f = Matrix3f.zero()
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
buff3x3.position(0)
|
buff3x3.position(0)
|
||||||
@ -198,7 +195,7 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
override var value: Matrix4f = Matrix4f.zero()
|
override var value: Matrix4f = Matrix4f.zero()
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
buff4x4.position(0)
|
buff4x4.position(0)
|
||||||
value.storeRowColumn(buff4x4)
|
value.storeRowColumn(buff4x4)
|
||||||
@ -223,7 +220,7 @@ open class GLShaderProgram(
|
|||||||
|
|
||||||
override var value: IStruct4f = Vector4f.ZERO
|
override var value: IStruct4f = Vector4f.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
val (v0, v1, v2, v3) = value
|
val (v0, v1, v2, v3) = value
|
||||||
|
|
||||||
@ -243,7 +240,7 @@ open class GLShaderProgram(
|
|||||||
inner class IUniform(name: String) : Uniform<Int>(name) {
|
inner class IUniform(name: String) : Uniform<Int>(name) {
|
||||||
override var value: Int = 0
|
override var value: Int = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
state.ensureSameThread()
|
client.ensureSameThread()
|
||||||
|
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
glProgramUniform1i(pointer, location, value)
|
glProgramUniform1i(pointer, location, value)
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
|
|
||||||
private fun GLStateTracker.shaders(name: String): List<GLStateTracker.Shader> {
|
private fun internalVertex(string: String) = StarboundClient.current().internalVertex(string)
|
||||||
return listOf(internalVertex("shaders/$name.vsh"), internalFragment("shaders/$name.fsh"))
|
private fun internalFragment(string: String) = StarboundClient.current().internalFragment(string)
|
||||||
|
|
||||||
|
private fun shaders(name: String): List<GLShader> {
|
||||||
|
val client = StarboundClient.current()
|
||||||
|
return listOf(client.internalVertex("shaders/$name.vsh"), client.internalFragment("shaders/$name.fsh"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun GLStateTracker.gshaders(name: String): List<GLStateTracker.Shader> {
|
class GLLiquidProgram : GLShaderProgram(shaders("liquid"), FORMAT) {
|
||||||
return listOf(
|
|
||||||
internalVertex(name),
|
|
||||||
internalFragment(name),
|
|
||||||
internalGeometry(name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("liquid"), FORMAT) {
|
|
||||||
var baselineColor by F4Uniform("baselineColor")
|
var baselineColor by F4Uniform("baselineColor")
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384)
|
StreamVertexBuilder(FORMAT, GeometryType.QUADS, 16384)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -35,118 +29,11 @@ class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light"), FORMAT) {
|
class GLFlatColorProgram : GLShaderProgram(shaders("flat_color"), FORMAT) {
|
||||||
var baselineColor by F4Uniform("baselineColor")
|
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 32)
|
StreamVertexBuilder(FORMAT, GeometryType.QUADS, 16384)
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad"), FORMAT) {
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
|
|
||||||
private val builder by lazy {
|
|
||||||
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
builder.builder.quad(-1f, -1f, 1f, 1f)
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearAlpha() {
|
|
||||||
use()
|
|
||||||
|
|
||||||
color = 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: RGBAColor = RGBAColor.WHITE) {
|
|
||||||
use()
|
|
||||||
|
|
||||||
this.color = 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 = RGBAColor(0f, 0f, 0f, 1f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex"), FORMAT) {
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
private val builder by lazy {
|
|
||||||
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
builder.builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv())
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
|
|
||||||
fun run(texture: Int = 0) {
|
|
||||||
use()
|
|
||||||
this.texture = texture
|
|
||||||
builder.draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur"), FORMAT) {
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
private val builder by lazy {
|
|
||||||
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
|
|
||||||
|
|
||||||
builder.builder.begin()
|
|
||||||
builder.builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv())
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
|
|
||||||
fun run(texture: Int = 0) {
|
|
||||||
use()
|
|
||||||
this.texture = texture
|
|
||||||
builder.draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color"), FORMAT) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
|
|
||||||
val builder by lazy {
|
|
||||||
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -154,7 +41,7 @@ class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("tile"), FORMAT) {
|
class GLTileProgram : GLShaderProgram(shaders("tile"), FORMAT) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
@ -169,7 +56,7 @@ class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("font"), GLAttributeList.VERTEX_2D_TEXTURE) {
|
class GLFontProgram : GLShaderProgram(shaders("font"), GLAttributeList.VERTEX_2D_TEXTURE) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
@ -180,7 +67,7 @@ class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat"), GLAttributeList.VEC2F) {
|
class GLFlatProgram : GLShaderProgram(shaders("flat"), GLAttributeList.VEC2F) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
|
|
||||||
@ -189,12 +76,12 @@ class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
class GLTexturedProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/texture.glsl"), internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
StreamVertexBuilder(state, GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
StreamVertexBuilder(GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -202,12 +89,12 @@ class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTextured2dProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/2dtexture.glsl"), state.internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
class GLTextured2dProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/2dtexture.glsl"), internalFragment("shaders/fragment/texture.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
StreamVertexBuilder(state, GLAttributeList.Builder().push(GLType.VEC2F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
StreamVertexBuilder(GLAttributeList.Builder().push(GLType.VEC2F, GLType.VEC2F).build(), GeometryType.QUADS, 16384)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -215,7 +102,7 @@ class GLTextured2dProgram(state: GLStateTracker) : GLShaderProgram(state, listOf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture_color.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
class GLTexturedColoredProgram : GLShaderProgram(listOf(internalVertex("shaders/vertex/texture.glsl"), internalFragment("shaders/fragment/texture_color.glsl")), GLAttributeList.VERTEX_TEXTURE) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
@ -226,18 +113,13 @@ class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLPrograms(val state: GLStateTracker) {
|
class GLPrograms {
|
||||||
val tile by lazy { GLTileProgram(state) }
|
val tile = GLTileProgram()
|
||||||
val font by lazy { GLFontProgram(state) }
|
val font = GLFontProgram()
|
||||||
val flat by lazy { GLFlatProgram(state) }
|
val flat = GLFlatProgram()
|
||||||
val flatColor by lazy { GLFlatColorProgram(state) }
|
val flatColor = GLFlatColorProgram()
|
||||||
val liquid by lazy { GLLiquidProgram(state) }
|
val liquid = GLLiquidProgram()
|
||||||
val light by lazy { GLLightProgram(state) }
|
val textured = GLTexturedProgram()
|
||||||
val textured by lazy { GLTexturedProgram(state) }
|
val textured2d = GLTextured2dProgram()
|
||||||
val textured2d by lazy { GLTextured2dProgram(state) }
|
val texturedColored = GLTexturedColoredProgram()
|
||||||
val texturedColored by lazy { GLTexturedColoredProgram(state) }
|
|
||||||
|
|
||||||
val viewColorQuad by lazy { GLColorQuadProgram(state) }
|
|
||||||
val viewTextureQuad by lazy { GLTextureQuadProgram(state) }
|
|
||||||
val viewTextureBlurQuad by lazy { GLTextureBlurredQuadProgram(state) }
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.vertex
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46
|
import org.lwjgl.opengl.GL46
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
||||||
*/
|
*/
|
||||||
class StreamVertexBuilder(
|
class StreamVertexBuilder(
|
||||||
val state: GLStateTracker,
|
|
||||||
attributes: GLAttributeList,
|
attributes: GLAttributeList,
|
||||||
type: GeometryType,
|
type: GeometryType,
|
||||||
initialCapacity: Int = 64,
|
initialCapacity: Int = 64,
|
||||||
) {
|
) {
|
||||||
|
val state = StarboundClient.current()
|
||||||
val builder = VertexBuilder(attributes, type, initialCapacity)
|
val builder = VertexBuilder(attributes, type, initialCapacity)
|
||||||
private val vao = state.newVAO()
|
private val vao = VertexArrayObject()
|
||||||
private val vbo = state.newVBO()
|
private val vbo = VertexBufferObject.vbo()
|
||||||
private val ebo = state.newEBO()
|
private val ebo = VertexBufferObject.ebo()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
vao.bind()
|
vao.bind()
|
||||||
|
@ -4,13 +4,14 @@ import org.lwjgl.opengl.GL46.*
|
|||||||
import ru.dbotthepony.kbox2d.api.IDebugDraw
|
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.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
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
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
|
class Box2DRenderer : IDebugDraw {
|
||||||
|
val state = StarboundClient.current()
|
||||||
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
|
||||||
|
@ -28,18 +28,6 @@ class Camera(val client: StarboundClient) {
|
|||||||
GLFW_KEY_RIGHT -> pressedRight = action > 0
|
GLFW_KEY_RIGHT -> pressedRight = action > 0
|
||||||
GLFW_KEY_UP -> pressedUp = action > 0
|
GLFW_KEY_UP -> pressedUp = action > 0
|
||||||
GLFW_KEY_DOWN -> pressedDown = action > 0
|
GLFW_KEY_DOWN -> pressedDown = action > 0
|
||||||
|
|
||||||
GLFW_KEY_C -> {
|
|
||||||
if (action > 0) {
|
|
||||||
client.settings.debugCollisions = !client.settings.debugCollisions
|
|
||||||
|
|
||||||
if (client.settings.debugCollisions) {
|
|
||||||
client.putDebugLog("Enable collsion draw")
|
|
||||||
} else {
|
|
||||||
client.putDebugLog("Disable collsion draw")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap
|
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
|
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
|
||||||
@ -56,10 +57,10 @@ enum class TextAlignY {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Font(
|
class Font(
|
||||||
val state: GLStateTracker,
|
|
||||||
val font: String = "hobo.ttf",
|
val font: String = "hobo.ttf",
|
||||||
val size: Int = 48
|
val size: Int = 48
|
||||||
) {
|
) {
|
||||||
|
val state = StarboundClient.current()
|
||||||
val face = state.freeType.Face(font, 0L)
|
val face = state.freeType.Face(font, 0L)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46
|
import org.lwjgl.opengl.GL46
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
|
|
||||||
class Mesh(state: GLStateTracker) {
|
class Mesh() {
|
||||||
constructor(state: GLStateTracker, builder: VertexBuilder) : this(state) {
|
constructor(builder: VertexBuilder) : this() {
|
||||||
load(builder, GL46.GL_STATIC_DRAW)
|
load(builder, GL46.GL_STATIC_DRAW)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val state = StarboundClient.current()
|
||||||
|
|
||||||
val vao = state.newVAO()
|
val vao = state.newVAO()
|
||||||
val vbo = state.newVBO()
|
val vbo = state.newVBO()
|
||||||
val ebo = state.newEBO()
|
val ebo = state.newEBO()
|
||||||
@ -45,8 +47,8 @@ class Mesh(state: GLStateTracker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ConfiguredMesh<T : GLShaderProgram>(val config: RenderConfig<T>, val mesh: Mesh = Mesh(config.state)) {
|
data class ConfiguredMesh<T : GLShaderProgram>(val config: RenderConfig<T>, val mesh: Mesh = Mesh()) {
|
||||||
fun render(transform: Matrix4f = config.state.matrixStack.last()) {
|
fun render(transform: Matrix4f = config.client.matrixStack.last()) {
|
||||||
config.setup(transform)
|
config.setup(transform)
|
||||||
mesh.render()
|
mesh.render()
|
||||||
config.uninstall()
|
config.uninstall()
|
||||||
|
@ -4,10 +4,10 @@ import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
|||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
|
|
||||||
abstract class RenderConfig<out T : GLShaderProgram>(val program: T) {
|
abstract class RenderConfig<out T : GLShaderProgram>(val program: T) {
|
||||||
val state get() = program.state
|
val client get() = program.client
|
||||||
open val initialBuilderCapacity: Int get() = 64
|
open val initialBuilderCapacity: Int get() = 64
|
||||||
|
|
||||||
open fun setup(transform: Matrix4f = state.matrixStack.last()) {
|
open fun setup(transform: Matrix4f = client.matrixStack.last()) {
|
||||||
program.use()
|
program.use()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,9 @@ import kotlin.collections.HashMap
|
|||||||
/**
|
/**
|
||||||
* Хранит в себе программы для отрисовки определённых [TileDefinition]
|
* Хранит в себе программы для отрисовки определённых [TileDefinition]
|
||||||
*
|
*
|
||||||
* Создаётся единожды как потомок [GLStateTracker]
|
* Создаётся единожды как потомок [Graphics]
|
||||||
*/
|
*/
|
||||||
class TileRenderers(val client: StarboundClient) {
|
class TileRenderers(val client: StarboundClient) {
|
||||||
val state get() = client.gl
|
|
||||||
|
|
||||||
private val foreground = HashMap<GLTexture2D, Config>()
|
private val foreground = HashMap<GLTexture2D, Config>()
|
||||||
private val background = HashMap<GLTexture2D, Config>()
|
private val background = HashMap<GLTexture2D, Config>()
|
||||||
private val matCache = HashMap<String, TileRenderer>()
|
private val matCache = HashMap<String, TileRenderer>()
|
||||||
@ -44,14 +42,14 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig<GLTileProgram>(state.programs.tile) {
|
private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig<GLTileProgram>(client.programs.tile) {
|
||||||
override val initialBuilderCapacity: Int
|
override val initialBuilderCapacity: Int
|
||||||
get() = 1024
|
get() = 1024
|
||||||
|
|
||||||
override fun setup(transform: Matrix4f) {
|
override fun setup(transform: Matrix4f) {
|
||||||
super.setup(transform)
|
super.setup(transform)
|
||||||
state.activeTexture = 0
|
client.activeTexture = 0
|
||||||
state.depthTest = false
|
client.depthTest = false
|
||||||
program.texture = 0
|
program.texture = 0
|
||||||
texture.bind()
|
texture.bind()
|
||||||
texture.textureMagFilter = GL_NEAREST
|
texture.textureMagFilter = GL_NEAREST
|
||||||
@ -97,7 +95,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
HALT
|
HALT
|
||||||
}
|
}
|
||||||
|
|
||||||
val state get() = renderers.state
|
val state get() = renderers.client
|
||||||
val texture = def.renderParameters.texture?.imagePath?.value?.let { state.loadTexture(it).also { it.textureMagFilter = GL_NEAREST }}
|
val texture = def.renderParameters.texture?.imagePath?.value?.let { state.loadTexture(it).also { it.textureMagFilter = GL_NEAREST }}
|
||||||
|
|
||||||
val equalityTester: EqualityRuleTester = when (def) {
|
val equalityTester: EqualityRuleTester = when (def) {
|
||||||
|
@ -3,7 +3,6 @@ package ru.dbotthepony.kstarbound.client.render.entity
|
|||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
import ru.dbotthepony.kstarbound.client.world.ClientChunk
|
import ru.dbotthepony.kstarbound.client.world.ClientChunk
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
@ -14,19 +13,12 @@ import ru.dbotthepony.kvector.vector.Vector2d
|
|||||||
* Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход)
|
* Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход)
|
||||||
*/
|
*/
|
||||||
open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) {
|
open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) {
|
||||||
inline val state: GLStateTracker get() = client.gl
|
|
||||||
open val renderPos: Vector2d get() = entity.position
|
open val renderPos: Vector2d get() = entity.position
|
||||||
|
|
||||||
open fun render(stack: Matrix4fStack) {
|
open fun render(stack: Matrix4fStack) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun renderDebug() {
|
|
||||||
if (chunk?.world?.client?.settings?.debugCollisions == true) {
|
|
||||||
state.quadWireframe(entity.movement.worldAABB)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open val layer: Int get() = Z_LEVEL_ENTITIES
|
open val layer: Int get() = Z_LEVEL_ENTITIES
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -9,9 +9,9 @@ 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) {
|
||||||
state.programs.textured.use()
|
client.programs.textured.use()
|
||||||
state.programs.textured.transform = stack.last()
|
client.programs.textured.transform = stack.last()
|
||||||
state.activeTexture = 0
|
client.activeTexture = 0
|
||||||
state.programs.textured.texture = 0
|
client.programs.textured.texture = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.world
|
package ru.dbotthepony.kstarbound.client.world
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
||||||
import ru.dbotthepony.kstarbound.world.Chunk
|
import ru.dbotthepony.kstarbound.world.Chunk
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
|
|
||||||
class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk>(world, pos){
|
class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, ClientChunk>(world, pos){
|
||||||
val state: GLStateTracker get() = world.client.gl
|
|
||||||
|
|
||||||
override fun foregroundChanges(cell: Cell) {
|
override fun foregroundChanges(cell: Cell) {
|
||||||
super.foregroundChanges(cell)
|
super.foregroundChanges(cell)
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class ClientWorld(
|
|||||||
loopY: Boolean = false
|
loopY: Boolean = false
|
||||||
) : World<ClientWorld, ClientChunk>(seed, size, loopX, loopY) {
|
) : World<ClientWorld, ClientChunk>(seed, size, loopX, loopY) {
|
||||||
init {
|
init {
|
||||||
physics.debugDraw = client.gl.box2dRenderer
|
physics.debugDraw = client.box2dRenderer
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineChunkSize(cells: Int): Int {
|
private fun determineChunkSize(cells: Int): Int {
|
||||||
@ -72,7 +72,6 @@ class ClientWorld(
|
|||||||
|
|
||||||
inner class RenderRegion(val x: Int, val y: Int) {
|
inner class RenderRegion(val x: Int, val y: Int) {
|
||||||
inner class Layer(private val view: ITileAccess, private val isBackground: Boolean) {
|
inner class Layer(private val view: ITileAccess, private val isBackground: Boolean) {
|
||||||
private val state get() = client.gl
|
|
||||||
val bakedMeshes = ArrayList<Pair<ConfiguredMesh<*>, Long>>()
|
val bakedMeshes = ArrayList<Pair<ConfiguredMesh<*>, Long>>()
|
||||||
var isDirty = true
|
var isDirty = true
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ class ClientWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ((baked, builder, zLevel) in meshes.meshes()) {
|
for ((baked, builder, zLevel) in meshes.meshes()) {
|
||||||
bakedMeshes.add(ConfiguredMesh(baked, Mesh(state, builder)) to zLevel)
|
bakedMeshes.add(ConfiguredMesh(baked, Mesh(builder)) to zLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +133,7 @@ class ClientWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (type in liquidTypes) {
|
for (type in liquidTypes) {
|
||||||
val builder = client.gl.programs.liquid.builder.builder
|
val builder = client.programs.liquid.builder.builder
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
@ -148,7 +147,7 @@ class ClientWorld(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
liquidMesh.add(Mesh(client.gl, builder) to type.color)
|
liquidMesh.add(Mesh(builder) to type.color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +171,7 @@ class ClientWorld(
|
|||||||
layers.add(RenderLayer.Liquid.index) {
|
layers.add(RenderLayer.Liquid.index) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
||||||
|
|
||||||
val program = client.gl.programs.liquid
|
val program = client.programs.liquid
|
||||||
|
|
||||||
program.use()
|
program.use()
|
||||||
program.transform = it.last()
|
program.transform = it.last()
|
||||||
@ -268,7 +267,7 @@ class ClientWorld(
|
|||||||
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) { m ->
|
||||||
client.gl.quadWireframe {
|
client.quadWireframe {
|
||||||
it.quad(
|
it.quad(
|
||||||
obj.pos.x.toFloat(),
|
obj.pos.x.toFloat(),
|
||||||
obj.pos.y.toFloat(),
|
obj.pos.y.toFloat(),
|
||||||
@ -281,7 +280,7 @@ class ClientWorld(
|
|||||||
val (x, y) = obj.imagePosition
|
val (x, y) = obj.imagePosition
|
||||||
|
|
||||||
it.render(
|
it.render(
|
||||||
client.gl,
|
client,
|
||||||
x = obj.pos.x.toFloat() + x / PIXELS_IN_STARBOUND_UNITf,
|
x = obj.pos.x.toFloat() + x / PIXELS_IN_STARBOUND_UNITf,
|
||||||
y = obj.pos.y.toFloat() + y / PIXELS_IN_STARBOUND_UNITf
|
y = obj.pos.y.toFloat() + y / PIXELS_IN_STARBOUND_UNITf
|
||||||
)
|
)
|
||||||
|
@ -9,14 +9,13 @@ import com.google.gson.stream.JsonReader
|
|||||||
import com.google.gson.stream.JsonWriter
|
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.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||||
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.contains
|
import ru.dbotthepony.kstarbound.util.contains
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
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
|
||||||
@ -30,7 +29,7 @@ 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(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +40,7 @@ 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(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,16 +63,16 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
return Image(newPath, transform, centered, position, color, fullbright)
|
return Image(newPath, transform, centered, position, color, fullbright)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {
|
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {
|
||||||
val sprite = path.sprite ?: return
|
val sprite = path.sprite ?: return
|
||||||
val texture = gl.loadTexture(path.imagePath.value!!)
|
val texture = client.loadTexture(path.imagePath.value!!)
|
||||||
|
|
||||||
if (centered) {
|
if (centered) {
|
||||||
gl.quadTexture(texture) {
|
client.quadTexture(texture) {
|
||||||
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.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))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gl.quadTexture(texture) {
|
client.quadTexture(texture) {
|
||||||
it.quad(x, y, x + sprite.width / PIXELS_IN_STARBOUND_UNITf, y + sprite.height / PIXELS_IN_STARBOUND_UNITf, QuadTransformers.uv(sprite))
|
it.quad(x, y, x + sprite.width / PIXELS_IN_STARBOUND_UNITf, y + sprite.height / PIXELS_IN_STARBOUND_UNITf, QuadTransformers.uv(sprite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,14 +80,14 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(gl: GLStateTracker, stack: Matrix4fStack, x: Float, y: Float) {}
|
override fun render(client: StarboundClient, stack: Matrix4fStack, x: Float, y: Float) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun with(values: (String) -> String?): Drawable {
|
open fun with(values: (String) -> String?): Drawable {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun render(gl: GLStateTracker = GLStateTracker.current(), stack: Matrix4fStack = gl.matrixStack, x: Float = 0f, y: Float = 0f)
|
abstract fun render(client: StarboundClient = StarboundClient.current(), stack: Matrix4fStack = client.matrixStack, x: Float = 0f, y: Float = 0f)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = Empty()
|
val EMPTY = Empty()
|
||||||
|
@ -18,7 +18,6 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
|||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.defs.util.flattenJsonElement
|
import ru.dbotthepony.kstarbound.defs.util.flattenJsonElement
|
||||||
import ru.dbotthepony.kstarbound.util.INotNullDelegate
|
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
@ -139,16 +138,10 @@ class BuilderAdapter<T : Any> private constructor(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val delegate = property.property.getDelegate(instance)
|
try {
|
||||||
|
property.property.get(instance)
|
||||||
if (delegate is INotNullDelegate && !delegate.isPresent) {
|
} catch (err: Throwable) {
|
||||||
throw JsonSyntaxException("${property.name} in ${instance::class.qualifiedName} can not be null, but it is missing from JSON structure")
|
throw JsonSyntaxException("${property.name} in ${instance::class.qualifiedName} does not like it being missing from JSON structure", err)
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
property.property.get(instance)
|
|
||||||
} catch (err: Throwable) {
|
|
||||||
throw JsonSyntaxException("${property.name} in ${instance::class.qualifiedName} does not like it being missing from JSON structure", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
class DoubleEdgeProgression : Iterator<Int> {
|
|
||||||
var value = 0
|
|
||||||
|
|
||||||
override fun hasNext(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun next(): Int {
|
|
||||||
return nextInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun nextInt(): Int {
|
|
||||||
return if (value > 0) {
|
|
||||||
val ret = value
|
|
||||||
value = -value
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
val ret = value
|
|
||||||
value = -value + 1
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
interface INotNullDelegate {
|
|
||||||
val isPresent: Boolean
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
import java.util.Spliterator
|
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
class IndexedArraySpliterator<T>(private val source: Array<T>, private val offset: Int = 0, private val size: Int = source.size) : Spliterator<IndexedArraySpliterator<T>.Entry> {
|
|
||||||
inner class Entry(val index: Int, val value: T)
|
|
||||||
|
|
||||||
init {
|
|
||||||
require(offset + size <= source.size) { "Invalid dimensions for spliterator: offset = $offset, size = $size, source has size of ${source.size}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var index = 0
|
|
||||||
|
|
||||||
override fun tryAdvance(action: Consumer<in IndexedArraySpliterator<T>.Entry>): Boolean {
|
|
||||||
if (index < size) {
|
|
||||||
val pointer = offset + index++
|
|
||||||
action.accept(Entry(pointer, source[pointer]))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun trySplit(): Spliterator<IndexedArraySpliterator<T>.Entry>? {
|
|
||||||
if (index + 1 >= size) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val splitSize = size / 2
|
|
||||||
|
|
||||||
if (splitSize == 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointer = index + offset
|
|
||||||
index += splitSize
|
|
||||||
return IndexedArraySpliterator(source, pointer, splitSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun estimateSize(): Long {
|
|
||||||
return (size - index).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun characteristics(): Int {
|
|
||||||
return Spliterator.ORDERED or Spliterator.SIZED
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSpliterators
|
|
||||||
import java.util.*
|
|
||||||
import java.util.stream.Stream
|
|
||||||
import java.util.stream.StreamSupport
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
class NotNullTwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int, initializer: (Int, Int) -> T) {
|
|
||||||
data class Entry<out T>(
|
|
||||||
val x: Int,
|
|
||||||
val y: Int,
|
|
||||||
val value: T,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val memory: Array<T> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T>
|
|
||||||
|
|
||||||
init {
|
|
||||||
for (x in 0 until width) {
|
|
||||||
for (y in 0 until height) {
|
|
||||||
memory[x + y * width] = initializer.invoke(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isOutside(x: Int, y: Int): Boolean {
|
|
||||||
return (x !in 0 until width) || (y !in 0 until height)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(x: Int, y: Int): T {
|
|
||||||
if (x !in 0 until width) {
|
|
||||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y !in 0 until height) {
|
|
||||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
|
||||||
}
|
|
||||||
|
|
||||||
return memory[x + y * width]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun set(x: Int, y: Int, value: T): T {
|
|
||||||
if (x !in 0 until width) {
|
|
||||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y !in 0 until height) {
|
|
||||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
|
||||||
}
|
|
||||||
|
|
||||||
val old = memory[x + y * width]
|
|
||||||
memory[x + y * width] = value
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stream(): Stream<out T> {
|
|
||||||
return Arrays.stream(memory)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun indexedStream(): Stream<out Entry<T>> {
|
|
||||||
return StreamSupport.stream(IndexedArraySpliterator(memory), false).map {
|
|
||||||
val x = it.index % width
|
|
||||||
val y = (it.index - x) / width
|
|
||||||
Entry(x, y, it.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : Any> NotNullTwoDimensionalArray(width: Int, height: Int, noinline initializer: (Int, Int) -> T) = NotNullTwoDimensionalArray(T::class, width, height, initializer)
|
|
@ -1,26 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
import kotlin.properties.ReadWriteProperty
|
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Аналог [Delegates.notNull], но со свойством [isInitialized]
|
|
||||||
*/
|
|
||||||
class NotNullVar<V : Any> : ReadWriteProperty<Any?, V>, INotNullDelegate {
|
|
||||||
private var value: V? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Имеет ли данный делегат не-null значение
|
|
||||||
*/
|
|
||||||
override val isPresent: Boolean
|
|
||||||
get() = value != null
|
|
||||||
|
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): V {
|
|
||||||
return value ?: throw IllegalStateException("Property ${property.name} was not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSpliterators
|
|
||||||
import java.util.Arrays
|
|
||||||
import java.util.stream.Stream
|
|
||||||
import java.util.stream.StreamSupport
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
class TwoDimensionalArray<T : Any>(clazz: KClass<T>, private val width: Int, private val height: Int) {
|
|
||||||
data class Entry<out T>(
|
|
||||||
val x: Int,
|
|
||||||
val y: Int,
|
|
||||||
val value: T,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val memory: Array<T?> = java.lang.reflect.Array.newInstance(clazz.java, width * height) as Array<T?>
|
|
||||||
|
|
||||||
fun isOutside(x: Int, y: Int): Boolean {
|
|
||||||
return (x !in 0 until width) || (y !in 0 until height)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(x: Int, y: Int): T? {
|
|
||||||
if (x !in 0 until width) {
|
|
||||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y !in 0 until height) {
|
|
||||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
|
||||||
}
|
|
||||||
|
|
||||||
return memory[x + y * width]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun set(x: Int, y: Int, value: T?): T? {
|
|
||||||
if (x !in 0 until width) {
|
|
||||||
throw IndexOutOfBoundsException("X $x is out of bounds between 0 and $width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y !in 0 until height) {
|
|
||||||
throw IndexOutOfBoundsException("Y $y is out of bounds between 0 and $height")
|
|
||||||
}
|
|
||||||
|
|
||||||
val old = memory[x + y * width]
|
|
||||||
memory[x + y * width] = value
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stream(): Stream<out T?> {
|
|
||||||
return Arrays.stream(memory)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun indexedStream(): Stream<out Entry<T?>> {
|
|
||||||
return StreamSupport.stream(IndexedArraySpliterator(memory), false).map {
|
|
||||||
val x = it.index % width
|
|
||||||
val y = (it.index - x) / width
|
|
||||||
Entry(x, y, it.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : Any> TwoDimensionalArray(width: Int, height: Int) = TwoDimensionalArray(T::class, width, height)
|
|
@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
out vec4 resultColor;
|
|
||||||
|
|
||||||
uniform vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
resultColor = color;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 vertexPos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(vertexPos, 0.0, 1.0);
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
#version 460
|
|
||||||
|
|
||||||
out vec4 resultColor;
|
|
||||||
in vec2 uvPos;
|
|
||||||
|
|
||||||
uniform sampler2D texture0;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
resultColor = texture(texture0, uvPos);
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user