From 0fd5fb0be76ca068818a35aac53edb0c4cd71011 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 13 Feb 2023 17:49:21 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D0=BD=20VertexBuilder=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=B8=D1=85=20=D0=B2=D1=81=D0=B5=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kstarbound/client/ClientChunk.kt | 24 +- .../kstarbound/client/gl/GLStateTracker.kt | 43 +- .../kstarbound/client/gl/program/Programs.kt | 13 +- .../client/gl/vertex/AbstractVertexBuilder.kt | 308 ------------- .../client/gl/vertex/DirectVertexBuilder.kt | 53 --- .../client/gl/vertex/GeometryType.kt | 12 +- .../client/gl/vertex/HeapVertexBuilder.kt | 61 --- .../client/gl/vertex/QuadTransformers.kt | 2 +- .../client/gl/vertex/StatefulVertexBuilder.kt | 47 -- .../client/gl/vertex/StreamVertexBuilder.kt | 17 +- .../client/gl/vertex/VertexBuilder.kt | 407 ++++++++++++++++++ .../kstarbound/client/render/Box2DRenderer.kt | 18 +- .../client/render/ConfiguredShaderProgram.kt | 6 +- .../kstarbound/client/render/Font.kt | 7 +- .../client/render/GPULightRenderer.kt | 9 +- .../kstarbound/client/render/TileRenderer.kt | 10 +- .../client/render/entity/ItemRenderer.kt | 4 +- .../kstarbound/util/ByteBufferOutputStream.kt | 27 -- 18 files changed, 482 insertions(+), 586 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/AbstractVertexBuilder.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/HeapVertexBuilder.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StatefulVertexBuilder.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/VertexBuilder.kt delete mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/util/ByteBufferOutputStream.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt index fd46ba28..6cc123ba 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt @@ -6,9 +6,7 @@ import ru.dbotthepony.kstarbound.client.gl.GLStateTracker import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType -import ru.dbotthepony.kstarbound.client.gl.vertex.StatefulVertexBuilder -import ru.dbotthepony.kstarbound.client.gl.vertex.quad -import ru.dbotthepony.kstarbound.client.gl.vertex.shadowLine +import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer import ru.dbotthepony.kstarbound.client.render.GPULightRenderer @@ -191,13 +189,13 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk Unit) { - builder.begin() + private fun buildGeometry(builder: StreamVertexBuilder, line: (StreamVertexBuilder, Float, Float, Float, Float) -> Unit) { + builder.builder.begin() for (x in this.x * SHADOW_GEOMETRY_SQUARE_SIZE until (this.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) { for (y in this.y * SHADOW_GEOMETRY_SQUARE_SIZE until (this.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) { @@ -228,14 +226,16 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk - it.vertex().pushVec2f(x0, y0) - it.vertex().pushVec2f(x1, y1) + it.builder.vertex().pushVec2f(x0, y0) + it.builder.vertex().pushVec2f(x1, y1) } } fun buildSoftGeometry() { softShadowGeometryRev = tileChangeset - buildGeometry(softShadowGeometry, StatefulVertexBuilder::shadowLine) + buildGeometry(softShadowGeometry) { it, x0, y0, x1, y1 -> + it.builder.shadowLine(x0, y0, x1, y1) + } } override fun renderHardGeometry( @@ -398,14 +398,14 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk Unit) { + inline fun quadWireframe(color: Color = Color.WHITE, lambda: (VertexBuilder) -> Unit) { val builder = quadWireframe - builder.begin() - lambda.invoke(builder) + builder.builder.begin() + lambda.invoke(builder.builder) builder.upload() programs.flat.use() @@ -606,11 +587,11 @@ class GLStateTracker(val locator: ISBFileLocator) { builder.draw(GL_LINES) } - inline fun quadColor(lambda: (StreamVertexBuilder) -> Unit) { + inline fun quadColor(lambda: (VertexBuilder) -> Unit) { val builder = programs.flatColor.builder - builder.begin() - lambda.invoke(builder) + builder.builder.begin() + lambda.invoke(builder.builder) builder.upload() programs.flatColor.use() @@ -619,7 +600,7 @@ class GLStateTracker(val locator: ISBFileLocator) { builder.draw(GL_TRIANGLES) } - inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) { + inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (VertexBuilder) -> Unit = {}) { quadWireframe(color) { it.quad(value.mins.x.toFloat(), value.mins.y.toFloat(), value.maxs.x.toFloat(), value.maxs.y.toFloat()) chain(it) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt index f9ab52e3..1bf2cbea 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/program/Programs.kt @@ -9,7 +9,6 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers -import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kstarbound.client.render.GPULightRenderer import ru.dbotthepony.kvector.vector.Color import kotlin.properties.ReadOnlyProperty @@ -123,8 +122,8 @@ class GLColorQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "scre private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) - builder.begin() - builder.quad(-1f, -1f, 1f, 1f) + builder.builder.begin() + builder.builder.quad(-1f, -1f, 1f, 1f) builder.upload() builder @@ -173,8 +172,8 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) - builder.begin() - builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv()) + builder.builder.begin() + builder.builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv()) builder.upload() builder @@ -201,8 +200,8 @@ class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(sta private val builder by lazy { val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1) - builder.begin() - builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv()) + builder.builder.begin() + builder.builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv()) builder.upload() builder diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/AbstractVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/AbstractVertexBuilder.kt deleted file mode 100644 index 899fa094..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/AbstractVertexBuilder.kt +++ /dev/null @@ -1,308 +0,0 @@ -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.GLType -import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject -import ru.dbotthepony.kstarbound.client.gl.checkForGLError -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>( - val attributes: GLAttributeList, - val type: GeometryType, -) { - 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: VertexBufferObject, ebo: VertexBufferObject, drawType: Int) - - fun upload(vbo: VertexBufferObject, ebo: VertexBufferObject, 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) - checkForGLError() - } - - 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.indices) { - put(elementIndexType, elementMemory, index + elementIndexOffset) - } - - elementIndexOffset += type.elements - indexCount += type.indices.size - } - } - } - - fun vertex(): T { - end() - inVertex = true - attributeIndex = 0 - return this as T - } - - fun pushVec4f(x: Float, y: Float, z: Float, w: Float): T { - expect(GLType.VEC4F) - val memory = vertexMemory - memory.writeLEFloat(x) - memory.writeLEFloat(y) - memory.writeLEFloat(z) - memory.writeLEFloat(w) - attributeIndex++ - 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 > T.shadowLine(x0: Float, y0: Float, x1: Float, y1: Float): T { - vertex() - pushVec4f(x0, y0, x1, y1) - pushVec2f(0f, 0f) - - vertex() - pushVec4f(x0, y0, x1, y1) - pushVec2f(0f, 1f) - - vertex() - pushVec4f(x0, y0, x1, y1) - pushVec2f(1f, 1f) - - vertex() - pushVec4f(x0, y0, x1, y1) - pushVec2f(1f, 0f) - - return this -} - -fun > T.dfShadowLine(x0: Float, y0: Float, x1: Float, y1: Float): T { - shadowLine(x0, y0, x1, y1) - shadowLine(x1, y1, x0, y0) - return this -} - -fun > T.shadowQuad(x0: Float, y0: Float, x1: Float, y1: Float): T { - shadowLine(x0, y0, x1, y0) - shadowLine(x1, y0, x1, y1) - shadowLine(x1, y1, x0, y1) - shadowLine(x0, y1, x0, y0) - return this -} - -// Помощники -fun > T.quad( - x0: Float, - y0: Float, - x1: Float, - y1: Float, - lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM -): T { - check(type.elements == 4) { "Currently building $type" } - - lambda(vertex().pushVec2f(x0, y0), 0).end() - lambda(vertex().pushVec2f(x1, y0), 1).end() - lambda(vertex().pushVec2f(x0, y1), 2).end() - lambda(vertex().pushVec2f(x1, y1), 3).end() - - return this -} - -fun > T.quadRotated( - x0: Float, - y0: Float, - x1: Float, - y1: Float, - x: Float, - y: Float, - angle: Double, - lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM -): T { - check(type.elements == 4) { "Currently building $type" } - - val s = sin(angle).toFloat() - val c = cos(angle).toFloat() - - lambda(vertex().pushVec2f(x + x0 * c - s * y0, y + s * x0 + c * y0), 0).end() - lambda(vertex().pushVec2f(x + x1 * c - s * y0, y + s * x1 + c * y0), 1).end() - lambda(vertex().pushVec2f(x + x0 * c - s * y1, y + s * x0 + c * y1), 2).end() - lambda(vertex().pushVec2f(x + x1 * c - s * y1, y + s * x1 + c * y1), 3).end() - - return this -} - -fun > T.quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): T { - return quad( - aabb.mins.x.toFloat(), - aabb.mins.y.toFloat(), - aabb.maxs.x.toFloat(), - aabb.maxs.y.toFloat(), - lambda - ) -} - -fun > T.quadZ( - x0: Float, - y0: Float, - x1: Float, - y1: Float, - z: Float, - lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM -): T { - check(type.elements == 4) { "Currently building $type" } - - lambda(vertex().pushVec3f(x0, y0, z), 0).end() - lambda(vertex().pushVec3f(x1, y0, z), 1).end() - lambda(vertex().pushVec3f(x0, y1, z), 2).end() - lambda(vertex().pushVec3f(x1, y1, z), 3).end() - - return this -} - -fun > T.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 -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt deleted file mode 100644 index fc080f8c..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt +++ /dev/null @@ -1,53 +0,0 @@ -package ru.dbotthepony.kstarbound.client.gl.vertex - -import org.lwjgl.opengl.GL46 -import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject -import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream - -/** - * Создаёт буфер для данных вне кучи, записывает данные напрямую в него, - * при [upload] загружает данные из буфера напрямую в память видеокарты. - */ -open class DirectVertexBuilder>( - attributes: GLAttributeList, - type: GeometryType, - val maxElements: Int, -) : AbstractVertexBuilder>(attributes, type) { - val maxIndexCount = maxElements * type.indices.size - val maxVertexCount = maxElements * type.elements - - final override val elementIndexType: Int = when (maxIndexCount) { - // api performance issue 102: glDrawElements uses element index type 'GL_UNSIGNED_BYTE' that is not optimal for the current hardware configuration; consider using 'GL_UNSIGNED_SHORT' instead - // in 0 .. 255 -> GL_UNSIGNED_BYTE - in 0 .. 65535 -> GL46.GL_UNSIGNED_SHORT - else -> GL46.GL_UNSIGNED_INT - } - - final override val vertexMemory = ByteBufferOutputStream.directLE(maxVertexCount * attributes.stride) - final override val elementMemory = ByteBufferOutputStream.directLE(maxIndexCount * (if (elementIndexType == GL46.GL_UNSIGNED_SHORT) 2 else 4)) - - override fun ensureIndexCapacity() { - if (vertexCount > maxVertexCount) { - 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: VertexBufferObject, ebo: VertexBufferObject, 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 - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GeometryType.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GeometryType.kt index e7d1eeff..fe99e516 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GeometryType.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/GeometryType.kt @@ -1,6 +1,16 @@ package ru.dbotthepony.kstarbound.client.gl.vertex -enum class GeometryType(val elements: Int, val indices: IntArray) { +enum class GeometryType( + /** + * Число вершин у одного элемента + */ + val elements: Int, + + /** + * Индекс вершин одного элемента + */ + val indices: IntArray +) { AS_IS(1, intArrayOf(0)), LINES(2, intArrayOf(0, 1)), TRIANGLES(3, intArrayOf(0, 1, 2)), diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/HeapVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/HeapVertexBuilder.kt deleted file mode 100644 index 3cbd256f..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/HeapVertexBuilder.kt +++ /dev/null @@ -1,61 +0,0 @@ -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.VertexBufferObject -import ru.dbotthepony.kstarbound.util.readLEShort -import ru.dbotthepony.kstarbound.util.writeLEInt -import java.nio.ByteBuffer - -/** - * Vertex Builder который хранит все данные на куче, при [upload] создаётся [ByteBuffer] вне кучи, - * в него записываются данные из кучи, данные загружаются на видеокарту. - * - * Полезен в случаях, когда размер данных для загрузки заранее не известен. - */ -open class HeapVertexBuilder>( - attributes: GLAttributeList, - type: GeometryType, -) : AbstractVertexBuilder(attributes, type) { - final override val vertexMemory = FastByteArrayOutputStream() - final override val elementMemory = FastByteArrayOutputStream() - - final override var elementIndexType: Int = GL_UNSIGNED_SHORT - private set - - final override fun ensureIndexCapacity() { - if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indices.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()) - } - } - } - - final override fun resetMemory() { - vertexMemory.reset() - elementMemory.reset() - } - - final override fun doUpload(vbo: VertexBufferObject, ebo: VertexBufferObject, 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) - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/QuadTransformers.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/QuadTransformers.kt index 545439f2..7ab02db7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/QuadTransformers.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/QuadTransformers.kt @@ -1,6 +1,6 @@ package ru.dbotthepony.kstarbound.client.gl.vertex -typealias QuadVertexTransformer = (AbstractVertexBuilder<*>, Int) -> AbstractVertexBuilder<*> +typealias QuadVertexTransformer = (VertexBuilder, Int) -> VertexBuilder val EMPTY_VERTEX_TRANSFORM: QuadVertexTransformer = { it, _ -> it } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StatefulVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StatefulVertexBuilder.kt deleted file mode 100644 index f5385ad3..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StatefulVertexBuilder.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ru.dbotthepony.kstarbound.client.gl.vertex - -import org.lwjgl.opengl.GL46 -import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import java.io.Closeable - -class StatefulVertexBuilder( - val state: GLStateTracker, - attributes: GLAttributeList, - type: GeometryType, -) : HeapVertexBuilder(attributes, type), 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_STATIC_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() - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt index 93bc17be..07b4de10 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt @@ -1,21 +1,20 @@ package ru.dbotthepony.kstarbound.client.gl.vertex import org.lwjgl.opengl.GL46 -import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.gl.GLTexture2D import ru.dbotthepony.kstarbound.client.gl.checkForGLError import java.io.Closeable /** - * Класс-помощник для быстрого построения, загрузки и отрисовки геометрии + * Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка */ class StreamVertexBuilder( val state: GLStateTracker, attributes: GLAttributeList, type: GeometryType, - maxElements: Int, -) : DirectVertexBuilder(attributes, type, maxElements), Closeable { + initialCapacity: Int = 64, +) : Closeable { + val builder = VertexBuilder(attributes, type, initialCapacity) private val vao = state.newVAO() private val vbo = state.newVBO() private val ebo = state.newEBO() @@ -33,7 +32,7 @@ class StreamVertexBuilder( } fun upload(drawType: Int = GL46.GL_DYNAMIC_DRAW) { - upload(vbo, ebo, drawType) + builder.upload(vbo, ebo, drawType) } fun bind() = vao.bind() @@ -41,7 +40,7 @@ class StreamVertexBuilder( fun draw(primitives: Int = GL46.GL_TRIANGLES) { bind() - GL46.glDrawElements(primitives, indexCount, elementIndexType, 0L) + GL46.glDrawElements(primitives, builder.indexCount, builder.indexType, 0L) checkForGLError() } @@ -52,9 +51,9 @@ class StreamVertexBuilder( } fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) { - begin() + builder.begin() - quadRotatedZ(x, y, width, height, z, 0f, 0f, angle, transformer) + builder.quadRotatedZ(x, y, width, height, z, 0f, 0f, angle, transformer) upload() draw() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/VertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/VertexBuilder.kt new file mode 100644 index 00000000..a009db5c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/VertexBuilder.kt @@ -0,0 +1,407 @@ +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.GLType +import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject +import ru.dbotthepony.kvector.util2d.AABB +import java.nio.ByteBuffer +import java.nio.ByteOrder +import kotlin.math.cos +import kotlin.math.sin + +private fun interface IndexWriter { + fun write(buffer: ByteBuffer, value: Int) +} + +private fun writer(type: Int): IndexWriter { + return when (type) { + GL_UNSIGNED_SHORT -> IndexWriter { it, value -> it.putShort(value.toShort()) } + GL_UNSIGNED_BYTE -> IndexWriter { it, value -> it.put(value.toByte()) } + else -> IndexWriter { it, value -> it.putInt(value) } + } +} + +private fun chooseIndexType(capacity: Int): Int { + return when (capacity) { + // 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 fun indexSize(type: Int): Int { + return when (type) { + GL_UNSIGNED_BYTE -> 1 + GL_UNSIGNED_SHORT -> 2 + GL_UNSIGNED_INT -> 4 + else -> throw IllegalStateException() + } +} + +/** + * Создаёт буферы вне кучи для загрузки геометрии в них, с набором аттрибутов [attributes], типом [type] и указанным начальным размером. + * + * По мере необходимости, буферы могут быть увеличены в размерах. + * + * Загрузка в память видеокарты происходит напрямую из буферов, через метод [upload] + */ +@Suppress("unchecked_cast") +class VertexBuilder( + val attributes: GLAttributeList, + val type: GeometryType, + initialCapacity: Int = 64 +) { + /** + * [GL_UNSIGNED_BYTE], [GL_UNSIGNED_SHORT] или [GL_UNSIGNED_INT] + */ + var indexType: Int = chooseIndexType(initialCapacity) + private set + var indexSize: Int = indexSize(indexType) + private set + + /** + * В элементах, не вершинах + */ + private var capacity = initialCapacity + private var vertexMemory = ByteBuffer.allocateDirect(capacity * attributes.stride * type.elements).also { it.order(ByteOrder.LITTLE_ENDIAN) } + private var indexMemory = ByteBuffer.allocateDirect(capacity * indexSize * type.indices.size).also { it.order(ByteOrder.LITTLE_ENDIAN) } + private var indexWriter = writer(indexType) + + private var inVertex = false + private var attributeIndex = 0 + private var elementIndexOffset = 0 + + private var elementVertices = 0 + + var vertexCount = 0 + private set + + var indexCount = 0 + private set + + var elementCount = 0 + private set + + fun begin() { + inVertex = false + attributeIndex = 0 + elementIndexOffset = 0 + vertexCount = 0 + indexCount = 0 + elementCount = 0 + vertexMemory.position(0) + indexMemory.position(0) + } + + private 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)") + } + } + + fun upload(vbo: VertexBufferObject, ebo: VertexBufferObject, drawType: Int = GL46.GL_DYNAMIC_DRAW) { + require(vbo.isArray) { "$vbo is not an array" } + require(ebo.isElementArray) { "$ebo 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)" } + + val vertexPos = vertexMemory.position() + val elementPos = indexMemory.position() + + vertexMemory.position(0) + indexMemory.position(0) + + vbo.bufferData(vertexMemory, drawType, length = vertexPos.toLong()) + ebo.bufferData(indexMemory, drawType, length = elementPos.toLong()) + + vertexMemory.position(vertexPos) + indexMemory.position(elementPos) + } + + 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) { + elementCount++ + elementVertices = 0 + + for (index in type.indices) { + indexWriter.write(indexMemory, index + elementIndexOffset) + } + + elementIndexOffset += type.elements + indexCount += type.indices.size + + if (capacity <= elementCount) { + check(this.vertexMemory.position() == this.vertexMemory.capacity()) { "${this.vertexMemory.position()} != ${this.vertexMemory.capacity()}" } + check(this.indexMemory.position() == this.indexMemory.capacity()) { "${this.indexMemory.position()} != ${this.indexMemory.capacity()}" } + + val capacity = capacity * 2 + val indexType = chooseIndexType(capacity) + val indexSize = indexSize(indexType) + + val vertexMemory = ByteBuffer.allocateDirect(capacity * attributes.stride * type.elements) + vertexMemory.order(ByteOrder.LITTLE_ENDIAN) + + this.vertexMemory.position(0) + vertexMemory.put(this.vertexMemory) + + val indexMemory = ByteBuffer.allocateDirect(capacity * indexSize * type.indices.size) + indexMemory.order(ByteOrder.LITTLE_ENDIAN) + + if (indexType != this.indexType) { + when (this.indexType) { + GL_UNSIGNED_BYTE -> { + when (indexType) { + GL_UNSIGNED_BYTE -> throw IllegalArgumentException() + GL_UNSIGNED_SHORT -> { + this.indexMemory.position(0) + + for (i in 0 until this.capacity) { + indexMemory.putShort(this.indexMemory.get().toShort()) + } + } + GL_UNSIGNED_INT -> { + this.indexMemory.position(0) + + for (i in 0 until this.capacity) { + indexMemory.putInt(this.indexMemory.get().toInt()) + } + } + } + } + + GL_UNSIGNED_SHORT -> { + when (indexType) { + GL_UNSIGNED_BYTE -> throw IllegalArgumentException() + GL_UNSIGNED_SHORT -> throw IllegalArgumentException() + GL_UNSIGNED_INT -> { + this.indexMemory.position(0) + + for (i in 0 until this.capacity) { + indexMemory.putInt(this.indexMemory.getShort().toInt()) + } + } + } + } + + else -> throw IllegalArgumentException() + } + } else { + this.indexMemory.position(0) + indexMemory.put(this.indexMemory) + } + + this.capacity = capacity + this.vertexMemory = vertexMemory + this.indexMemory = indexMemory + this.indexType = indexType + this.indexSize = indexSize + this.indexWriter = writer(indexType) + } + } + } + } + + fun vertex(): VertexBuilder { + end() + + inVertex = true + attributeIndex = 0 + return this + } + + fun pushVec4f(x: Float, y: Float, z: Float, w: Float): VertexBuilder { + expect(GLType.VEC4F) + val memory = vertexMemory + memory.putFloat(x) + memory.putFloat(y) + memory.putFloat(z) + memory.putFloat(w) + attributeIndex++ + return this + } + + fun pushVec3f(x: Float, y: Float, z: Float): VertexBuilder { + expect(GLType.VEC3F) + val memory = vertexMemory + memory.putFloat(x) + memory.putFloat(y) + memory.putFloat(z) + attributeIndex++ + return this + } + + fun pushVec2f(x: Float, y: Float): VertexBuilder { + expect(GLType.VEC2F) + val memory = vertexMemory + memory.putFloat(x) + memory.putFloat(y) + attributeIndex++ + return this + } + + fun push(value: Float): VertexBuilder { + expect(GLType.FLOAT) + vertexMemory.putFloat(value) + attributeIndex++ + return this + } + + fun shadowLine(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder { + vertex() + pushVec4f(x0, y0, x1, y1) + pushVec2f(0f, 0f) + + vertex() + pushVec4f(x0, y0, x1, y1) + pushVec2f(0f, 1f) + + vertex() + pushVec4f(x0, y0, x1, y1) + pushVec2f(1f, 1f) + + vertex() + pushVec4f(x0, y0, x1, y1) + pushVec2f(1f, 0f) + + return this + } + + fun dfShadowLine(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder { + shadowLine(x0, y0, x1, y1) + shadowLine(x1, y1, x0, y0) + return this + } + + fun shadowQuad(x0: Float, y0: Float, x1: Float, y1: Float): VertexBuilder { + shadowLine(x0, y0, x1, y0) + shadowLine(x1, y0, x1, y1) + shadowLine(x1, y1, x0, y1) + shadowLine(x0, y1, x0, y0) + return this + } + + // Помощники + fun quad( + x0: Float, + y0: Float, + x1: Float, + y1: Float, + lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM + ): VertexBuilder { + check(type.elements == 4) { "Currently building $type" } + + lambda(vertex().pushVec2f(x0, y0), 0).end() + lambda(vertex().pushVec2f(x1, y0), 1).end() + lambda(vertex().pushVec2f(x0, y1), 2).end() + lambda(vertex().pushVec2f(x1, y1), 3).end() + + return this + } + + fun quadRotated( + x0: Float, + y0: Float, + x1: Float, + y1: Float, + x: Float, + y: Float, + angle: Double, + lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM + ): VertexBuilder { + check(type.elements == 4) { "Currently building $type" } + + val s = sin(angle).toFloat() + val c = cos(angle).toFloat() + + lambda(vertex().pushVec2f(x + x0 * c - s * y0, y + s * x0 + c * y0), 0).end() + lambda(vertex().pushVec2f(x + x1 * c - s * y0, y + s * x1 + c * y0), 1).end() + lambda(vertex().pushVec2f(x + x0 * c - s * y1, y + s * x0 + c * y1), 2).end() + lambda(vertex().pushVec2f(x + x1 * c - s * y1, y + s * x1 + c * y1), 3).end() + + return this + } + + fun quad(aabb: AABB, lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM): VertexBuilder { + 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 + ): VertexBuilder { + check(type.elements == 4) { "Currently building $type" } + + lambda(vertex().pushVec3f(x0, y0, z), 0).end() + lambda(vertex().pushVec3f(x1, y0, z), 1).end() + lambda(vertex().pushVec3f(x0, y1, z), 2).end() + lambda(vertex().pushVec3f(x1, y1, z), 3).end() + + return this + } + + fun quadRotatedZ( + x0: Float, + y0: Float, + x1: Float, + y1: Float, + z: Float, + x: Float, + y: Float, + angle: Double, + lambda: QuadVertexTransformer = EMPTY_VERTEX_TRANSFORM + ): VertexBuilder { + 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 + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt index 64b355e1..5f2e39ca 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Box2DRenderer.kt @@ -20,15 +20,15 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw { override fun drawPolygon(vertices: List, color: Color) { require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" } - val builder = state.flat2DLines.small + val builder = state.flat2DLines - builder.begin() + builder.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.builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat()) + builder.builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat()) } builder.upload() @@ -43,18 +43,18 @@ class Box2DRenderer(val state: GLStateTracker) : IDebugDraw { private fun drawSolid(vertices: List, color: Color) { require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" } - val builder = state.flat2DTriangles.small + val builder = state.flat2DTriangles - builder.begin() + builder.builder.begin() val zero = vertices[0] 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.builder.vertex().pushVec2f(zero.x.toFloat(), zero.y.toFloat()) + builder.builder.vertex().pushVec2f(current.x.toFloat(), current.y.toFloat()) + builder.builder.vertex().pushVec2f(next.x.toFloat(), next.y.toFloat()) } builder.upload() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt index b9270131..7e766b2e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ConfiguredShaderProgram.kt @@ -4,7 +4,7 @@ import org.lwjgl.opengl.GL46.* import ru.dbotthepony.kstarbound.client.gl.program.GLShaderProgram import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject import ru.dbotthepony.kstarbound.client.gl.checkForGLError -import ru.dbotthepony.kstarbound.client.gl.vertex.AbstractVertexBuilder +import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder import ru.dbotthepony.kvector.api.IFloatMatrix import ru.dbotthepony.kvector.matrix.Matrix4fStack @@ -47,11 +47,11 @@ class ConfiguredStaticMesh( ) : AutoCloseable { private var onClose = {} - constructor(programState: ConfiguredShaderProgram, builder: AbstractVertexBuilder<*>) : this( + constructor(programState: ConfiguredShaderProgram, builder: VertexBuilder) : this( programState, builder.indexCount, programState.program.state.newVAO(), - builder.elementIndexType, + builder.indexType, ) { val vbo = programState.program.state.newVBO() val ebo = programState.program.state.newEBO() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt index 10a38b1f..b6645873 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Font.kt @@ -7,10 +7,9 @@ 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.vertex.GLAttributeList -import ru.dbotthepony.kstarbound.client.gl.vertex.HeapVertexBuilder import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType -import ru.dbotthepony.kstarbound.client.gl.vertex.quad +import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.vector.Color @@ -308,14 +307,14 @@ class Font( ebo.bind() vbo.bind() - val builder = HeapVertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, GeometryType.QUADS) + val builder = VertexBuilder(GLAttributeList.VERTEX_2D_TEXTURE, GeometryType.QUADS) 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 + elementIndexType = builder.indexType vao.unbind() ebo.unbind() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt index 3b1aa7ed..9dbca75e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/GPULightRenderer.kt @@ -16,7 +16,6 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder -import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kvector.matrix.Matrix4fStack import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.nfloat.Vector2f @@ -182,8 +181,8 @@ class GPULightRenderer(val state: GLStateTracker) { // Свет val builder = state.programs.light.builder - builder.begin() - builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv()) + builder.builder.begin() + builder.builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv()) builder.upload() state.blendFunc = BLEND_MODE_INV @@ -248,8 +247,8 @@ class GPULightRenderer(val state: GLStateTracker) { // Свет val builder = state.programs.light.builder - builder.begin() - builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv()) + builder.builder.begin() + builder.builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv()) builder.upload() state.blendFunc = BLEND_MODE_SOFT diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt index 41c4d2e3..13b5adf0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/TileRenderer.kt @@ -18,7 +18,7 @@ import kotlin.collections.HashMap data class TileLayer( val bakedProgramState: ConfiguredShaderProgram, - val vertexBuilder: AbstractVertexBuilder<*>, + val vertexBuilder: VertexBuilder, val zPos: Int ) @@ -30,7 +30,7 @@ class TileLayerList { * * Если такого слоя нет, вызывается [compute] и создаётся новый [TileLayer], затем возвращается результат [compute]. */ - fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> AbstractVertexBuilder<*>): AbstractVertexBuilder<*> { + fun computeIfAbsent(program: ConfiguredShaderProgram, zLevel: Int, compute: () -> VertexBuilder): VertexBuilder { return layers.computeIfAbsent(program) { Int2ObjectAVLTreeMap() }.computeIfAbsent(zLevel, Int2ObjectFunction { return@Int2ObjectFunction TileLayer(program, compute.invoke(), zLevel) }).vertexBuilder @@ -169,7 +169,7 @@ private enum class TileRenderTesselateResult { HALT } -private fun vertexTextureBuilder() = HeapVertexBuilder(GLAttributeList.TILE, GeometryType.QUADS) +private fun vertexTextureBuilder() = VertexBuilder(GLAttributeList.TILE, GeometryType.QUADS) private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { override fun test(thisTile: ITileState, otherTile: ITileState): Boolean { @@ -199,7 +199,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { val bakedBackgroundProgramState = renderers.background(texture) // private var notifiedDepth = false - private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileChunk, builder: AbstractVertexBuilder<*>, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) { + private fun tesselateAt(self: ITileState, piece: RenderPiece, getter: ITileChunk, builder: VertexBuilder, pos: Vector2i, offset: Vector2i = Vector2i.ZERO, isModifier: Boolean) { val fx = pos.x.toFloat() val fy = pos.y.toFloat() @@ -246,7 +246,7 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) { getter: ITileChunk, layers: TileLayerList, pos: Vector2i, - thisBuilder: AbstractVertexBuilder<*>, + thisBuilder: VertexBuilder, background: Boolean, isModifier: Boolean, ): TileRenderTesselateResult { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt index 38f9b6e5..c6925757 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt @@ -3,8 +3,6 @@ package ru.dbotthepony.kstarbound.client.render.entity import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.ClientChunk import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers -import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ import ru.dbotthepony.kstarbound.client.render.bind import ru.dbotthepony.kstarbound.world.entities.ItemEntity import ru.dbotthepony.kvector.matrix.Matrix4fStack @@ -25,7 +23,7 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk for (texture in textures) { texture.bind() - state.flat2DTexturedQuads.small.singleSprite(texture.width / PIXELS_IN_STARBOUND_UNITf, texture.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, texture.transformer) + state.flat2DTexturedQuads.singleSprite(texture.width / PIXELS_IN_STARBOUND_UNITf, texture.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, texture.transformer) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/ByteBufferOutputStream.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/ByteBufferOutputStream.kt deleted file mode 100644 index 0e34dd7f..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/ByteBufferOutputStream.kt +++ /dev/null @@ -1,27 +0,0 @@ -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) }) - } -}