Redo vertex builder
This commit is contained in:
parent
48cf205506
commit
194a7e479c
@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.client
|
|||||||
|
|
||||||
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.gl.VertexTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
|
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
|
||||||
import ru.dbotthepony.kstarbound.defs.ParallaxPrototype
|
import ru.dbotthepony.kstarbound.defs.ParallaxPrototype
|
||||||
@ -10,7 +10,6 @@ import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
|
|||||||
import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression
|
import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression
|
||||||
import ru.dbotthepony.kstarbound.world.*
|
import ru.dbotthepony.kstarbound.world.*
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
|
|
||||||
class ClientWorld(
|
class ClientWorld(
|
||||||
@ -49,8 +48,7 @@ class ClientWorld(
|
|||||||
|
|
||||||
client.gl.shaderVertexTexture.use()
|
client.gl.shaderVertexTexture.use()
|
||||||
|
|
||||||
val stateful = client.gl.flat2DTexturedQuads.statefulSmall
|
val builder = client.gl.flat2DTexturedQuads.small
|
||||||
val builder = stateful.builder
|
|
||||||
|
|
||||||
client.gl.activeTexture = 0
|
client.gl.activeTexture = 0
|
||||||
client.gl.shaderVertexTexture["_texture"] = 0
|
client.gl.shaderVertexTexture["_texture"] = 0
|
||||||
@ -80,7 +78,7 @@ class ClientWorld(
|
|||||||
x0 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
x0 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
||||||
x1 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
x1 += diffx.toFloat() / PIXELS_IN_STARBOUND_UNITf
|
||||||
|
|
||||||
builder.quadZ(x0, diffy, x1, diffy + texture.height.toFloat() / PIXELS_IN_STARBOUND_UNITf, 1f, VertexTransformers.uv(0f, 1f, 1f, 0f))
|
builder.quadZ(x0, diffy, x1, diffy + texture.height.toFloat() / PIXELS_IN_STARBOUND_UNITf, 1f, QuadTransformers.uv(0f, 1f, 1f, 0f))
|
||||||
|
|
||||||
/*if (x1 < size.mins.x) {
|
/*if (x1 < size.mins.x) {
|
||||||
break
|
break
|
||||||
@ -91,8 +89,8 @@ class ClientWorld(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateful.upload()
|
builder.upload()
|
||||||
stateful.draw()
|
builder.draw()
|
||||||
|
|
||||||
client.gl.matrixStack.pop()
|
client.gl.matrixStack.pop()
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,19 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
package ru.dbotthepony.kstarbound.client.gl
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import org.lwjgl.opengl.GL46.*
|
|
||||||
|
|
||||||
enum class GLType(val identity: Int, val typeIndentity: Int, val byteSize: Int, val logicalSize: Int) {
|
|
||||||
INT(GL_INT, GL_INT, 4, 1),
|
|
||||||
UINT(GL_UNSIGNED_INT, GL_UNSIGNED_INT, 4, 1),
|
|
||||||
FLOAT(GL_FLOAT, GL_FLOAT, 4, 1),
|
|
||||||
DOUBLE(GL_DOUBLE, GL_DOUBLE, 8, 1),
|
|
||||||
|
|
||||||
VEC2F(GL_FLOAT_VEC2, GL_FLOAT, 8, 2),
|
|
||||||
VEC3F(GL_FLOAT_VEC3, GL_FLOAT, 12, 3),
|
|
||||||
VEC4F(GL_FLOAT_VEC4, GL_FLOAT, 16, 4),
|
|
||||||
|
|
||||||
VEC2I(GL_INT_VEC2, GL_INT, 8, 2),
|
|
||||||
VEC3I(GL_INT_VEC3, GL_INT, 12, 3),
|
|
||||||
VEC4I(GL_INT_VEC4, GL_INT, 16, 4),
|
|
||||||
|
|
||||||
MAT2F(GL_FLOAT_MAT2, GL_FLOAT, 2 * 2 * 4, 2 * 2),
|
|
||||||
MAT3F(GL_FLOAT_MAT3, GL_FLOAT, 3 * 3 * 4, 3 * 3),
|
|
||||||
MAT4F(GL_FLOAT_MAT4, GL_FLOAT, 4 * 4 * 4, 4 * 4),
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IGLAttributeList {
|
|
||||||
/**
|
|
||||||
* Применяет список атрибутов к заданному [GLVertexArrayObject] (попутно включая или отключая их через [enable])
|
|
||||||
*/
|
|
||||||
fun apply(target: GLVertexArrayObject, enable: Boolean = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class AttributeListPosition(val name: String, val index: Int, val glType: GLType, val stride: Int, val offset: Long)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хранит список аттрибутов для применения к Vertex Array Object
|
* Хранит список аттрибутов для применения к Vertex Array Object
|
||||||
*
|
*
|
||||||
* Аттрибуты плотно упакованы и идут один за другим
|
* Аттрибуты плотно упакованы и идут один за другим
|
||||||
*
|
*
|
||||||
* Создаётся через [GLFlatAttributeListBuilder]
|
* Создаётся через [GLAttributeList.Builder]
|
||||||
*/
|
*/
|
||||||
class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeList {
|
class GLAttributeList(builder: Builder) {
|
||||||
val attributes: List<AttributeListPosition>
|
data class Attribute(val name: String, val index: Int, val glType: GLType, val stride: Int, val offset: Long)
|
||||||
|
|
||||||
|
val attributes: List<Attribute>
|
||||||
val size get() = attributes.size
|
val size get() = attributes.size
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,10 +23,8 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
|||||||
|
|
||||||
operator fun get(index: Int) = attributes[index]
|
operator fun get(index: Int) = attributes[index]
|
||||||
|
|
||||||
fun vertexBuilder(vertexType: VertexType) = DynamicVertexBuilder(this, vertexType)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val buildList = ArrayList<AttributeListPosition>()
|
val buildList = ArrayList<Attribute>()
|
||||||
|
|
||||||
var offset = 0L
|
var offset = 0L
|
||||||
var stride = 0
|
var stride = 0
|
||||||
@ -63,20 +35,19 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
|||||||
|
|
||||||
this.stride = stride
|
this.stride = stride
|
||||||
|
|
||||||
// val counter = mutableMapOf<Int, Int>()
|
|
||||||
|
|
||||||
for (i in builder.attributes.indices) {
|
for (i in builder.attributes.indices) {
|
||||||
val value = builder.attributes[i].second
|
val value = builder.attributes[i].second
|
||||||
buildList.add(AttributeListPosition(builder.attributes[i].first, i, value, stride, offset))
|
buildList.add(Attribute(builder.attributes[i].first, i, value, stride, offset))
|
||||||
offset += value.byteSize
|
offset += value.byteSize
|
||||||
|
|
||||||
// counter[value.typeIndentity] = (counter[value.typeIndentity] ?: 0) + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes = ImmutableList.copyOf(buildList)
|
attributes = ImmutableList.copyOf(buildList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
/**
|
||||||
|
* Применяет список атрибутов к заданному [GLVertexArrayObject] (попутно включая или отключая их через [enable])
|
||||||
|
*/
|
||||||
|
fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
||||||
for (i in attributes.indices) {
|
for (i in attributes.indices) {
|
||||||
val value = attributes[i]
|
val value = attributes[i]
|
||||||
target.attribute(i, value.glType.logicalSize, value.glType.typeIndentity, false, value.stride, value.offset)
|
target.attribute(i, value.glType.logicalSize, value.glType.typeIndentity, false, value.stride, value.offset)
|
||||||
@ -87,66 +58,36 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
val attributes = ArrayList<Pair<String, GLType>>()
|
||||||
|
|
||||||
|
fun push(type: GLType): Builder {
|
||||||
|
return push("$type#${attributes.size}", type)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(vararg types: GLType): Builder {
|
||||||
|
for (type in types) push(type)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(name: String, type: GLType): Builder {
|
||||||
|
check(!attributes.any { it.first == name }) { "Already has named attribute $name!" }
|
||||||
|
attributes.add(name to type)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build() = GLAttributeList(this)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val VEC2F = GLFlatAttributeListBuilder().push(GLType.VEC2F).build()
|
val VEC2F = Builder().push(GLType.VEC2F).build()
|
||||||
val VEC3F = GLFlatAttributeListBuilder().push(GLType.VEC3F).build()
|
val VEC3F = Builder().push(GLType.VEC3F).build()
|
||||||
|
|
||||||
val VERTEX_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
val VERTEX_TEXTURE = Builder().push(GLType.VEC3F).push(GLType.VEC2F).build()
|
||||||
|
|
||||||
val VERTEX_HSV_TEXTURE = GLFlatAttributeListBuilder()
|
val VERTEX_HSV_TEXTURE = Builder()
|
||||||
.push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
.push(GLType.VEC3F, GLType.VEC2F, GLType.FLOAT).build()
|
||||||
|
|
||||||
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GLFlatAttributeListBuilder : IGLAttributeList {
|
|
||||||
val attributes = ArrayList<Pair<String, GLType>>()
|
|
||||||
|
|
||||||
private fun findName(name: String): Boolean {
|
|
||||||
for (value in attributes) {
|
|
||||||
if (value.first == name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(type: GLType): GLFlatAttributeListBuilder {
|
|
||||||
return push("$type#${attributes.size}", type)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(vararg types: GLType): GLFlatAttributeListBuilder {
|
|
||||||
for (type in types) push(type)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun push(name: String, type: GLType): GLFlatAttributeListBuilder {
|
|
||||||
check(!findName(name)) { "Already has named attribute $name!" }
|
|
||||||
attributes.add(name to type)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build() = GLFlatAttributeList(this)
|
|
||||||
|
|
||||||
@Deprecated("Используй build()", replaceWith = ReplaceWith("build()"))
|
|
||||||
override fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
|
||||||
var offset = 0L
|
|
||||||
var stride = 0
|
|
||||||
|
|
||||||
for (i in attributes) {
|
|
||||||
stride += i.second.byteSize
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i in attributes.indices) {
|
|
||||||
val value = attributes[i].second
|
|
||||||
target.attribute(i, value.logicalSize, value.typeIndentity, false, stride, offset)
|
|
||||||
offset += value.byteSize
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
target.enableAttribute(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import org.lwjgl.opengl.GL
|
|||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.FreeType
|
import ru.dbotthepony.kstarbound.client.freetype.FreeType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
|
||||||
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
|
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.Font
|
import ru.dbotthepony.kstarbound.client.render.Font
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||||
@ -86,7 +88,6 @@ interface GLCleanable : Cleaner.Cleanable {
|
|||||||
|
|
||||||
interface GLStreamBuilderList {
|
interface GLStreamBuilderList {
|
||||||
val small: StreamVertexBuilder
|
val small: StreamVertexBuilder
|
||||||
val statefulSmall: StatefulStreamVertexBuilder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLStateTracker {
|
class GLStateTracker {
|
||||||
@ -403,61 +404,37 @@ class GLStateTracker {
|
|||||||
|
|
||||||
val flat2DLines = object : GLStreamBuilderList {
|
val flat2DLines = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.LINES, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.LINES, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DTriangles = object : GLStreamBuilderList {
|
val flat2DTriangles = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.TRIANGLES, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.TRIANGLES, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DQuads = object : GLStreamBuilderList {
|
val flat2DQuads = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DTexturedQuads = object : GLStreamBuilderList {
|
val flat2DTexturedQuads = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, VertexType.QUADS, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DQuadLines = object : GLStreamBuilderList {
|
val flat2DQuadLines = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS_AS_LINES, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flat2DQuadWireframe = object : GLStreamBuilderList {
|
val flat2DQuadWireframe = object : GLStreamBuilderList {
|
||||||
override val small by lazy {
|
override val small by lazy {
|
||||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 1024)
|
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 1024)
|
||||||
}
|
|
||||||
|
|
||||||
override val statefulSmall by lazy {
|
|
||||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,20 +444,17 @@ class GLStateTracker {
|
|||||||
val font = Font(this)
|
val font = Font(this)
|
||||||
|
|
||||||
inline fun quadWireframe(color: Color = Color.WHITE, lambda: (StreamVertexBuilder) -> Unit) {
|
inline fun quadWireframe(color: Color = Color.WHITE, lambda: (StreamVertexBuilder) -> Unit) {
|
||||||
val stateful = flat2DQuadWireframe.statefulSmall
|
val builder = flat2DQuadWireframe.small
|
||||||
val builder = stateful.builder
|
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
lambda.invoke(builder)
|
lambda.invoke(builder)
|
||||||
|
builder.upload()
|
||||||
stateful.upload()
|
|
||||||
|
|
||||||
flatProgram.use()
|
flatProgram.use()
|
||||||
flatProgram.color.set(color)
|
flatProgram.color.set(color)
|
||||||
flatProgram.transform.set(matrixStack.last)
|
flatProgram.transform.set(matrixStack.last)
|
||||||
|
|
||||||
stateful.draw(GL_LINES)
|
builder.draw(GL_LINES)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) {
|
inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL46.*
|
||||||
|
|
||||||
|
enum class GLType(
|
||||||
|
val identity: Int,
|
||||||
|
val typeIndentity: Int,
|
||||||
|
val byteSize: Int,
|
||||||
|
val logicalSize: Int,
|
||||||
|
) {
|
||||||
|
INT(GL_INT, GL_INT, 4, 1),
|
||||||
|
UINT(GL_UNSIGNED_INT, GL_UNSIGNED_INT, 4, 1),
|
||||||
|
FLOAT(GL_FLOAT, GL_FLOAT, 4, 1),
|
||||||
|
DOUBLE(GL_DOUBLE, GL_DOUBLE, 8, 1),
|
||||||
|
|
||||||
|
VEC2F(GL_FLOAT_VEC2, GL_FLOAT, 8, 2),
|
||||||
|
VEC3F(GL_FLOAT_VEC3, GL_FLOAT, 12, 3),
|
||||||
|
VEC4F(GL_FLOAT_VEC4, GL_FLOAT, 16, 4),
|
||||||
|
|
||||||
|
VEC2I(GL_INT_VEC2, GL_INT, 8, 2),
|
||||||
|
VEC3I(GL_INT_VEC3, GL_INT, 12, 3),
|
||||||
|
VEC4I(GL_INT_VEC4, GL_INT, 16, 4),
|
||||||
|
|
||||||
|
MAT2F(GL_FLOAT_MAT2, GL_FLOAT, 2 * 2 * 4, 2 * 2),
|
||||||
|
MAT3F(GL_FLOAT_MAT3, GL_FLOAT, 3 * 3 * 4, 3 * 3),
|
||||||
|
MAT4F(GL_FLOAT_MAT4, GL_FLOAT, 4 * 4 * 4, 4 * 4),
|
||||||
|
}
|
@ -1,601 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
enum class VertexType(val elements: Int, val indicies: IntArray) {
|
|
||||||
LINES(2, intArrayOf(0, 1)),
|
|
||||||
TRIANGLES(3, intArrayOf(0, 1, 2)),
|
|
||||||
QUADS(4, intArrayOf(0, 1, 2, 1, 2, 3)),
|
|
||||||
QUADS_AS_LINES(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3)),
|
|
||||||
QUADS_AS_LINES_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IVertexBuilder<This : IVertexBuilder<This, VertexType>, VertexType : IVertex<VertexType, This>> {
|
|
||||||
val type: ru.dbotthepony.kstarbound.client.gl.VertexType
|
|
||||||
val indexCount: Int
|
|
||||||
|
|
||||||
fun begin(): This
|
|
||||||
fun vertex(): VertexType
|
|
||||||
fun checkValid()
|
|
||||||
fun upload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int = GL_DYNAMIC_DRAW)
|
|
||||||
|
|
||||||
fun quad(
|
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
lambda: VertexTransformer = emptyTransform
|
|
||||||
): This {
|
|
||||||
check(type.elements == 4) { "Currently building $type" }
|
|
||||||
|
|
||||||
lambda(vertex().pushVec2f(x0, y0), 0).end()
|
|
||||||
lambda(vertex().pushVec2f(x1, y0), 1).end()
|
|
||||||
lambda(vertex().pushVec2f(x0, y1), 2).end()
|
|
||||||
lambda(vertex().pushVec2f(x1, y1), 3).end()
|
|
||||||
|
|
||||||
return this as This
|
|
||||||
}
|
|
||||||
|
|
||||||
fun quadRotated(
|
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
x: Float,
|
|
||||||
y: Float,
|
|
||||||
angle: Double,
|
|
||||||
lambda: VertexTransformer = emptyTransform
|
|
||||||
): This {
|
|
||||||
check(type.elements == 4) { "Currently building $type" }
|
|
||||||
|
|
||||||
val s = sin(angle).toFloat()
|
|
||||||
val c = cos(angle).toFloat()
|
|
||||||
|
|
||||||
lambda(vertex().pushVec2f(x + x0 * c - s * y0, y + s * x0 + c * y0), 0).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x1 * c - s * y0, y + s * x1 + c * y0), 1).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x0 * c - s * y1, y + s * x0 + c * y1), 2).end()
|
|
||||||
lambda(vertex().pushVec2f(x + x1 * c - s * y1, y + s * x1 + c * y1), 3).end()
|
|
||||||
|
|
||||||
return this as This
|
|
||||||
}
|
|
||||||
|
|
||||||
fun quad(aabb: AABB, lambda: VertexTransformer = emptyTransform): This {
|
|
||||||
return quad(
|
|
||||||
aabb.mins.x.toFloat(),
|
|
||||||
aabb.mins.y.toFloat(),
|
|
||||||
aabb.maxs.x.toFloat(),
|
|
||||||
aabb.maxs.y.toFloat(),
|
|
||||||
lambda
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun quadZ(
|
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
z: Float,
|
|
||||||
lambda: VertexTransformer = emptyTransform
|
|
||||||
): This {
|
|
||||||
check(type.elements == 4) { "Currently building $type" }
|
|
||||||
|
|
||||||
lambda(vertex().pushVec3f(x0, y0, z), 0).end()
|
|
||||||
lambda(vertex().pushVec3f(x1, y0, z), 1).end()
|
|
||||||
lambda(vertex().pushVec3f(x0, y1, z), 2).end()
|
|
||||||
lambda(vertex().pushVec3f(x1, y1, z), 3).end()
|
|
||||||
|
|
||||||
return this as This
|
|
||||||
}
|
|
||||||
|
|
||||||
fun quadRotatedZ(
|
|
||||||
x0: Float,
|
|
||||||
y0: Float,
|
|
||||||
x1: Float,
|
|
||||||
y1: Float,
|
|
||||||
z: Float,
|
|
||||||
x: Float,
|
|
||||||
y: Float,
|
|
||||||
angle: Double,
|
|
||||||
lambda: VertexTransformer = emptyTransform
|
|
||||||
): This {
|
|
||||||
check(type.elements == 4) { "Currently building $type" }
|
|
||||||
|
|
||||||
val s = sin(angle).toFloat()
|
|
||||||
val c = cos(angle).toFloat()
|
|
||||||
|
|
||||||
lambda(vertex().pushVec3f(x + x0 * c - s * y0, y + s * x0 + c * y0, z), 0).end()
|
|
||||||
lambda(vertex().pushVec3f(x + x1 * c - s * y0, y + s * x1 + c * y0, z), 1).end()
|
|
||||||
lambda(vertex().pushVec3f(x + x0 * c - s * y1, y + s * x0 + c * y1, z), 2).end()
|
|
||||||
lambda(vertex().pushVec3f(x + x1 * c - s * y1, y + s * x1 + c * y1, z), 3).end()
|
|
||||||
|
|
||||||
return this as This
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IVertex<This : IVertex<This, VertexBuilderType>, VertexBuilderType> {
|
|
||||||
fun checkValid()
|
|
||||||
fun expect(name: String): This
|
|
||||||
fun expect(type: GLType): This
|
|
||||||
fun pushVec3f(x: Float, y: Float, z: Float): This
|
|
||||||
fun pushVec2f(x: Float, y: Float): This
|
|
||||||
fun push(value: Float): This
|
|
||||||
fun end(): VertexBuilderType
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias VertexTransformer = (IVertex<*, *>, Int) -> IVertex<*, *>
|
|
||||||
private val emptyTransform: VertexTransformer = { it, _ -> it }
|
|
||||||
private val EMPTY_BUFFER = ByteBuffer.allocateDirect(0)
|
|
||||||
|
|
||||||
fun VertexTransformer.before(other: VertexTransformer): VertexTransformer {
|
|
||||||
return { a, b ->
|
|
||||||
other.invoke(a, b)
|
|
||||||
this.invoke(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun VertexTransformer.after(other: VertexTransformer): VertexTransformer {
|
|
||||||
return { a, b ->
|
|
||||||
this.invoke(a, b)
|
|
||||||
other.invoke(a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object VertexTransformers {
|
|
||||||
fun uv(u0: Float,
|
|
||||||
v0: Float,
|
|
||||||
u1: Float,
|
|
||||||
v1: Float,
|
|
||||||
lambda: VertexTransformer = emptyTransform
|
|
||||||
): VertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
when (index) {
|
|
||||||
0 -> it.pushVec2f(u0, v0)
|
|
||||||
1 -> it.pushVec2f(u1, v0)
|
|
||||||
2 -> it.pushVec2f(u0, v1)
|
|
||||||
3 -> it.pushVec2f(u1, v1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@transformer lambda(it, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uv(): VertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
when (index) {
|
|
||||||
0 -> it.pushVec2f(0f, 0f)
|
|
||||||
1 -> it.pushVec2f(1f, 0f)
|
|
||||||
2 -> it.pushVec2f(0f, 1f)
|
|
||||||
3 -> it.pushVec2f(1f, 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@transformer it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uv(lambda: VertexTransformer): VertexTransformer {
|
|
||||||
return transformer@{ it, index ->
|
|
||||||
when (index) {
|
|
||||||
0 -> it.pushVec2f(0f, 0f)
|
|
||||||
1 -> it.pushVec2f(1f, 0f)
|
|
||||||
2 -> it.pushVec2f(0f, 1f)
|
|
||||||
3 -> it.pushVec2f(1f, 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
return@transformer lambda(it, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DynamicVertexBuilder(val attributes: GLFlatAttributeList, override val type: VertexType) : IVertexBuilder<DynamicVertexBuilder, DynamicVertexBuilder.Vertex> {
|
|
||||||
private val verticies = ArrayList<Vertex>()
|
|
||||||
override val indexCount get() = (verticies.size / type.elements) * type.indicies.size
|
|
||||||
|
|
||||||
override fun begin(): DynamicVertexBuilder {
|
|
||||||
verticies.clear()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun vertex(): Vertex {
|
|
||||||
return Vertex()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun checkValid() {
|
|
||||||
for (vertex in verticies) {
|
|
||||||
vertex.checkValid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Загружает (копирует) данные в указанные буферы, с их текущей позиции
|
|
||||||
*/
|
|
||||||
fun upload(
|
|
||||||
vertexBuffer: ByteBuffer,
|
|
||||||
elementBuffer: ByteBuffer,
|
|
||||||
) {
|
|
||||||
check(verticies.size % type.elements == 0) { "Not fully built (expected ${type.elements} verticies to be present for each element, last element has only ${verticies.size % type.elements})" }
|
|
||||||
|
|
||||||
require(vertexBuffer.order() == ByteOrder.nativeOrder()) { "Byte order of $vertexBuffer does not match native order" }
|
|
||||||
require(elementBuffer.order() == ByteOrder.nativeOrder()) { "Byte order of $elementBuffer does not match native order" }
|
|
||||||
|
|
||||||
checkValid()
|
|
||||||
|
|
||||||
for (vertex in verticies) {
|
|
||||||
vertex.upload(vertexBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
var offsetVertex = 0
|
|
||||||
|
|
||||||
for (i in 0 until verticies.size / type.elements) {
|
|
||||||
for (i2 in type.indicies.indices) {
|
|
||||||
elementBuffer.putInt(type.indicies[i2] + offsetVertex)
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetVertex += type.elements
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Загружает буфер в указанные VBO и EBO
|
|
||||||
*
|
|
||||||
* операция создаёт мусор вне кучи и довольно медленная
|
|
||||||
*/
|
|
||||||
override fun upload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int) {
|
|
||||||
require(vbo.isArray) { "$vbo is not an array" }
|
|
||||||
require(ebo.isElementArray) { "$vbo is not an element array" }
|
|
||||||
|
|
||||||
check(verticies.size % type.elements == 0) { "Not fully built (expected ${type.elements} verticies to be present for each element, last element has only ${verticies.size % type.elements})" }
|
|
||||||
|
|
||||||
checkValid()
|
|
||||||
|
|
||||||
if (verticies.size == 0) {
|
|
||||||
vbo.bufferData(EMPTY_BUFFER, drawType)
|
|
||||||
ebo.bufferData(EMPTY_BUFFER, drawType)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val vertexBuffer = ByteBuffer.allocateDirect(verticies.size * attributes.stride)
|
|
||||||
vertexBuffer.order(ByteOrder.nativeOrder())
|
|
||||||
|
|
||||||
val elementBuffer = ByteBuffer.allocateDirect((verticies.size / type.elements) * type.indicies.size * 4)
|
|
||||||
elementBuffer.order(ByteOrder.nativeOrder())
|
|
||||||
|
|
||||||
upload(vertexBuffer, elementBuffer)
|
|
||||||
|
|
||||||
check(vertexBuffer.position() == vertexBuffer.capacity()) { "Vertex Buffer is not fully filled (position: ${vertexBuffer.position()}; capacity: ${vertexBuffer.capacity()})" }
|
|
||||||
check(elementBuffer.position() == elementBuffer.capacity()) { "Element Buffer is not fully filled (position: ${elementBuffer.position()}; capacity: ${elementBuffer.capacity()})" }
|
|
||||||
|
|
||||||
vertexBuffer.position(0)
|
|
||||||
elementBuffer.position(0)
|
|
||||||
|
|
||||||
vbo.bufferData(vertexBuffer, drawType)
|
|
||||||
ebo.bufferData(elementBuffer, drawType)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class Vertex : IVertex<Vertex, DynamicVertexBuilder> {
|
|
||||||
init {
|
|
||||||
verticies.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val store = arrayOfNulls<Any>(attributes.size)
|
|
||||||
private var index = 0
|
|
||||||
|
|
||||||
fun upload(bytes: ByteBuffer) {
|
|
||||||
for (element in store) {
|
|
||||||
when (element) {
|
|
||||||
is FloatArray -> for (i in element) bytes.putFloat(i)
|
|
||||||
is IntArray -> for (i in element) bytes.putInt(i)
|
|
||||||
is ByteArray -> for (i in element) bytes.put(i)
|
|
||||||
is DoubleArray -> for (i in element) bytes.putDouble(i)
|
|
||||||
is Float -> bytes.putFloat(element)
|
|
||||||
else -> throw IllegalStateException("Unknown element $element")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Vertex(${store.map {
|
|
||||||
return@map when (it) {
|
|
||||||
is FloatArray -> it.joinToString(", ")
|
|
||||||
is IntArray -> it.joinToString(", ")
|
|
||||||
is ByteArray -> it.joinToString(", ")
|
|
||||||
is DoubleArray -> it.joinToString(", ")
|
|
||||||
is Float -> it
|
|
||||||
else -> "null"
|
|
||||||
} }.joinToString("; ")})"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun expect(name: String): Vertex {
|
|
||||||
if (index >= attributes.size) {
|
|
||||||
throw IllegalStateException("Reached end of attribute list early, expected $name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes[index].name != name) {
|
|
||||||
throw IllegalStateException("Expected $name, got ${attributes[index].name}[${attributes[index].glType}] (at position $index)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun expect(type: GLType): Vertex {
|
|
||||||
if (index >= attributes.size) {
|
|
||||||
throw IllegalStateException("Reached end of attribute list early, expected type $type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes[index].glType != type) {
|
|
||||||
throw IllegalStateException("Expected $type, got ${attributes[index].name}[${attributes[index].glType}] (at position $index)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushVec3f(x: Float, y: Float, z: Float): Vertex {
|
|
||||||
expect(GLType.VEC3F)
|
|
||||||
store[index++] = floatArrayOf(x, y, z)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushVec2f(x: Float, y: Float): Vertex {
|
|
||||||
expect(GLType.VEC2F)
|
|
||||||
store[index++] = floatArrayOf(x, y)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun push(value: Float): Vertex {
|
|
||||||
expect(GLType.FLOAT)
|
|
||||||
store[index++] = value
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun checkValid() {
|
|
||||||
for (elem in store.indices) {
|
|
||||||
if (store[elem] == null) {
|
|
||||||
throw IllegalStateException("Vertex element at position $elem is null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun end(): DynamicVertexBuilder {
|
|
||||||
checkValid()
|
|
||||||
return this@DynamicVertexBuilder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* "Поточная" версия [DynamicVertexBuilder], ориентированная на скорость работы и имеющая фиксированный размер буфера
|
|
||||||
*
|
|
||||||
* Главные отличия:
|
|
||||||
* * Данный объект не желательно создавать каждый раз когда надо отрисовать какое либо количество геометрии, а использовать уже существующий, который
|
|
||||||
* удовлетворяет требованиям (формат вершин, и их потенциально максимальное количество)
|
|
||||||
* * Максимальное количество vertex'ов фиксированно и равняется [maxElements] * [VertexType.elements]
|
|
||||||
* * Имеет два встроенных DirectByteBuffer и НЕ позволяет загружать данные в другие ByteBuffer, только во внутренние ByteBuffer и только в VBO; EBO
|
|
||||||
*/
|
|
||||||
class StreamVertexBuilder(
|
|
||||||
val attributes: GLFlatAttributeList,
|
|
||||||
override val type: VertexType,
|
|
||||||
val maxElements: Int,
|
|
||||||
) : IVertexBuilder<StreamVertexBuilder, StreamVertexBuilder.Vertex> {
|
|
||||||
val maxVertexNum = maxElements * type.elements
|
|
||||||
var nextVertex = 0
|
|
||||||
private set
|
|
||||||
|
|
||||||
override val indexCount get() = (nextVertex / type.elements) * type.indicies.size
|
|
||||||
val maxIndexCount = maxElements * type.indicies.size
|
|
||||||
|
|
||||||
val elementIndexType = when (maxIndexCount) {
|
|
||||||
// api performance issue 102: glDrawElements uses element index type 'GL_UNSIGNED_BYTE' that is not optimal for the current hardware configuration; consider using 'GL_UNSIGNED_SHORT' instead
|
|
||||||
// in 0 .. 255 -> GL_UNSIGNED_BYTE
|
|
||||||
in 0 .. 65535 -> GL_UNSIGNED_SHORT
|
|
||||||
else -> GL_UNSIGNED_INT
|
|
||||||
}
|
|
||||||
|
|
||||||
private var head: Vertex? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Буфер для VBO достаточного размера
|
|
||||||
*/
|
|
||||||
private val vertexBuffer = ByteBuffer.allocateDirect(maxVertexNum * attributes.stride)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Буфер для EBO достаточного размера
|
|
||||||
*/
|
|
||||||
private val elementBuffer = ByteBuffer.allocateDirect(maxElements * type.indicies.size * 4)
|
|
||||||
|
|
||||||
init {
|
|
||||||
vertexBuffer.order(ByteOrder.nativeOrder())
|
|
||||||
elementBuffer.order(ByteOrder.nativeOrder())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeElementIndex(value: Int) {
|
|
||||||
when (elementIndexType) {
|
|
||||||
GL_UNSIGNED_BYTE -> elementBuffer.put(value.toByte())
|
|
||||||
GL_UNSIGNED_SHORT -> elementBuffer.putShort(value.toShort())
|
|
||||||
else -> elementBuffer.putInt(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var offsetElementIndex = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Устанавливает метку этого билдера в ноль.
|
|
||||||
*
|
|
||||||
* Не обнуляет память буферов!
|
|
||||||
*/
|
|
||||||
override fun begin(): StreamVertexBuilder {
|
|
||||||
nextVertex = 0
|
|
||||||
offsetElementIndex = 0
|
|
||||||
head = null
|
|
||||||
vertexBuffer.position(0)
|
|
||||||
elementBuffer.position(0)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun vertex(): Vertex {
|
|
||||||
return Vertex()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun upload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int) {
|
|
||||||
require(vbo.isArray) { "$vbo is not an array" }
|
|
||||||
require(ebo.isElementArray) { "$vbo is not an element array" }
|
|
||||||
|
|
||||||
if (nextVertex == 0) {
|
|
||||||
vbo.bufferData(EMPTY_BUFFER, drawType)
|
|
||||||
ebo.bufferData(EMPTY_BUFFER, drawType)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checkValid()
|
|
||||||
|
|
||||||
val a = vertexBuffer.position().toLong()
|
|
||||||
val b = elementBuffer.position().toLong()
|
|
||||||
|
|
||||||
vertexBuffer.position(0)
|
|
||||||
elementBuffer.position(0)
|
|
||||||
|
|
||||||
vbo.bufferData(vertexBuffer, drawType, length = a)
|
|
||||||
ebo.bufferData(elementBuffer, drawType, length = b)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun checkValid() {
|
|
||||||
var vertex = head
|
|
||||||
|
|
||||||
while (vertex != null) {
|
|
||||||
vertex.checkValid()
|
|
||||||
vertex = vertex.previous
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class Vertex : IVertex<Vertex, StreamVertexBuilder> {
|
|
||||||
private val vertexIndex = nextVertex++
|
|
||||||
val previous = head
|
|
||||||
private var bufferPosition = vertexIndex * attributes.stride
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (vertexIndex >= maxVertexNum) {
|
|
||||||
throw IndexOutOfBoundsException("Tried to push new vertex $vertexIndex, when already above limit of $maxVertexNum!")
|
|
||||||
}
|
|
||||||
|
|
||||||
head = this
|
|
||||||
|
|
||||||
for (i2 in type.indicies.indices) {
|
|
||||||
writeElementIndex(type.indicies[i2] + offsetElementIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetElementIndex += type.elements
|
|
||||||
}
|
|
||||||
|
|
||||||
private var index = 0
|
|
||||||
|
|
||||||
override fun checkValid() {
|
|
||||||
check(index == attributes.size) { "Vertex $vertexIndex is not fully filled (only $index attributes provided, ${attributes.size} required)" }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun expect(name: String): Vertex {
|
|
||||||
if (index >= attributes.size) {
|
|
||||||
throw IllegalStateException("Reached end of attribute list early, expected $name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes[index].name != name) {
|
|
||||||
throw IllegalStateException("Expected $name, got ${attributes[index].name}[${attributes[index].glType}] (at position $index)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun expect(type: GLType): Vertex {
|
|
||||||
if (index >= attributes.size) {
|
|
||||||
throw IllegalStateException("Reached end of attribute list early, expected type $type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributes[index].glType != type) {
|
|
||||||
throw IllegalStateException("Expected $type, got ${attributes[index].name}[${attributes[index].glType}] (at position $index)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushVec3f(x: Float, y: Float, z: Float): Vertex {
|
|
||||||
expect(GLType.VEC3F)
|
|
||||||
vertexBuffer.position(bufferPosition)
|
|
||||||
vertexBuffer.putFloat(x)
|
|
||||||
vertexBuffer.putFloat(y)
|
|
||||||
vertexBuffer.putFloat(z)
|
|
||||||
index++
|
|
||||||
bufferPosition += 12
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushVec2f(x: Float, y: Float): Vertex {
|
|
||||||
expect(GLType.VEC2F)
|
|
||||||
vertexBuffer.position(bufferPosition)
|
|
||||||
vertexBuffer.putFloat(x)
|
|
||||||
vertexBuffer.putFloat(y)
|
|
||||||
index++
|
|
||||||
bufferPosition += 8
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun push(value: Float): Vertex {
|
|
||||||
expect(GLType.FLOAT)
|
|
||||||
vertexBuffer.position(bufferPosition)
|
|
||||||
vertexBuffer.putFloat(value)
|
|
||||||
index++
|
|
||||||
bufferPosition += 4
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun end(): StreamVertexBuilder {
|
|
||||||
check(index == attributes.size) { "Vertex $vertexIndex is not fully filled (only $index attributes provided, ${attributes.size} required)" }
|
|
||||||
return this@StreamVertexBuilder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatefulStreamVertexBuilder(
|
|
||||||
val state: GLStateTracker,
|
|
||||||
val builder: StreamVertexBuilder
|
|
||||||
) : Closeable, IVertexBuilder<StreamVertexBuilder, StreamVertexBuilder.Vertex> by builder {
|
|
||||||
private val vao = state.newVAO()
|
|
||||||
private val vbo = state.newVBO()
|
|
||||||
private val ebo = state.newEBO()
|
|
||||||
|
|
||||||
init {
|
|
||||||
vao.bind()
|
|
||||||
vbo.bind()
|
|
||||||
ebo.bind()
|
|
||||||
|
|
||||||
builder.attributes.apply(vao, true)
|
|
||||||
|
|
||||||
vao.unbind()
|
|
||||||
vbo.unbind()
|
|
||||||
ebo.unbind()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun upload(drawType: Int = GL_DYNAMIC_DRAW) {
|
|
||||||
builder.upload(vbo, ebo, drawType)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bind() = vao.bind()
|
|
||||||
fun unbind() = vao.unbind()
|
|
||||||
|
|
||||||
fun draw(primitives: Int = GL_TRIANGLES) {
|
|
||||||
bind()
|
|
||||||
glDrawElements(primitives, builder.indexCount, builder.elementIndexType, 0L)
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
vao.close()
|
|
||||||
vbo.close()
|
|
||||||
ebo.close()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,262 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL46
|
||||||
|
import org.lwjgl.opengl.GL46.GL_UNSIGNED_INT
|
||||||
|
import org.lwjgl.opengl.GL46.GL_UNSIGNED_SHORT
|
||||||
|
import org.lwjgl.opengl.GL46.GL_UNSIGNED_BYTE
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLVertexBufferObject
|
||||||
|
import ru.dbotthepony.kstarbound.util.writeLEFloat
|
||||||
|
import ru.dbotthepony.kstarbound.util.writeLEInt
|
||||||
|
import ru.dbotthepony.kstarbound.util.writeLEShort
|
||||||
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
|
import java.io.OutputStream
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
private fun put(type: Int, memory: OutputStream, value: Int) {
|
||||||
|
when (type) {
|
||||||
|
GL_UNSIGNED_SHORT -> memory.writeLEShort(value)
|
||||||
|
GL_UNSIGNED_BYTE -> memory.write(value)
|
||||||
|
else -> memory.writeLEInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс для построения геометрии для загрузки в память видеокарты.
|
||||||
|
*/
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
abstract class AbstractVertexBuilder<T : AbstractVertexBuilder<T>>(
|
||||||
|
val attributes: GLAttributeList,
|
||||||
|
val type: VertexType,
|
||||||
|
) {
|
||||||
|
protected abstract val vertexMemory: OutputStream
|
||||||
|
protected abstract val elementMemory: OutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [GL_UNSIGNED_BYTE], [GL_UNSIGNED_SHORT] или [GL_UNSIGNED_INT]
|
||||||
|
*
|
||||||
|
* Обязана быть статичным числом.
|
||||||
|
*
|
||||||
|
* Если нет, и его надо изменить, то это можно только делать внутри [ensureIndexCapacity].
|
||||||
|
*
|
||||||
|
* Подкласс обязан самостоятельно изменить тип существующих элементов внутри [elementMemory]
|
||||||
|
*/
|
||||||
|
abstract val elementIndexType: Int
|
||||||
|
|
||||||
|
protected abstract fun ensureIndexCapacity()
|
||||||
|
|
||||||
|
protected abstract fun resetMemory()
|
||||||
|
|
||||||
|
fun begin() {
|
||||||
|
inVertex = false
|
||||||
|
attributeIndex = 0
|
||||||
|
elementIndexOffset = 0
|
||||||
|
vertexCount = 0
|
||||||
|
indexCount = 0
|
||||||
|
resetMemory()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var inVertex = false
|
||||||
|
private var attributeIndex = 0
|
||||||
|
protected var elementIndexOffset = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var vertexCount = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
var indexCount = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
protected fun checkBounds() {
|
||||||
|
if (attributeIndex >= attributes.size) {
|
||||||
|
throw IndexOutOfBoundsException("Tried to add new attribute when already added all attributes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expect(type: GLType) {
|
||||||
|
checkBounds()
|
||||||
|
|
||||||
|
if (attributes[attributeIndex].glType != type) {
|
||||||
|
throw IllegalStateException("Expected attribute type $type, got ${attributes[attributeIndex].name}[${attributes[attributeIndex].glType}] (at position $attributeIndex)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expect(name: String) {
|
||||||
|
checkBounds()
|
||||||
|
|
||||||
|
if (attributes[attributeIndex].name != name) {
|
||||||
|
throw IllegalStateException("Expected attribute name $name, got ${attributes[attributeIndex].name}[${attributes[attributeIndex].glType}] (at position $attributeIndex)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun doUpload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int)
|
||||||
|
|
||||||
|
fun upload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int = GL46.GL_DYNAMIC_DRAW) {
|
||||||
|
require(vbo.isArray) { "$vbo is not an array" }
|
||||||
|
require(ebo.isElementArray) { "$vbo is not an element array" }
|
||||||
|
|
||||||
|
end()
|
||||||
|
|
||||||
|
check(elementVertices == 0) { "Not fully built vertex element ($type requires ${type.elements} vertex points to be present, yet last strip has only $elementVertices elements)" }
|
||||||
|
|
||||||
|
doUpload(vbo, ebo, drawType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var elementVertices = 0
|
||||||
|
|
||||||
|
fun end() {
|
||||||
|
if (inVertex) {
|
||||||
|
inVertex = false
|
||||||
|
|
||||||
|
if (attributeIndex != attributes.size) {
|
||||||
|
throw IllegalStateException("Unfinished vertex, we are at $attributeIndex, while we have ${attributes.size} attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
vertexCount++
|
||||||
|
elementVertices++
|
||||||
|
|
||||||
|
if (elementVertices == type.elements) {
|
||||||
|
ensureIndexCapacity()
|
||||||
|
|
||||||
|
elementVertices = 0
|
||||||
|
val elementMemory = elementMemory
|
||||||
|
val elementIndexType = elementIndexType
|
||||||
|
|
||||||
|
for (index in type.indicies) {
|
||||||
|
put(elementIndexType, elementMemory, index + elementIndexOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
elementIndexOffset += type.elements
|
||||||
|
indexCount += type.indicies.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun vertex(): T {
|
||||||
|
end()
|
||||||
|
inVertex = true
|
||||||
|
attributeIndex = 0
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pushVec3f(x: Float, y: Float, z: Float): T {
|
||||||
|
expect(GLType.VEC3F)
|
||||||
|
val memory = vertexMemory
|
||||||
|
memory.writeLEFloat(x)
|
||||||
|
memory.writeLEFloat(y)
|
||||||
|
memory.writeLEFloat(z)
|
||||||
|
attributeIndex++
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pushVec2f(x: Float, y: Float): T {
|
||||||
|
expect(GLType.VEC2F)
|
||||||
|
val memory = vertexMemory
|
||||||
|
memory.writeLEFloat(x)
|
||||||
|
memory.writeLEFloat(y)
|
||||||
|
attributeIndex++
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(value: Float): T {
|
||||||
|
expect(GLType.FLOAT)
|
||||||
|
vertexMemory.writeLEFloat(value)
|
||||||
|
attributeIndex++
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
// Помощники
|
||||||
|
fun quad(
|
||||||
|
x0: Float,
|
||||||
|
y0: Float,
|
||||||
|
x1: Float,
|
||||||
|
y1: Float,
|
||||||
|
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
||||||
|
): T {
|
||||||
|
check(type.elements == 4) { "Currently building $type" }
|
||||||
|
|
||||||
|
lambda(vertex().pushVec2f(x0, y0), 0).end()
|
||||||
|
lambda(vertex().pushVec2f(x1, y0), 1).end()
|
||||||
|
lambda(vertex().pushVec2f(x0, y1), 2).end()
|
||||||
|
lambda(vertex().pushVec2f(x1, y1), 3).end()
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun quadRotated(
|
||||||
|
x0: Float,
|
||||||
|
y0: Float,
|
||||||
|
x1: Float,
|
||||||
|
y1: Float,
|
||||||
|
x: Float,
|
||||||
|
y: Float,
|
||||||
|
angle: Double,
|
||||||
|
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
||||||
|
): T {
|
||||||
|
check(type.elements == 4) { "Currently building $type" }
|
||||||
|
|
||||||
|
val s = sin(angle).toFloat()
|
||||||
|
val c = cos(angle).toFloat()
|
||||||
|
|
||||||
|
lambda(vertex().pushVec2f(x + x0 * c - s * y0, y + s * x0 + c * y0), 0).end()
|
||||||
|
lambda(vertex().pushVec2f(x + x1 * c - s * y0, y + s * x1 + c * y0), 1).end()
|
||||||
|
lambda(vertex().pushVec2f(x + x0 * c - s * y1, y + s * x0 + c * y1), 2).end()
|
||||||
|
lambda(vertex().pushVec2f(x + x1 * c - s * y1, y + s * x1 + c * y1), 3).end()
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): T {
|
||||||
|
return quad(
|
||||||
|
aabb.mins.x.toFloat(),
|
||||||
|
aabb.mins.y.toFloat(),
|
||||||
|
aabb.maxs.x.toFloat(),
|
||||||
|
aabb.maxs.y.toFloat(),
|
||||||
|
lambda
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun quadZ(
|
||||||
|
x0: Float,
|
||||||
|
y0: Float,
|
||||||
|
x1: Float,
|
||||||
|
y1: Float,
|
||||||
|
z: Float,
|
||||||
|
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
||||||
|
): T {
|
||||||
|
check(type.elements == 4) { "Currently building $type" }
|
||||||
|
|
||||||
|
lambda(vertex().pushVec3f(x0, y0, z), 0).end()
|
||||||
|
lambda(vertex().pushVec3f(x1, y0, z), 1).end()
|
||||||
|
lambda(vertex().pushVec3f(x0, y1, z), 2).end()
|
||||||
|
lambda(vertex().pushVec3f(x1, y1, z), 3).end()
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun quadRotatedZ(
|
||||||
|
x0: Float,
|
||||||
|
y0: Float,
|
||||||
|
x1: Float,
|
||||||
|
y1: Float,
|
||||||
|
z: Float,
|
||||||
|
x: Float,
|
||||||
|
y: Float,
|
||||||
|
angle: Double,
|
||||||
|
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
||||||
|
): T {
|
||||||
|
check(type.elements == 4) { "Currently building $type" }
|
||||||
|
|
||||||
|
val s = sin(angle).toFloat()
|
||||||
|
val c = cos(angle).toFloat()
|
||||||
|
|
||||||
|
lambda(vertex().pushVec3f(x + x0 * c - s * y0, y + s * x0 + c * y0, z), 0).end()
|
||||||
|
lambda(vertex().pushVec3f(x + x1 * c - s * y0, y + s * x1 + c * y0, z), 1).end()
|
||||||
|
lambda(vertex().pushVec3f(x + x0 * c - s * y1, y + s * x0 + c * y1, z), 2).end()
|
||||||
|
lambda(vertex().pushVec3f(x + x1 * c - s * y1, y + s * x1 + c * y1, z), 3).end()
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL46
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLVertexBufferObject
|
||||||
|
import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream
|
||||||
|
|
||||||
|
open class DirectVertexBuilder(
|
||||||
|
attributes: GLAttributeList,
|
||||||
|
type: VertexType,
|
||||||
|
val maxElements: Int,
|
||||||
|
) : AbstractVertexBuilder<HeapVertexBuilder>(attributes, type) {
|
||||||
|
val maxIndexCount = maxElements * type.indicies.size
|
||||||
|
val maxVertexCount = maxElements * type.elements
|
||||||
|
|
||||||
|
override val vertexMemory = ByteBufferOutputStream.directLE(maxVertexCount)
|
||||||
|
override val elementMemory = ByteBufferOutputStream.directLE(maxIndexCount)
|
||||||
|
|
||||||
|
override val elementIndexType: Int = when (maxIndexCount) {
|
||||||
|
// api performance issue 102: glDrawElements uses element index type 'GL_UNSIGNED_BYTE' that is not optimal for the current hardware configuration; consider using 'GL_UNSIGNED_SHORT' instead
|
||||||
|
// in 0 .. 255 -> GL_UNSIGNED_BYTE
|
||||||
|
in 0 .. 65535 -> GL46.GL_UNSIGNED_SHORT
|
||||||
|
else -> GL46.GL_UNSIGNED_INT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun ensureIndexCapacity() {
|
||||||
|
if (vertexCount >= maxVertexCount) {
|
||||||
|
throw IndexOutOfBoundsException("Vertex count overflow (can hold max of $maxElements elements, that's $maxVertexCount vertexes)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetMemory() {
|
||||||
|
vertexMemory.position = 0
|
||||||
|
elementMemory.position = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doUpload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int) {
|
||||||
|
val vertexPos = vertexMemory.position
|
||||||
|
val elementPos = elementMemory.position
|
||||||
|
|
||||||
|
vertexMemory.position = 0
|
||||||
|
elementMemory.position = 0
|
||||||
|
|
||||||
|
vbo.bufferData(vertexMemory.buffer, drawType, length = vertexPos.toLong())
|
||||||
|
ebo.bufferData(elementMemory.buffer, drawType, length = elementPos.toLong())
|
||||||
|
|
||||||
|
vertexMemory.position = vertexPos
|
||||||
|
elementMemory.position = elementPos
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
|
||||||
|
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||||
|
import org.lwjgl.opengl.GL11.GL_UNSIGNED_INT
|
||||||
|
import org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLVertexBufferObject
|
||||||
|
import ru.dbotthepony.kstarbound.util.readLEShort
|
||||||
|
import ru.dbotthepony.kstarbound.util.writeLEInt
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertex Builder который хранит все данные на куче, при [upload] создаётся [ByteBuffer] вне кучи,
|
||||||
|
* в него записываются данные из кучи, данные загружаются на видеокарту.
|
||||||
|
*
|
||||||
|
* Полезен в случаях, когда размер данных для загрузки заранее не известен.
|
||||||
|
*/
|
||||||
|
class HeapVertexBuilder(
|
||||||
|
attributes: GLAttributeList,
|
||||||
|
type: VertexType,
|
||||||
|
) : AbstractVertexBuilder<HeapVertexBuilder>(attributes, type) {
|
||||||
|
override val vertexMemory = FastByteArrayOutputStream()
|
||||||
|
override val elementMemory = FastByteArrayOutputStream()
|
||||||
|
|
||||||
|
override var elementIndexType: Int = GL_UNSIGNED_SHORT
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun ensureIndexCapacity() {
|
||||||
|
if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indicies.size >= 30000) {
|
||||||
|
val backing = elementMemory.array
|
||||||
|
val copy = FastByteArrayInputStream(ByteArray(elementMemory.length) { backing[it] })
|
||||||
|
|
||||||
|
elementIndexType = GL_UNSIGNED_INT
|
||||||
|
val elementMemory = elementMemory
|
||||||
|
elementMemory.reset()
|
||||||
|
|
||||||
|
for (i in 0 until (copy.length / 2)) {
|
||||||
|
elementMemory.writeLEInt(copy.readLEShort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetMemory() {
|
||||||
|
vertexMemory.reset()
|
||||||
|
elementMemory.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doUpload(vbo: GLVertexBufferObject, ebo: GLVertexBufferObject, drawType: Int) {
|
||||||
|
val vboMemory = ByteBuffer.allocateDirect(vertexMemory.length)
|
||||||
|
vboMemory.put(vertexMemory.array, 0, vertexMemory.length)
|
||||||
|
|
||||||
|
val eboMemory = ByteBuffer.allocateDirect(elementMemory.length)
|
||||||
|
eboMemory.put(elementMemory.array, 0, elementMemory.length)
|
||||||
|
|
||||||
|
vboMemory.position(0)
|
||||||
|
eboMemory.position(0)
|
||||||
|
|
||||||
|
vbo.bufferData(vboMemory, drawType)
|
||||||
|
ebo.bufferData(eboMemory, drawType)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
typealias QuadVertexTransformer = (AbstractVertexBuilder<*>, Int) -> AbstractVertexBuilder<*>
|
||||||
|
|
||||||
|
val EMPTY_VERTEX_TRANSFORM: QuadVertexTransformer = { it, _ -> it }
|
||||||
|
|
||||||
|
fun QuadVertexTransformer.before(other: QuadVertexTransformer): QuadVertexTransformer {
|
||||||
|
return { a, b ->
|
||||||
|
other.invoke(a, b)
|
||||||
|
this.invoke(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun QuadVertexTransformer.after(other: QuadVertexTransformer): QuadVertexTransformer {
|
||||||
|
return { a, b ->
|
||||||
|
this.invoke(a, b)
|
||||||
|
other.invoke(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object QuadTransformers {
|
||||||
|
fun uv(u0: Float,
|
||||||
|
v0: Float,
|
||||||
|
u1: Float,
|
||||||
|
v1: Float,
|
||||||
|
lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM
|
||||||
|
): QuadVertexTransformer {
|
||||||
|
return transformer@{ it, index ->
|
||||||
|
when (index) {
|
||||||
|
0 -> it.pushVec2f(u0, v0)
|
||||||
|
1 -> it.pushVec2f(u1, v0)
|
||||||
|
2 -> it.pushVec2f(u0, v1)
|
||||||
|
3 -> it.pushVec2f(u1, v1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@transformer lambda(it, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uv(): QuadVertexTransformer {
|
||||||
|
return transformer@{ it, index ->
|
||||||
|
when (index) {
|
||||||
|
0 -> it.pushVec2f(0f, 0f)
|
||||||
|
1 -> it.pushVec2f(1f, 0f)
|
||||||
|
2 -> it.pushVec2f(0f, 1f)
|
||||||
|
3 -> it.pushVec2f(1f, 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@transformer it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uv(lambda: QuadVertexTransformer): QuadVertexTransformer {
|
||||||
|
return transformer@{ it, index ->
|
||||||
|
when (index) {
|
||||||
|
0 -> it.pushVec2f(0f, 0f)
|
||||||
|
1 -> it.pushVec2f(1f, 0f)
|
||||||
|
2 -> it.pushVec2f(0f, 1f)
|
||||||
|
3 -> it.pushVec2f(1f, 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@transformer lambda(it, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL46
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс-помощник для быстрого построения, загрузки и отрисовки геометрии
|
||||||
|
*/
|
||||||
|
class StreamVertexBuilder(
|
||||||
|
val state: GLStateTracker,
|
||||||
|
attributes: GLAttributeList,
|
||||||
|
type: VertexType,
|
||||||
|
maxElements: Int,
|
||||||
|
) : DirectVertexBuilder(attributes, type, maxElements), Closeable {
|
||||||
|
private val vao = state.newVAO()
|
||||||
|
private val vbo = state.newVBO()
|
||||||
|
private val ebo = state.newEBO()
|
||||||
|
|
||||||
|
init {
|
||||||
|
vao.bind()
|
||||||
|
vbo.bind()
|
||||||
|
ebo.bind()
|
||||||
|
|
||||||
|
attributes.apply(vao, true)
|
||||||
|
|
||||||
|
vao.unbind()
|
||||||
|
vbo.unbind()
|
||||||
|
ebo.unbind()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun upload(drawType: Int = GL46.GL_DYNAMIC_DRAW) {
|
||||||
|
upload(vbo, ebo, drawType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bind() = vao.bind()
|
||||||
|
fun unbind() = vao.unbind()
|
||||||
|
|
||||||
|
fun draw(primitives: Int = GL46.GL_TRIANGLES) {
|
||||||
|
bind()
|
||||||
|
GL46.glDrawElements(primitives, indexCount, elementIndexType, 0L)
|
||||||
|
checkForGLError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
vao.close()
|
||||||
|
vbo.close()
|
||||||
|
ebo.close()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
|
enum class VertexType(val elements: Int, val indicies: IntArray) {
|
||||||
|
LINES(2, intArrayOf(0, 1)),
|
||||||
|
TRIANGLES(3, intArrayOf(0, 1, 2)),
|
||||||
|
QUADS(4, intArrayOf(0, 1, 2, 1, 2, 3)),
|
||||||
|
QUADS_AS_LINES(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3)),
|
||||||
|
QUADS_AS_LINES_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
||||||
|
}
|
@ -20,32 +20,30 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
|
|||||||
override fun drawPolygon(vertices: List<Vector2d>, color: Color) {
|
override fun drawPolygon(vertices: List<Vector2d>, color: Color) {
|
||||||
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
val stateful = state.flat2DLines.statefulSmall
|
val builder = state.flat2DLines.small
|
||||||
val builder = stateful.builder
|
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
for (i in vertices.indices) {
|
for (i in vertices.indices) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.Vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||||
builder.Vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
stateful.upload()
|
builder.upload()
|
||||||
|
|
||||||
state.flatProgram.use()
|
state.flatProgram.use()
|
||||||
state.flatProgram.color.set(color)
|
state.flatProgram.color.set(color)
|
||||||
state.flatProgram.transform.set(state.matrixStack.last)
|
state.flatProgram.transform.set(state.matrixStack.last)
|
||||||
|
|
||||||
stateful.draw(GL_LINES)
|
builder.draw(GL_LINES)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawSolid(vertices: List<Vector2d>, color: Color) {
|
private fun drawSolid(vertices: List<Vector2d>, color: Color) {
|
||||||
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
val stateful = state.flat2DTriangles.statefulSmall
|
val builder = state.flat2DTriangles.small
|
||||||
val builder = stateful.builder
|
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
@ -54,18 +52,18 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
|
|||||||
for (i in 1 until vertices.size) {
|
for (i in 1 until vertices.size) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.Vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat())
|
builder.vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat())
|
||||||
builder.Vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||||
builder.Vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||||
}
|
}
|
||||||
|
|
||||||
stateful.upload()
|
builder.upload()
|
||||||
|
|
||||||
state.flatProgram.use()
|
state.flatProgram.use()
|
||||||
state.flatProgram.color.set(color)
|
state.flatProgram.color.set(color)
|
||||||
state.flatProgram.transform.set(state.matrixStack.last)
|
state.flatProgram.transform.set(state.matrixStack.last)
|
||||||
|
|
||||||
stateful.draw(GL_TRIANGLES)
|
builder.draw(GL_TRIANGLES)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun drawSolidPolygon(vertices: List<Vector2d>, color: Color) {
|
override fun drawSolidPolygon(vertices: List<Vector2d>, color: Color) {
|
||||||
|
@ -3,8 +3,8 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
import org.lwjgl.opengl.GL46.*
|
import org.lwjgl.opengl.GL46.*
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLShaderProgram
|
import ru.dbotthepony.kstarbound.client.gl.GLShaderProgram
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLVertexArrayObject
|
import ru.dbotthepony.kstarbound.client.gl.GLVertexArrayObject
|
||||||
import ru.dbotthepony.kstarbound.client.gl.DynamicVertexBuilder
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.AbstractVertexBuilder
|
||||||
import ru.dbotthepony.kvector.api.IFloatMatrix
|
import ru.dbotthepony.kvector.api.IFloatMatrix
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
|
|
||||||
@ -43,13 +43,15 @@ class ConfiguredStaticMesh(
|
|||||||
val programState: ConfiguredShaderProgram,
|
val programState: ConfiguredShaderProgram,
|
||||||
val indexCount: Int,
|
val indexCount: Int,
|
||||||
val vao: GLVertexArrayObject,
|
val vao: GLVertexArrayObject,
|
||||||
|
val elementIndexType: Int,
|
||||||
) : AutoCloseable {
|
) : AutoCloseable {
|
||||||
private var onClose = {}
|
private var onClose = {}
|
||||||
|
|
||||||
constructor(programState: ConfiguredShaderProgram, builder: DynamicVertexBuilder) : this(
|
constructor(programState: ConfiguredShaderProgram, builder: AbstractVertexBuilder<*>) : this(
|
||||||
programState,
|
programState,
|
||||||
builder.indexCount,
|
builder.indexCount,
|
||||||
programState.program.state.newVAO(),
|
programState.program.state.newVAO(),
|
||||||
|
builder.elementIndexType,
|
||||||
) {
|
) {
|
||||||
val vbo = programState.program.state.newVBO()
|
val vbo = programState.program.state.newVBO()
|
||||||
val ebo = programState.program.state.newEBO()
|
val ebo = programState.program.state.newEBO()
|
||||||
@ -80,7 +82,7 @@ class ConfiguredStaticMesh(
|
|||||||
}
|
}
|
||||||
|
|
||||||
vao.bind()
|
vao.bind()
|
||||||
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0L)
|
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
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.PIXELS_IN_STARBOUND_UNIT
|
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.gl.VertexTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
@ -70,8 +69,7 @@ open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk:
|
|||||||
|
|
||||||
animator.advance()
|
animator.advance()
|
||||||
|
|
||||||
val stateful = state.flat2DTexturedQuads.statefulSmall
|
val builder = state.flat2DTexturedQuads.small
|
||||||
val builder = stateful.builder
|
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
@ -81,9 +79,9 @@ open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk:
|
|||||||
val width = (animator.frameObj.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
val width = (animator.frameObj.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
val height = (animator.frameObj.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
val height = (animator.frameObj.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
|
|
||||||
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, VertexTransformers.uv(u0, v0, u1, v1))
|
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, QuadTransformers.uv(u0, v0, u1, v1))
|
||||||
|
|
||||||
stateful.upload()
|
builder.upload()
|
||||||
stateful.draw()
|
builder.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import org.lwjgl.opengl.GL46.*
|
|||||||
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
||||||
import ru.dbotthepony.kstarbound.client.gl.*
|
import ru.dbotthepony.kstarbound.client.gl.*
|
||||||
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
import ru.dbotthepony.kstarbound.client.freetype.struct.FT_Pixel_Mode
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.HeapVertexBuilder
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexType
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
|
|
||||||
@ -259,6 +263,8 @@ class Font(
|
|||||||
|
|
||||||
private val indexCount: Int
|
private val indexCount: Int
|
||||||
|
|
||||||
|
private val elementIndexType: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
face.loadChar(char, LoadFlag.RENDER)
|
face.loadChar(char, LoadFlag.RENDER)
|
||||||
val glyph = face.nativeMemory.glyph ?: throw IllegalStateException("Unable to load glyph data for $char (code ${char.code})")
|
val glyph = face.nativeMemory.glyph ?: throw IllegalStateException("Unable to load glyph data for $char (code ${char.code})")
|
||||||
@ -301,19 +307,22 @@ class Font(
|
|||||||
ebo.bind()
|
ebo.bind()
|
||||||
vbo.bind()
|
vbo.bind()
|
||||||
|
|
||||||
val builder = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_2D_TEXTURE, VertexType.QUADS)
|
val builder = HeapVertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, VertexType.QUADS)
|
||||||
|
|
||||||
builder.quad(0f, 0f, width, height, VertexTransformers.uv())
|
builder.quad(0f, 0f, width, height, QuadTransformers.uv())
|
||||||
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
||||||
builder.attributes.apply(vao, true)
|
builder.attributes.apply(vao, true)
|
||||||
indexCount = builder.indexCount
|
indexCount = builder.indexCount
|
||||||
|
|
||||||
|
elementIndexType = builder.elementIndexType
|
||||||
|
|
||||||
vao.unbind()
|
vao.unbind()
|
||||||
ebo.unbind()
|
ebo.unbind()
|
||||||
vbo.unbind()
|
vbo.unbind()
|
||||||
} else {
|
} else {
|
||||||
isEmpty = true
|
isEmpty = true
|
||||||
indexCount = 0
|
indexCount = 0
|
||||||
|
elementIndexType = 0
|
||||||
|
|
||||||
vao = null
|
vao = null
|
||||||
vbo = null
|
vbo = null
|
||||||
@ -335,7 +344,7 @@ class Font(
|
|||||||
|
|
||||||
texture!!.bind()
|
texture!!.bind()
|
||||||
state.fontProgram.transform.set(stack.last)
|
state.fontProgram.transform.set(stack.last)
|
||||||
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0L)
|
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||||
checkForGLError()
|
checkForGLError()
|
||||||
|
|
||||||
stack.translateWithMultiplication(advanceX - bearingX, bearingY)
|
stack.translateWithMultiplication(advanceX - bearingX, bearingY)
|
||||||
|
@ -7,6 +7,8 @@ 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.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.client.gl.*
|
import ru.dbotthepony.kstarbound.client.gl.*
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.*
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.*
|
import ru.dbotthepony.kstarbound.defs.tile.*
|
||||||
import ru.dbotthepony.kstarbound.world.TileState
|
import ru.dbotthepony.kstarbound.world.TileState
|
||||||
import ru.dbotthepony.kstarbound.world.ITileChunk
|
import ru.dbotthepony.kstarbound.world.ITileChunk
|
||||||
@ -16,7 +18,7 @@ import kotlin.collections.HashMap
|
|||||||
|
|
||||||
data class TileLayer(
|
data class TileLayer(
|
||||||
val bakedProgramState: ConfiguredShaderProgram,
|
val bakedProgramState: ConfiguredShaderProgram,
|
||||||
val vertexBuilder: DynamicVertexBuilder,
|
val vertexBuilder: AbstractVertexBuilder<*>,
|
||||||
val zPos: Int
|
val zPos: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ class TileLayerList {
|
|||||||
*
|
*
|
||||||
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute].
|
* Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute].
|
||||||
*/
|
*/
|
||||||
fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> DynamicVertexBuilder): DynamicVertexBuilder {
|
fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> AbstractVertexBuilder<*>): AbstractVertexBuilder<*> {
|
||||||
return layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction {
|
return layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction {
|
||||||
return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
|
return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
|
||||||
}).vertexBuilder
|
}).vertexBuilder
|
||||||
@ -166,7 +168,7 @@ private enum class TileRenderTesselateResult {
|
|||||||
HALT
|
HALT
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun vertexTextureBuilder() = DynamicVertexBuilder(GLFlatAttributeList.VERTEX_HSV_TEXTURE, VertexType.QUADS)
|
private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.VERTEX_HSV_TEXTURE, VertexType.QUADS)
|
||||||
|
|
||||||
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
|
||||||
override fun test(tile: TileState?): Boolean {
|
override fun test(tile: TileState?): Boolean {
|
||||||
@ -194,7 +196,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
|||||||
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
||||||
// private var notifiedDepth = false
|
// private var notifiedDepth = false
|
||||||
|
|
||||||
private fun tesselateAt(self: TileState, piece: RenderPiece, getter: ITileChunk, builder: DynamicVertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
private fun tesselateAt(self: TileState, piece: RenderPiece, getter: ITileChunk, builder: AbstractVertexBuilder<*>, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) {
|
||||||
val fx = pos.x.toFloat()
|
val fx = pos.x.toFloat()
|
||||||
val fy = pos.y.toFloat()
|
val fy = pos.y.toFloat()
|
||||||
|
|
||||||
@ -232,7 +234,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
|||||||
val (u0, v0) = texture.pixelToUV(mins)
|
val (u0, v0) = texture.pixelToUV(mins)
|
||||||
val (u1, v1) = texture.pixelToUV(maxs)
|
val (u1, v1) = texture.pixelToUV(maxs)
|
||||||
|
|
||||||
builder.quadZ(a, b, c, d, Z_LEVEL, VertexTransformers.uv(u0, v1, u1, v0).after { it, _ -> it.push(if (!isModifier || self.modifier?.grass == true) self.hueShift else 0f) })
|
builder.quadZ(a, b, c, d, Z_LEVEL, QuadTransformers.uv(u0, v1, u1, v0).after { it, _ -> it.push(if (!isModifier || self.modifier?.grass == true) self.hueShift else 0f) })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tesselatePiece(
|
private fun tesselatePiece(
|
||||||
@ -241,7 +243,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
|||||||
getter: ITileChunk,
|
getter: ITileChunk,
|
||||||
layers: TileLayerList,
|
layers: TileLayerList,
|
||||||
pos: Vector2i,
|
pos: Vector2i,
|
||||||
thisBuilder: DynamicVertexBuilder,
|
thisBuilder: AbstractVertexBuilder<*>,
|
||||||
background: Boolean,
|
background: Boolean,
|
||||||
isModifier: Boolean,
|
isModifier: Boolean,
|
||||||
): TileRenderTesselateResult {
|
): TileRenderTesselateResult {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.util
|
||||||
|
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
|
||||||
|
class ByteBufferOutputStream(val buffer: ByteBuffer) : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
buffer.put(b.toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(b: ByteArray) {
|
||||||
|
buffer.put(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(b: ByteArray, off: Int, len: Int) {
|
||||||
|
buffer.put(b, off, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
var position: Int
|
||||||
|
get() = buffer.position()
|
||||||
|
set(value) { buffer.position(value) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun directLE(capacity: Int) = ByteBufferOutputStream(ByteBuffer.allocateDirect(capacity).also { it.order(ByteOrder.LITTLE_ENDIAN) })
|
||||||
|
}
|
||||||
|
}
|
39
src/main/kotlin/ru/dbotthepony/kstarbound/util/LEStream.kt
Normal file
39
src/main/kotlin/ru/dbotthepony/kstarbound/util/LEStream.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.util
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
fun OutputStream.writeLEInt(value: Int) {
|
||||||
|
write(value)
|
||||||
|
write(value ushr 8)
|
||||||
|
write(value ushr 16)
|
||||||
|
write(value ushr 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeLEShort(value: Int) {
|
||||||
|
write(value)
|
||||||
|
write(value ushr 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeLELong(value: Long) {
|
||||||
|
writeLEInt(value.toInt())
|
||||||
|
writeLEInt((value ushr 32).toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OutputStream.writeLEFloat(value: Float) = writeLEInt(value.toBits())
|
||||||
|
fun OutputStream.writeLEDouble(value: Double) = writeLELong(value.toBits())
|
||||||
|
|
||||||
|
fun InputStream.readLEShort(): Int {
|
||||||
|
return read() or (read() shl 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readLEInt(): Int {
|
||||||
|
return read() or (read() shl 8) or (read() shl 16) or (read() shl 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readLELong(): Long {
|
||||||
|
return readLEInt().toLong() or (readLEInt().toLong() shl 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InputStream.readLEFloat() = Float.fromBits(readLEInt())
|
||||||
|
fun InputStream.readLEDouble() = Double.fromBits(readLELong())
|
Loading…
Reference in New Issue
Block a user