Updated render classes structure to be less alien
This commit is contained in:
parent
370c93226b
commit
ef838d52c2
@ -1,21 +1,10 @@
|
|||||||
package ru.dbotthepony.kstarbound.client
|
package ru.dbotthepony.kstarbound.client
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.world.Chunk
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.world.*
|
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZEd
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.util.LinkedList
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Псевдо zPos у фоновых тайлов
|
* Псевдо zPos у фоновых тайлов
|
||||||
@ -70,6 +59,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onEntityRemoved(entity: Entity) {
|
override fun onEntityRemoved(entity: Entity) {
|
||||||
entityRenderers.remove(entity)!!.close()
|
entityRenderers.remove(entity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
|||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.longs.LongArraySet
|
import it.unimi.dsi.fastutil.longs.LongArraySet
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
||||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.Mesh
|
import ru.dbotthepony.kstarbound.client.render.Mesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
import ru.dbotthepony.kstarbound.client.render.MultiMeshBuilder
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
@ -51,23 +51,16 @@ 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
|
private val state get() = client.gl
|
||||||
private val layers = TileLayerList()
|
val bakedMeshes = ArrayList<Pair<ConfiguredMesh<*>, Int>>()
|
||||||
val bakedMeshes = ArrayList<Pair<ConfiguredStaticMesh, Int>>()
|
|
||||||
var isDirty = true
|
var isDirty = true
|
||||||
|
|
||||||
fun bake() {
|
fun bake() {
|
||||||
if (!isDirty) return
|
if (!isDirty) return
|
||||||
isDirty = false
|
isDirty = false
|
||||||
|
|
||||||
if (state.isSameThread()) {
|
|
||||||
for (mesh in bakedMeshes) {
|
|
||||||
mesh.first.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bakedMeshes.clear()
|
bakedMeshes.clear()
|
||||||
|
|
||||||
layers.clear()
|
val meshes = MultiMeshBuilder()
|
||||||
|
|
||||||
for (x in 0 until renderRegionWidth) {
|
for (x in 0 until renderRegionWidth) {
|
||||||
for (y in 0 until renderRegionHeight) {
|
for (y in 0 until renderRegionHeight) {
|
||||||
@ -77,30 +70,22 @@ class ClientWorld(
|
|||||||
val material = tile.material
|
val material = tile.material
|
||||||
|
|
||||||
if (material != null) {
|
if (material != null) {
|
||||||
client.tileRenderers.getTileRenderer(material.materialName).tesselate(tile, view, layers, Vector2i(x, y), background = isBackground)
|
client.tileRenderers.getMaterialRenderer(material.materialName).tesselate(tile, view, meshes, Vector2i(x, y), background = isBackground)
|
||||||
}
|
}
|
||||||
|
|
||||||
val modifier = tile.modifier
|
val modifier = tile.modifier
|
||||||
|
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
client.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, layers, Vector2i(x, y), background = isBackground, isModifier = true)
|
client.tileRenderers.getModifierRenderer(modifier.modName).tesselate(tile, view, meshes, Vector2i(x, y), background = isBackground, isModifier = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layers.isNotEmpty) {
|
for ((baked, builder, zLevel) in meshes.meshes()) {
|
||||||
for (mesh in bakedMeshes) {
|
bakedMeshes.add(ConfiguredMesh(baked, Mesh(state, builder)) to zLevel)
|
||||||
mesh.first.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
bakedMeshes.clear()
|
|
||||||
|
|
||||||
for ((baked, builder, zLevel) in layers.layers()) {
|
|
||||||
bakedMeshes.add(ConfiguredStaticMesh(baked, builder) to zLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
layers.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meshes.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +135,7 @@ class ClientWorld(
|
|||||||
for ((baked, zLevel) in background.bakedMeshes) {
|
for ((baked, zLevel) in background.bakedMeshes) {
|
||||||
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
||||||
baked.renderStacked(it)
|
baked.render(it.last())
|
||||||
it.pop()
|
it.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +143,7 @@ class ClientWorld(
|
|||||||
for ((baked, zLevel) in foreground.bakedMeshes) {
|
for ((baked, zLevel) in foreground.bakedMeshes) {
|
||||||
layers.add(zLevel) {
|
layers.add(zLevel) {
|
||||||
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
it.push().last().translateWithMultiplication(renderOrigin.x, renderOrigin.y)
|
||||||
baked.renderStacked(it)
|
baked.render(it.last())
|
||||||
it.pop()
|
it.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@ data class BlendFunc(
|
|||||||
Func.ZERO
|
Func.ZERO
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(
|
constructor(source: Func, destination: Func) : this(
|
||||||
source: Func, destination: Func
|
|
||||||
) : this(
|
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
source,
|
source,
|
||||||
|
@ -13,7 +13,7 @@ import org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus
|
|||||||
import org.lwjgl.opengl.GL45.glNamedFramebufferTexture
|
import org.lwjgl.opengl.GL45.glNamedFramebufferTexture
|
||||||
import org.lwjgl.opengl.GL46
|
import org.lwjgl.opengl.GL46
|
||||||
|
|
||||||
class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
|
class GLFrameBuffer(val state: GLStateTracker) {
|
||||||
init {
|
init {
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
}
|
}
|
||||||
@ -22,6 +22,7 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError("Creating framebuffer")
|
checkForGLError("Creating framebuffer")
|
||||||
|
state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isComplete: Boolean get() {
|
val isComplete: Boolean get() {
|
||||||
@ -50,7 +51,6 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) {
|
fun reattachTexture(width: Int, height: Int, format: Int = GL_RGB) {
|
||||||
texture?.close()
|
|
||||||
texture = null
|
texture = null
|
||||||
attachTexture(width, height, format)
|
attachTexture(width, height, format)
|
||||||
}
|
}
|
||||||
@ -76,17 +76,4 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
|
|||||||
state.readFramebuffer = null
|
state.readFramebuffer = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cleanable = state.registerCleanable(this, GL46::glDeleteFramebuffers, pointer)
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (!isValid)
|
|
||||||
return
|
|
||||||
|
|
||||||
cleanable.clean()
|
|
||||||
texture?.close()
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : AutoCloseable {
|
class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") {
|
||||||
init {
|
init {
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
}
|
}
|
||||||
@ -38,10 +38,9 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
state.registerCleanable(this, ::glDeleteTextures, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cleanable = state.registerCleanable(this, ::glDeleteTextures, pointer)
|
|
||||||
|
|
||||||
var width = 0
|
var width = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -269,23 +268,6 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!isValid)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (state.texture2D == this) {
|
|
||||||
state.texture2D = null
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanable.clean()
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,24 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
package ru.dbotthepony.kstarbound.client.gl
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import java.io.Closeable
|
|
||||||
|
|
||||||
class VertexArrayObject(val state: GLStateTracker) : Closeable {
|
class VertexArrayObject(val state: GLStateTracker) {
|
||||||
val pointer = glGenVertexArrays()
|
val pointer = glGenVertexArrays()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
state.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cleanable = state.registerCleanable(this, ::glDeleteVertexArrays, pointer)
|
|
||||||
|
|
||||||
fun bind(): VertexArrayObject {
|
fun bind(): VertexArrayObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexArrayObject" }
|
|
||||||
return state.bind(this)
|
return state.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind(): VertexArrayObject {
|
fun unbind(): VertexArrayObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexArrayObject" }
|
|
||||||
return state.unbind(this)
|
return state.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 {
|
||||||
check(isValid) { "Tried to use NULL GLVertexArrayObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glVertexAttribPointer(position, size, type, normalize, stride, offset)
|
glVertexAttribPointer(position, size, type, normalize, stride, offset)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
@ -31,27 +26,10 @@ class VertexArrayObject(val state: GLStateTracker) : Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun enableAttribute(position: Int): VertexArrayObject {
|
fun enableAttribute(position: Int): VertexArrayObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexArrayObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glEnableVertexArrayAttrib(pointer, position)
|
glEnableVertexArrayAttrib(pointer, position)
|
||||||
//glEnableVertexAttribArray(position)
|
//glEnableVertexAttribArray(position)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!isValid) return
|
|
||||||
|
|
||||||
if (state.VAO == this) {
|
|
||||||
state.VAO = null
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanable.clean()
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ 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 java.io.Closeable
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
enum class VBOType(val glType: Int) {
|
enum class VBOType(val glType: Int) {
|
||||||
@ -10,32 +9,28 @@ 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) : Closeable {
|
class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.ARRAY) {
|
||||||
val pointer = glGenBuffers()
|
val pointer = glGenBuffers()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkForGLError("Creating Vertex Buffer Object")
|
checkForGLError("Creating Vertex Buffer Object")
|
||||||
|
state.registerCleanable(this, ::glDeleteBuffers, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cleanable = state.registerCleanable(this, ::glDeleteBuffers, pointer)
|
|
||||||
|
|
||||||
val isArray get() = type == VBOType.ARRAY
|
val isArray get() = type == VBOType.ARRAY
|
||||||
val isElementArray get() = type == VBOType.ELEMENT_ARRAY
|
val isElementArray get() = type == VBOType.ELEMENT_ARRAY
|
||||||
|
|
||||||
fun bind(): VertexBufferObject {
|
fun bind(): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.bind(this)
|
state.bind(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind(): VertexBufferObject {
|
fun unbind(): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.unbind(this)
|
state.unbind(this)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: ByteBuffer, usage: Int): VertexBufferObject {
|
fun bufferData(data: ByteBuffer, usage: Int): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
@ -43,7 +38,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: ByteBuffer, usage: Int, length: Long): VertexBufferObject {
|
fun bufferData(data: ByteBuffer, usage: Int, length: Long): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
|
|
||||||
if (length > data.remaining().toLong()) {
|
if (length > data.remaining().toLong()) {
|
||||||
@ -57,7 +51,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: IntArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: IntArray, usage: Int): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
@ -65,7 +58,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: FloatArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: FloatArray, usage: Int): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
@ -73,7 +65,6 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: DoubleArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: DoubleArray, usage: Int): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
@ -81,27 +72,9 @@ class VertexBufferObject(val state: GLStateTracker, val type: VBOType = VBOType.
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun bufferData(data: LongArray, usage: Int): VertexBufferObject {
|
fun bufferData(data: LongArray, usage: Int): VertexBufferObject {
|
||||||
check(isValid) { "Tried to use NULL GLVertexBufferObject" }
|
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
glNamedBufferData(pointer, data, usage)
|
glNamedBufferData(pointer, data, usage)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
state.ensureSameThread()
|
|
||||||
|
|
||||||
if (!isValid) return
|
|
||||||
|
|
||||||
if (state.VBO == this) {
|
|
||||||
state.VBO = null
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanable.clean()
|
|
||||||
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ 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.gl.GLStateTracker
|
||||||
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.kvector.api.IStruct2f
|
import ru.dbotthepony.kvector.api.IStruct2f
|
||||||
import ru.dbotthepony.kvector.api.IStruct3f
|
import ru.dbotthepony.kvector.api.IStruct3f
|
||||||
import ru.dbotthepony.kvector.api.IStruct4f
|
import ru.dbotthepony.kvector.api.IStruct4f
|
||||||
@ -22,7 +23,11 @@ import kotlin.NoSuchElementException
|
|||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
open class GLShaderProgram(val state: GLStateTracker, shaders: Iterable<GLStateTracker.Shader>) {
|
open class GLShaderProgram(
|
||||||
|
val state: GLStateTracker,
|
||||||
|
shaders: Iterable<GLStateTracker.Shader>,
|
||||||
|
val attributes: GLAttributeList
|
||||||
|
) {
|
||||||
init {
|
init {
|
||||||
state.ensureSameThread()
|
state.ensureSameThread()
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ private fun GLStateTracker.gshaders(name: String): List<GLStateTracker.Shader> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("liquid")) {
|
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")
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class GLLiquidProgram(state: GLStateTracker) : GLShaderProgram(state, state.shad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light")) {
|
class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("light"), FORMAT) {
|
||||||
var baselineColor by F4Uniform("baselineColor")
|
var baselineColor by F4Uniform("baselineColor")
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class GLLightProgram(state: GLStateTracker) : GLShaderProgram(state, state.shade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad")) {
|
class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad"), FORMAT) {
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
|
|
||||||
private val builder by lazy {
|
private val builder by lazy {
|
||||||
@ -94,7 +94,7 @@ class GLColorQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex")) {
|
class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex"), FORMAT) {
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
private val builder by lazy {
|
private val builder by lazy {
|
||||||
@ -118,7 +118,7 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur")) {
|
class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("screen_quad_tex_blur"), FORMAT) {
|
||||||
var texture by IUniform("texture0")
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
private val builder by lazy {
|
private val builder by lazy {
|
||||||
@ -142,7 +142,7 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLShaderProgram(state
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color")) {
|
class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat_color"), FORMAT) {
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
@ -154,7 +154,22 @@ class GLFlatColorProgram(state: GLStateTracker) : GLShaderProgram(state, state.s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("tile")) {
|
class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("tile"), FORMAT) {
|
||||||
|
var transform by F4x4Uniform("transform")
|
||||||
|
var color by F4Uniform("color")
|
||||||
|
var texture by IUniform("texture0")
|
||||||
|
|
||||||
|
init {
|
||||||
|
transform = Matrix4f.identity()
|
||||||
|
color = RGBAColor.WHITE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val FORMAT = GLAttributeList.Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.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")
|
||||||
@ -165,18 +180,7 @@ class GLTileProgram(state: GLStateTracker) : GLShaderProgram(state, state.shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLFontProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("font")) {
|
class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat"), GLAttributeList.VEC2F) {
|
||||||
var transform by F4x4Uniform("transform")
|
|
||||||
var color by F4Uniform("color")
|
|
||||||
var texture by IUniform("texture0")
|
|
||||||
|
|
||||||
init {
|
|
||||||
transform = Matrix4f.identity()
|
|
||||||
color = RGBAColor.WHITE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFlatProgram(state: GLStateTracker) : GLShaderProgram(state, state.shaders("flat")) {
|
|
||||||
var transform by F4x4Uniform("transform")
|
var transform by F4x4Uniform("transform")
|
||||||
var color by F4Uniform("color")
|
var color by F4Uniform("color")
|
||||||
|
|
||||||
@ -185,7 +189,7 @@ 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"))) {
|
class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.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")
|
||||||
|
|
||||||
@ -194,7 +198,7 @@ class GLTexturedProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.internalFragment("shaders/fragment/texture_color.glsl"))) {
|
class GLTexturedColoredProgram(state: GLStateTracker) : GLShaderProgram(state, listOf(state.internalVertex("shaders/vertex/texture.glsl"), state.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")
|
||||||
|
@ -83,12 +83,7 @@ class GLAttributeList(builder: Builder) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val VEC2F = Builder().push(GLType.VEC2F).build()
|
val VEC2F = Builder().push(GLType.VEC2F).build()
|
||||||
val VEC3F = Builder().push(GLType.VEC3F).build()
|
|
||||||
|
|
||||||
val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
||||||
|
|
||||||
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
||||||
|
|
||||||
val TILE = Builder().push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ 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.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
import java.io.Closeable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
||||||
@ -13,7 +12,7 @@ class StreamVertexBuilder(
|
|||||||
attributes: GLAttributeList,
|
attributes: GLAttributeList,
|
||||||
type: GeometryType,
|
type: GeometryType,
|
||||||
initialCapacity: Int = 64,
|
initialCapacity: Int = 64,
|
||||||
) : Closeable {
|
) {
|
||||||
val builder = VertexBuilder(attributes, type, initialCapacity)
|
val builder = VertexBuilder(attributes, type, initialCapacity)
|
||||||
private val vao = state.newVAO()
|
private val vao = state.newVAO()
|
||||||
private val vbo = state.newVBO()
|
private val vbo = state.newVBO()
|
||||||
@ -44,12 +43,6 @@ class StreamVertexBuilder(
|
|||||||
checkForGLError()
|
checkForGLError()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
vao.close()
|
|
||||||
vbo.close()
|
|
||||||
ebo.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4f
|
|
||||||
import ru.dbotthepony.kvector.arrays.Matrix4fStack
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Служит для быстрой настройки состояния для будущей отрисовки
|
|
||||||
*
|
|
||||||
* Установка текстурных блоков, программы, самих текстур и загрузка юниформ должна осуществляться тут
|
|
||||||
*
|
|
||||||
* Класс обязан быть наследован для осмысленных результатов
|
|
||||||
*
|
|
||||||
* Ожидается, что состояние будет выставлено ПОЛНОСТЬЮ, т.е. НИКАКОЙ предыдущий код НЕ МОЖЕТ повлиять на результат выполнения
|
|
||||||
* шейдерной программы, которая связанна с этим объектом (за исключением не вызова [setTransform] внешним кодом)
|
|
||||||
*/
|
|
||||||
open class ConfiguredShaderProgram<T : GLShaderProgram>(
|
|
||||||
val program: T,
|
|
||||||
) {
|
|
||||||
private val transformLocation = program.getUniform("transform") as? GLShaderProgram.F4x4Uniform
|
|
||||||
|
|
||||||
open fun setTransform(value: Matrix4f) {
|
|
||||||
transformLocation?.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Вызывается перед началом отрисовки
|
|
||||||
*/
|
|
||||||
open fun setup() {
|
|
||||||
program.use()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Запечённый статичный меш, позволяет быстро отрисовать меш со всеми параметрами
|
|
||||||
* с заданной матрицей трансформации
|
|
||||||
*/
|
|
||||||
class ConfiguredStaticMesh(
|
|
||||||
val programState: ConfiguredShaderProgram<*>,
|
|
||||||
val indexCount: Int,
|
|
||||||
val vao: VertexArrayObject,
|
|
||||||
val elementIndexType: Int,
|
|
||||||
) : AutoCloseable {
|
|
||||||
private var onClose = {}
|
|
||||||
|
|
||||||
constructor(programState: ConfiguredShaderProgram<*>, builder: VertexBuilder) : this(
|
|
||||||
programState,
|
|
||||||
builder.indexCount,
|
|
||||||
programState.program.state.newVAO(),
|
|
||||||
builder.indexType,
|
|
||||||
) {
|
|
||||||
val vbo = programState.program.state.newVBO()
|
|
||||||
val ebo = programState.program.state.newEBO()
|
|
||||||
|
|
||||||
onClose = {
|
|
||||||
vbo.close()
|
|
||||||
ebo.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
vao.bind()
|
|
||||||
vbo.bind()
|
|
||||||
ebo.bind()
|
|
||||||
|
|
||||||
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
|
||||||
builder.attributes.apply(vao, true)
|
|
||||||
|
|
||||||
vao.unbind()
|
|
||||||
vbo.unbind()
|
|
||||||
ebo.unbind()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun render(transform: Matrix4f? = null) {
|
|
||||||
check(isValid) { "$this is no longer valid" }
|
|
||||||
programState.setup()
|
|
||||||
|
|
||||||
if (transform != null) {
|
|
||||||
programState.setTransform(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
vao.bind()
|
|
||||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renderStacked(transform: Matrix4fStack) = render(transform.last())
|
|
||||||
|
|
||||||
var isValid = true
|
|
||||||
private set
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
vao.close()
|
|
||||||
onClose.invoke()
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
}
|
|
@ -244,7 +244,7 @@ class Font(
|
|||||||
return size(breakLines(text))
|
return size(breakLines(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Glyph(val char: Char) : AutoCloseable {
|
private inner class Glyph(val char: Char) {
|
||||||
private val texture: GLTexture2D?
|
private val texture: GLTexture2D?
|
||||||
|
|
||||||
val isEmpty: Boolean
|
val isEmpty: Boolean
|
||||||
@ -349,13 +349,5 @@ class Font(
|
|||||||
|
|
||||||
stack.last().translateWithMultiplication(advanceX - bearingX, bearingY)
|
stack.last().translateWithMultiplication(advanceX - bearingX, bearingY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
vao?.close()
|
|
||||||
ebo?.close()
|
|
||||||
vbo?.close()
|
|
||||||
|
|
||||||
texture?.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ 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.gl.GLStateTracker
|
||||||
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.vertex.VertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
|
|
||||||
class Mesh(state: GLStateTracker) {
|
class Mesh(state: GLStateTracker) {
|
||||||
constructor(state: GLStateTracker, builder: VertexBuilder) : this(state) {
|
constructor(state: GLStateTracker, builder: VertexBuilder) : this(state) {
|
||||||
@ -39,5 +41,14 @@ class Mesh(state: GLStateTracker) {
|
|||||||
vao.bind()
|
vao.bind()
|
||||||
GL46.glDrawElements(GL46.GL_TRIANGLES, indexCount, indexType, 0L)
|
GL46.glDrawElements(GL46.GL_TRIANGLES, indexCount, indexType, 0L)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
vao.unbind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ConfiguredMesh<T : GLShaderProgram>(val config: RenderConfig<T>, val mesh: Mesh = Mesh(config.state)) {
|
||||||
|
fun render(transform: Matrix4f = config.state.matrixStack.last()) {
|
||||||
|
config.setup(transform)
|
||||||
|
mesh.render()
|
||||||
|
config.uninstall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
class MultiMeshBuilder {
|
||||||
|
data class Entry(val config: RenderConfig<*>, val builder: VertexBuilder, val layer: Int)
|
||||||
|
|
||||||
|
private val meshes = Reference2ObjectOpenHashMap<RenderConfig<*>, Int2ObjectOpenHashMap<Entry>>()
|
||||||
|
|
||||||
|
fun get(config: RenderConfig<*>, layer: Int): VertexBuilder {
|
||||||
|
return meshes.computeIfAbsent(config, Reference2ObjectFunction {
|
||||||
|
Int2ObjectOpenHashMap()
|
||||||
|
}).computeIfAbsent(layer, Int2ObjectFunction { Entry(config, VertexBuilder(config.program.attributes, GeometryType.QUADS), layer) }).builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() = meshes.clear()
|
||||||
|
fun isEmpty() = meshes.isEmpty()
|
||||||
|
fun isNotEmpty() = meshes.isNotEmpty()
|
||||||
|
|
||||||
|
fun meshes(): Stream<Entry> {
|
||||||
|
return meshes.values.stream().flatMap { it.values.stream() }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLShaderProgram
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
|
|
||||||
|
abstract class RenderConfig<out T : GLShaderProgram>(val state: GLStateTracker, val program: T) {
|
||||||
|
open fun setup(transform: Matrix4f = state.matrixStack.last()) {
|
||||||
|
program.use()
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun uninstall() {}
|
||||||
|
}
|
@ -1,54 +1,21 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.*
|
import ru.dbotthepony.kstarbound.client.gl.*
|
||||||
import ru.dbotthepony.kstarbound.client.gl.shader.GLTileProgram
|
import ru.dbotthepony.kstarbound.client.gl.shader.GLTileProgram
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.*
|
import ru.dbotthepony.kstarbound.client.gl.vertex.*
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.*
|
import ru.dbotthepony.kstarbound.defs.tile.*
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.ITileState
|
import ru.dbotthepony.kstarbound.world.api.ITileState
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
|
import ru.dbotthepony.kvector.arrays.Matrix4f
|
||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
import java.util.stream.Stream
|
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
data class TileLayer(
|
|
||||||
val program: ConfiguredShaderProgram<GLTileProgram>,
|
|
||||||
val vertices: VertexBuilder,
|
|
||||||
val zPos: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
class TileLayerList {
|
|
||||||
private val layers = HashMap<ConfiguredShaderProgram<GLTileProgram>, Int2ObjectOpenHashMap<TileLayer>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получает геометрию слоя ([DynamicVertexBuilder]), который имеет программу для отрисовки [program] и располагается на [zLevel].
|
|
||||||
*
|
|
||||||
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute].
|
|
||||||
*/
|
|
||||||
fun computeIfAbsent(program: ConfiguredShaderProgram<GLTileProgram>, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder {
|
|
||||||
return layers.computeIfAbsent(program) { Int2ObjectOpenHashMap() }.computeIfAbsent(zLevel, Int2ObjectFunction {
|
|
||||||
return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
|
|
||||||
}).vertices
|
|
||||||
}
|
|
||||||
|
|
||||||
fun layers(): Stream<TileLayer> {
|
|
||||||
return layers.values.stream().flatMap { it.values.stream() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() = layers.clear()
|
|
||||||
|
|
||||||
val isEmpty get() = layers.isEmpty()
|
|
||||||
val isNotEmpty get() = layers.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хранит в себе программы для отрисовки определённых [TileDefinition]
|
* Хранит в себе программы для отрисовки определённых [TileDefinition]
|
||||||
*
|
*
|
||||||
@ -56,28 +23,29 @@ class TileLayerList {
|
|||||||
*/
|
*/
|
||||||
class TileRenderers(val client: StarboundClient) {
|
class TileRenderers(val client: StarboundClient) {
|
||||||
val state get() = client.gl
|
val state get() = client.gl
|
||||||
private val foregroundTilePrograms = HashMap<GLTexture2D, ForegroundTileProgram>()
|
|
||||||
private val backgroundTilePrograms = HashMap<GLTexture2D, BackgroundTileProgram>()
|
|
||||||
private val tileRenderersCache = HashMap<String, TileRenderer>()
|
|
||||||
private val modifierRenderersCache = HashMap<String, TileRenderer>()
|
|
||||||
|
|
||||||
fun getTileRenderer(defName: String): TileRenderer {
|
private val foreground = HashMap<GLTexture2D, Config>()
|
||||||
return tileRenderersCache.computeIfAbsent(defName) {
|
private val background = HashMap<GLTexture2D, Config>()
|
||||||
|
private val matCache = HashMap<String, TileRenderer>()
|
||||||
|
private val modCache = HashMap<String, TileRenderer>()
|
||||||
|
|
||||||
|
fun getMaterialRenderer(defName: String): TileRenderer {
|
||||||
|
return matCache.computeIfAbsent(defName) {
|
||||||
val def = client.starbound.tiles[defName] // TODO: Пустой рендерер
|
val def = client.starbound.tiles[defName] // TODO: Пустой рендерер
|
||||||
return@computeIfAbsent TileRenderer(this, def!!.value)
|
return@computeIfAbsent TileRenderer(this, def!!.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModifierRenderer(defName: String): TileRenderer {
|
fun getModifierRenderer(defName: String): TileRenderer {
|
||||||
return modifierRenderersCache.computeIfAbsent(defName) {
|
return modCache.computeIfAbsent(defName) {
|
||||||
val def = client.starbound.tileModifiers[defName] // TODO: Пустой рендерер
|
val def = client.starbound.tileModifiers[defName] // TODO: Пустой рендерер
|
||||||
return@computeIfAbsent TileRenderer(this, def!!.value)
|
return@computeIfAbsent TileRenderer(this, def!!.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ForegroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram<GLTileProgram>(state.programs.tile) {
|
private inner class Config(private val texture: GLTexture2D, private val color: RGBAColor) : RenderConfig<GLTileProgram>(state, state.programs.tile) {
|
||||||
override fun setup() {
|
override fun setup(transform: Matrix4f) {
|
||||||
super.setup()
|
super.setup(transform)
|
||||||
state.activeTexture = 0
|
state.activeTexture = 0
|
||||||
state.depthTest = false
|
state.depthTest = false
|
||||||
program.texture = 0
|
program.texture = 0
|
||||||
@ -85,68 +53,19 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
texture.textureMagFilter = GL_NEAREST
|
texture.textureMagFilter = GL_NEAREST
|
||||||
texture.textureMinFilter = GL_NEAREST
|
texture.textureMinFilter = GL_NEAREST
|
||||||
|
|
||||||
program.color = FOREGROUND_COLOR
|
program.transform = transform
|
||||||
|
program.color = color
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun uninstall() {}
|
||||||
if (this === other) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other is ForegroundTileProgram) {
|
|
||||||
return texture == other.texture
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.equals(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return texture.hashCode()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class BackgroundTileProgram(private val texture: GLTexture2D) : ConfiguredShaderProgram<GLTileProgram>(state.programs.tile) {
|
fun foreground(texture: GLTexture2D): RenderConfig<GLTileProgram> {
|
||||||
override fun setup() {
|
return foreground.computeIfAbsent(texture) { Config(it, FOREGROUND_COLOR) }
|
||||||
super.setup()
|
|
||||||
state.activeTexture = 0
|
|
||||||
state.depthTest = false
|
|
||||||
program.texture = 0
|
|
||||||
texture.bind()
|
|
||||||
texture.textureMagFilter = GL_NEAREST
|
|
||||||
texture.textureMinFilter = GL_NEAREST
|
|
||||||
|
|
||||||
program.color = BACKGROUND_COLOR
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other is BackgroundTileProgram) {
|
|
||||||
return texture == other.texture
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.equals(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
return texture.hashCode()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun background(texture: GLTexture2D): RenderConfig<GLTileProgram> {
|
||||||
* Возвращает запечённое состояние шейдера shaderVertexTexture с данной текстурой
|
return background.computeIfAbsent(texture) { Config(it, BACKGROUND_COLOR) }
|
||||||
*/
|
|
||||||
fun foreground(texture: GLTexture2D): ConfiguredShaderProgram<GLTileProgram> {
|
|
||||||
return foregroundTilePrograms.computeIfAbsent(texture, ::ForegroundTileProgram)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает запечённое состояние шейдера shaderVertexTextureRGBAColor с данной текстурой
|
|
||||||
*/
|
|
||||||
fun background(texture: GLTexture2D): ConfiguredShaderProgram<GLTileProgram> {
|
|
||||||
return backgroundTilePrograms.computeIfAbsent(texture, ::BackgroundTileProgram)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -155,8 +74,6 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun vertexTextureBuilder() = VertexBuilder(GLAttributeList.TILE, GeometryType.QUADS)
|
|
||||||
|
|
||||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||||
override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean {
|
override fun test(thisTile: ITileState?, otherTile: ITileState?): Boolean {
|
||||||
return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift
|
return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift
|
||||||
@ -236,7 +153,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
self: ITileState,
|
self: ITileState,
|
||||||
matchPiece: RenderMatch,
|
matchPiece: RenderMatch,
|
||||||
getter: ITileAccess,
|
getter: ITileAccess,
|
||||||
layers: TileLayerList,
|
meshBuilder: MultiMeshBuilder,
|
||||||
pos: Vector2i,
|
pos: Vector2i,
|
||||||
thisBuilder: VertexBuilder,
|
thisBuilder: VertexBuilder,
|
||||||
background: Boolean,
|
background: Boolean,
|
||||||
@ -251,14 +168,14 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
renderers.foreground(state.loadTexture(renderPiece.piece.texture!!))
|
renderers.foreground(state.loadTexture(renderPiece.piece.texture!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier)
|
tesselateAt(self, renderPiece.piece, getter, meshBuilder.get(program, def.renderParameters.zLevel), pos, renderPiece.offset, isModifier)
|
||||||
} else {
|
} else {
|
||||||
tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
|
tesselateAt(self, renderPiece.piece, getter, thisBuilder, pos, renderPiece.offset, isModifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (subPiece in matchPiece.subMatches) {
|
for (subPiece in matchPiece.subMatches) {
|
||||||
val matched = tesselatePiece(self, subPiece, getter, layers, pos, thisBuilder, background, isModifier)
|
val matched = tesselatePiece(self, subPiece, getter, meshBuilder, pos, thisBuilder, background, isModifier)
|
||||||
|
|
||||||
if (matched == TestResult.HALT || matched == TestResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
if (matched == TestResult.HALT || matched == TestResult.CONTINUE && matchPiece.haltOnSubMatch) {
|
||||||
return TestResult.HALT
|
return TestResult.HALT
|
||||||
@ -280,20 +197,20 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
|||||||
*
|
*
|
||||||
* [getter] Нужен для получения информации о ближайших блоках
|
* [getter] Нужен для получения информации о ближайших блоках
|
||||||
*
|
*
|
||||||
* [layers] содержит текущие программы и их билдеры и их zPos
|
* [meshBuilder] содержит текущие программы и их билдеры и их zPos
|
||||||
*
|
*
|
||||||
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
|
* Тесселирует тайлы в нужный VertexBuilder с масштабом согласно константе [PIXELS_IN_STARBOUND_UNITf]
|
||||||
*/
|
*/
|
||||||
fun tesselate(self: ITileState, getter: ITileAccess, layers: TileLayerList, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) {
|
fun tesselate(self: ITileState, getter: ITileAccess, meshBuilder: MultiMeshBuilder, pos: Vector2i, background: Boolean = false, isModifier: Boolean = false) {
|
||||||
// если у нас нет renderTemplate
|
// если у нас нет renderTemplate
|
||||||
// то мы просто не можем его отрисовать
|
// то мы просто не можем его отрисовать
|
||||||
val template = def.renderTemplate.value ?: return
|
val template = def.renderTemplate.value ?: return
|
||||||
|
|
||||||
val vertexBuilder = layers.computeIfAbsent(if (background) bakedBackgroundProgramState else bakedProgramState, def.renderParameters.zLevel, ::vertexTextureBuilder)
|
val vertexBuilder = meshBuilder.get(if (background) bakedBackgroundProgramState else bakedProgramState, def.renderParameters.zLevel)
|
||||||
|
|
||||||
for ((_, matcher) in template.matches) {
|
for ((_, matcher) in template.matches) {
|
||||||
for (matchPiece in matcher) {
|
for (matchPiece in matcher) {
|
||||||
val matched = tesselatePiece(self, matchPiece, getter, layers, pos, vertexBuilder, background, isModifier)
|
val matched = tesselatePiece(self, matchPiece, getter, meshBuilder, pos, vertexBuilder, background, isModifier)
|
||||||
|
|
||||||
if (matched == TestResult.HALT) {
|
if (matched == TestResult.HALT) {
|
||||||
break
|
break
|
||||||
|
@ -7,14 +7,13 @@ 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
|
||||||
import java.io.Closeable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Базовый класс, отвечающий за отрисовку определённого ентити в мире
|
* Базовый класс, отвечающий за отрисовку определённого ентити в мире
|
||||||
*
|
*
|
||||||
* Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход)
|
* Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход)
|
||||||
*/
|
*/
|
||||||
open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) : Closeable {
|
open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) {
|
||||||
inline val state: GLStateTracker get() = client.gl
|
inline val state: GLStateTracker get() = client.gl
|
||||||
open val renderPos: Vector2d get() = entity.position
|
open val renderPos: Vector2d get() = entity.position
|
||||||
|
|
||||||
@ -30,10 +29,6 @@ open class EntityRenderer(val client: StarboundClient, val entity: Entity, open
|
|||||||
|
|
||||||
open val layer: Int get() = Z_LEVEL_ENTITIES
|
open val layer: Int get() = Z_LEVEL_ENTITIES
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Pseudo Z position for entities, for them to appear behind tile geometry,
|
* Pseudo Z position for entities, for them to appear behind tile geometry,
|
||||||
|
@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.io
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|
||||||
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
import ru.dbotthepony.kstarbound.api.IStarboundFile
|
||||||
import ru.dbotthepony.kstarbound.io.json.BinaryJsonReader
|
import ru.dbotthepony.kstarbound.io.json.BinaryJsonReader
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
|
Loading…
Reference in New Issue
Block a user