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 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.renderLayeredList
|
||||
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.world.*
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
|
||||
class ClientWorld(
|
||||
@ -49,8 +48,7 @@ class ClientWorld(
|
||||
|
||||
client.gl.shaderVertexTexture.use()
|
||||
|
||||
val stateful = client.gl.flat2DTexturedQuads.statefulSmall
|
||||
val builder = stateful.builder
|
||||
val builder = client.gl.flat2DTexturedQuads.small
|
||||
|
||||
client.gl.activeTexture = 0
|
||||
client.gl.shaderVertexTexture["_texture"] = 0
|
||||
@ -80,7 +78,7 @@ class ClientWorld(
|
||||
x0 += 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) {
|
||||
break
|
||||
@ -91,8 +89,8 @@ class ClientWorld(
|
||||
}
|
||||
}
|
||||
|
||||
stateful.upload()
|
||||
stateful.draw()
|
||||
builder.upload()
|
||||
builder.draw()
|
||||
|
||||
client.gl.matrixStack.pop()
|
||||
}
|
||||
|
@ -1,45 +1,19 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl
|
||||
|
||||
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
|
||||
*
|
||||
* Аттрибуты плотно упакованы и идут один за другим
|
||||
*
|
||||
* Создаётся через [GLFlatAttributeListBuilder]
|
||||
* Создаётся через [GLAttributeList.Builder]
|
||||
*/
|
||||
class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeList {
|
||||
val attributes: List<AttributeListPosition>
|
||||
class GLAttributeList(builder: Builder) {
|
||||
data class Attribute(val name: String, val index: Int, val glType: GLType, val stride: Int, val offset: Long)
|
||||
|
||||
val attributes: List<Attribute>
|
||||
val size get() = attributes.size
|
||||
|
||||
/**
|
||||
@ -49,10 +23,8 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
||||
|
||||
operator fun get(index: Int) = attributes[index]
|
||||
|
||||
fun vertexBuilder(vertexType: VertexType) = DynamicVertexBuilder(this, vertexType)
|
||||
|
||||
init {
|
||||
val buildList = ArrayList<AttributeListPosition>()
|
||||
val buildList = ArrayList<Attribute>()
|
||||
|
||||
var offset = 0L
|
||||
var stride = 0
|
||||
@ -63,20 +35,19 @@ class GLFlatAttributeList(builder: GLFlatAttributeListBuilder) : IGLAttributeLis
|
||||
|
||||
this.stride = stride
|
||||
|
||||
// val counter = mutableMapOf<Int, Int>()
|
||||
|
||||
for (i in builder.attributes.indices) {
|
||||
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
|
||||
|
||||
// counter[value.typeIndentity] = (counter[value.typeIndentity] ?: 0) + 1
|
||||
}
|
||||
|
||||
attributes = ImmutableList.copyOf(buildList)
|
||||
}
|
||||
|
||||
override fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
||||
/**
|
||||
* Применяет список атрибутов к заданному [GLVertexArrayObject] (попутно включая или отключая их через [enable])
|
||||
*/
|
||||
fun apply(target: GLVertexArrayObject, enable: Boolean) {
|
||||
for (i in attributes.indices) {
|
||||
val value = attributes[i]
|
||||
target.attribute(i, value.glType.logicalSize, value.glType.typeIndentity, false, value.stride, value.offset)
|
||||
@ -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 {
|
||||
val VEC2F = GLFlatAttributeListBuilder().push(GLType.VEC2F).build()
|
||||
val VEC3F = GLFlatAttributeListBuilder().push(GLType.VEC3F).build()
|
||||
val VEC2F = Builder().push(GLType.VEC2F).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()
|
||||
|
||||
val VERTEX_2D_TEXTURE = GLFlatAttributeListBuilder().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)
|
||||
}
|
||||
}
|
||||
val VERTEX_2D_TEXTURE = Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import org.lwjgl.opengl.GL
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
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.Font
|
||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||
@ -86,7 +88,6 @@ interface GLCleanable : Cleaner.Cleanable {
|
||||
|
||||
interface GLStreamBuilderList {
|
||||
val small: StreamVertexBuilder
|
||||
val statefulSmall: StatefulStreamVertexBuilder
|
||||
}
|
||||
|
||||
class GLStateTracker {
|
||||
@ -403,61 +404,37 @@ class GLStateTracker {
|
||||
|
||||
val flat2DLines = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.LINES, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.LINES, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val flat2DTriangles = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.TRIANGLES, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.TRIANGLES, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val flat2DQuads = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val flat2DTexturedQuads = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VERTEX_TEXTURE, VertexType.QUADS, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, VertexType.QUADS, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val flat2DQuadLines = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS_AS_LINES, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val flat2DQuadWireframe = object : GLStreamBuilderList {
|
||||
override val small by lazy {
|
||||
return@lazy StreamVertexBuilder(GLFlatAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 1024)
|
||||
}
|
||||
|
||||
override val statefulSmall by lazy {
|
||||
return@lazy StatefulStreamVertexBuilder(this@GLStateTracker, small)
|
||||
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, VertexType.QUADS_AS_LINES_WIREFRAME, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,20 +444,17 @@ class GLStateTracker {
|
||||
val font = Font(this)
|
||||
|
||||
inline fun quadWireframe(color: Color = Color.WHITE, lambda: (StreamVertexBuilder) -> Unit) {
|
||||
val stateful = flat2DQuadWireframe.statefulSmall
|
||||
val builder = stateful.builder
|
||||
val builder = flat2DQuadWireframe.small
|
||||
|
||||
builder.begin()
|
||||
|
||||
lambda.invoke(builder)
|
||||
|
||||
stateful.upload()
|
||||
builder.upload()
|
||||
|
||||
flatProgram.use()
|
||||
flatProgram.color.set(color)
|
||||
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 = {}) {
|
||||
|
@ -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) {
|
||||
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
||||
|
||||
val stateful = state.flat2DLines.statefulSmall
|
||||
val builder = stateful.builder
|
||||
val builder = state.flat2DLines.small
|
||||
|
||||
builder.begin()
|
||||
|
||||
for (i in vertices.indices) {
|
||||
val current = vertices[i]
|
||||
val next = vertices[(i + 1) % vertices.size]
|
||||
builder.Vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||
builder.Vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||
builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||
builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||
}
|
||||
|
||||
stateful.upload()
|
||||
builder.upload()
|
||||
|
||||
state.flatProgram.use()
|
||||
state.flatProgram.color.set(color)
|
||||
state.flatProgram.transform.set(state.matrixStack.last)
|
||||
|
||||
stateful.draw(GL_LINES)
|
||||
builder.draw(GL_LINES)
|
||||
}
|
||||
|
||||
private fun drawSolid(vertices: List<Vector2d>, color: Color) {
|
||||
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
||||
|
||||
val stateful = state.flat2DTriangles.statefulSmall
|
||||
val builder = stateful.builder
|
||||
val builder = state.flat2DTriangles.small
|
||||
|
||||
builder.begin()
|
||||
|
||||
@ -54,18 +52,18 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw {
|
||||
for (i in 1 until vertices.size) {
|
||||
val current = vertices[i]
|
||||
val next = vertices[(i + 1) % vertices.size]
|
||||
builder.Vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat())
|
||||
builder.Vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||
builder.Vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||
builder.vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat())
|
||||
builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat())
|
||||
builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat())
|
||||
}
|
||||
|
||||
stateful.upload()
|
||||
builder.upload()
|
||||
|
||||
state.flatProgram.use()
|
||||
state.flatProgram.color.set(color)
|
||||
state.flatProgram.transform.set(state.matrixStack.last)
|
||||
|
||||
stateful.draw(GL_TRIANGLES)
|
||||
builder.draw(GL_TRIANGLES)
|
||||
}
|
||||
|
||||
override fun drawSolidPolygon(vertices: List<Vector2d>, color: Color) {
|
||||
|
@ -3,8 +3,8 @@ package ru.dbotthepony.kstarbound.client.render
|
||||
import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLShaderProgram
|
||||
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.vertex.AbstractVertexBuilder
|
||||
import ru.dbotthepony.kvector.api.IFloatMatrix
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
|
||||
@ -43,13 +43,15 @@ class ConfiguredStaticMesh(
|
||||
val programState: ConfiguredShaderProgram,
|
||||
val indexCount: Int,
|
||||
val vao: GLVertexArrayObject,
|
||||
val elementIndexType: Int,
|
||||
) : AutoCloseable {
|
||||
private var onClose = {}
|
||||
|
||||
constructor(programState: ConfiguredShaderProgram, builder: DynamicVertexBuilder) : this(
|
||||
constructor(programState: ConfiguredShaderProgram, builder: AbstractVertexBuilder<*>) : this(
|
||||
programState,
|
||||
builder.indexCount,
|
||||
programState.program.state.newVAO(),
|
||||
builder.elementIndexType,
|
||||
) {
|
||||
val vbo = programState.program.state.newVBO()
|
||||
val ebo = programState.program.state.newEBO()
|
||||
@ -80,7 +82,7 @@ class ConfiguredStaticMesh(
|
||||
}
|
||||
|
||||
vao.bind()
|
||||
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0L)
|
||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||
checkForGLError()
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
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.client.ClientChunk
|
||||
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.projectile.Projectile
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
@ -70,8 +69,7 @@ open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk:
|
||||
|
||||
animator.advance()
|
||||
|
||||
val stateful = state.flat2DTexturedQuads.statefulSmall
|
||||
val builder = stateful.builder
|
||||
val builder = state.flat2DTexturedQuads.small
|
||||
|
||||
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 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()
|
||||
stateful.draw()
|
||||
builder.upload()
|
||||
builder.draw()
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ import org.lwjgl.opengl.GL46.*
|
||||
import ru.dbotthepony.kstarbound.client.freetype.LoadFlag
|
||||
import ru.dbotthepony.kstarbound.client.gl.*
|
||||
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.vector.Color
|
||||
|
||||
@ -259,6 +263,8 @@ class Font(
|
||||
|
||||
private val indexCount: Int
|
||||
|
||||
private val elementIndexType: Int
|
||||
|
||||
init {
|
||||
face.loadChar(char, LoadFlag.RENDER)
|
||||
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()
|
||||
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.attributes.apply(vao, true)
|
||||
indexCount = builder.indexCount
|
||||
|
||||
elementIndexType = builder.elementIndexType
|
||||
|
||||
vao.unbind()
|
||||
ebo.unbind()
|
||||
vbo.unbind()
|
||||
} else {
|
||||
isEmpty = true
|
||||
indexCount = 0
|
||||
elementIndexType = 0
|
||||
|
||||
vao = null
|
||||
vbo = null
|
||||
@ -335,7 +344,7 @@ class Font(
|
||||
|
||||
texture!!.bind()
|
||||
state.fontProgram.transform.set(stack.last)
|
||||
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0L)
|
||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||
checkForGLError()
|
||||
|
||||
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.Starbound
|
||||
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.world.TileState
|
||||
import ru.dbotthepony.kstarbound.world.ITileChunk
|
||||
@ -16,7 +18,7 @@ import kotlin.collections.HashMap
|
||||
|
||||
data class TileLayer(
|
||||
val bakedProgramState: ConfiguredShaderProgram,
|
||||
val vertexBuilder: DynamicVertexBuilder,
|
||||
val vertexBuilder: AbstractVertexBuilder<*>,
|
||||
val zPos: Int
|
||||
)
|
||||
|
||||
@ -28,7 +30,7 @@ class TileLayerList {
|
||||
*
|
||||
* Если такого слоя нет, вызывается [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@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel)
|
||||
}).vertexBuilder
|
||||
@ -166,7 +168,7 @@ private enum class TileRenderTesselateResult {
|
||||
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 {
|
||||
override fun test(tile: TileState?): Boolean {
|
||||
@ -194,7 +196,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
val bakedBackgroundProgramState = state.tileRenderers.background(texture)
|
||||
// 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 fy = pos.y.toFloat()
|
||||
|
||||
@ -232,7 +234,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
val (u0, v0) = texture.pixelToUV(mins)
|
||||
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(
|
||||
@ -241,7 +243,7 @@ class TileRenderer(val state: GLStateTracker, val def: IRenderableTile) {
|
||||
getter: ITileChunk,
|
||||
layers: TileLayerList,
|
||||
pos: Vector2i,
|
||||
thisBuilder: DynamicVertexBuilder,
|
||||
thisBuilder: AbstractVertexBuilder<*>,
|
||||
background: Boolean,
|
||||
isModifier: Boolean,
|
||||
): 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