Text renderer batching
This commit is contained in:
parent
4c39ff2069
commit
4a1a26a493
@ -17,9 +17,8 @@ import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
||||
import ru.dbotthepony.kstarbound.io.json.VersionedJson
|
||||
import ru.dbotthepony.kstarbound.io.readVarInt
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import ru.dbotthepony.kstarbound.world.`object`.WorldObject
|
||||
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.Vector2f
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.DataInputStream
|
||||
|
@ -236,7 +236,8 @@ class StarboundClient : Closeable {
|
||||
putDebugLog("Initialized GLFW window")
|
||||
}
|
||||
|
||||
val maxTextureBlocks = glGetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
|
||||
val maxTextureBlocks = glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS)
|
||||
val maxUserTextureBlocks = maxTextureBlocks - 1 // available textures blocks for generic use
|
||||
val maxVertexAttribBindPoints = glGetInteger(GL_MAX_VERTEX_ATTRIB_BINDINGS)
|
||||
|
||||
init {
|
||||
@ -375,6 +376,17 @@ class StarboundClient : Closeable {
|
||||
checkForGLError()
|
||||
}
|
||||
|
||||
val whiteTexture = GLTexture2D("white")
|
||||
|
||||
init {
|
||||
val buffer = ByteBuffer.allocateDirect(3)
|
||||
buffer.put(0xFF.toByte())
|
||||
buffer.put(0xFF.toByte())
|
||||
buffer.put(0xFF.toByte())
|
||||
buffer.position(0)
|
||||
whiteTexture.upload(GL_RGB, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, buffer)
|
||||
}
|
||||
|
||||
fun setViewport(x: Int, y: Int, width: Int, height: Int) {
|
||||
ensureSameThread()
|
||||
|
||||
|
@ -32,7 +32,7 @@ private class GLTexturePropertyTracker(private val flag: Int, private var value:
|
||||
@Suppress("SameParameterValue")
|
||||
class GLTexture2D(val name: String = "<unknown>") : GLObject() {
|
||||
override val client = StarboundClient.current()
|
||||
override val pointer = glGenTextures()
|
||||
override val pointer = glCreateTextures(GL_TEXTURE_2D)
|
||||
|
||||
init {
|
||||
checkForGLError()
|
||||
|
@ -1,9 +1,13 @@
|
||||
package ru.dbotthepony.kstarbound.client.gl.shader
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER
|
||||
import org.lwjgl.opengl.GL20.GL_VERTEX_SHADER
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
|
||||
import ru.dbotthepony.kstarbound.client.render.FONT_VERTEX_FORMAT
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||
@ -18,14 +22,40 @@ private fun shaders1(name: String): List<GLShader> {
|
||||
return listOf(client.internalVertex("shaders/vertex/$name.vsh"), client.internalFragment("shaders/fragment/$name.fsh"))
|
||||
}
|
||||
|
||||
class FontProgram : GLShaderProgram(shaders1("font"), VertexAttributes.POSITION_UV), GLShaderProgram.Regular {
|
||||
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix", true)
|
||||
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix", true)
|
||||
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix", true)
|
||||
override var colorMultiplier: IStruct4f by F4Uniform("colorMultiplier", true)
|
||||
var texture by IUniform("texture0")
|
||||
private fun fontShaders(): List<GLShader> {
|
||||
val client = StarboundClient.current()
|
||||
var read = StarboundClient.readInternal("shaders/fragment/font.fsh")
|
||||
|
||||
val builder = StreamVertexBuilder(attributes, GeometryType.QUADS)
|
||||
val textures = ArrayList<String>()
|
||||
val cases = ArrayList<String>()
|
||||
val cases2 = ArrayList<String>()
|
||||
|
||||
for (i in 0 until client.maxUserTextureBlocks) {
|
||||
cases.add("\tvec4 colorResult$i = colorMultiplier * texture(texture$i, uvOut).r;")
|
||||
}
|
||||
|
||||
for (i in 0 until client.maxUserTextureBlocks) {
|
||||
textures.add("layout (binding=$i) uniform sampler2D texture$i;")
|
||||
cases2.add("if (textureIndexOut < ${i + 0.1f}) { colorResult = colorResult$i; }")
|
||||
}
|
||||
|
||||
cases2.add(" { colorResult = colorMultiplier; }")
|
||||
|
||||
read = read.replace("uniform sampler2D texture0;", textures.joinToString("\n")).replace("\tcolorResult = colorMultiplier * texture(texture0, uvOut).r;", cases.joinToString("\n") + cases2.joinToString("\n\telse "))
|
||||
|
||||
val fragment = GLShader(read, GL_FRAGMENT_SHADER)
|
||||
val vertex = GLShader(StarboundClient.readInternal("shaders/vertex/font.vsh"), GL_VERTEX_SHADER)
|
||||
|
||||
return listOf(fragment, vertex)
|
||||
}
|
||||
|
||||
class FontProgram : GLShaderProgram(shaders1("font"), FONT_VERTEX_FORMAT), GLShaderProgram.Regular {
|
||||
override var viewMatrix: Matrix3f by F3x3Uniform("viewMatrix")
|
||||
override var worldMatrix: Matrix3f by F3x3Uniform("worldMatrix")
|
||||
override var modelMatrix: Matrix3f by F3x3Uniform("modelMatrix", true)
|
||||
override var colorMultiplier: IStruct4f by F4Uniform("colorMultiplier")
|
||||
|
||||
var texture by IUniform("texture0")
|
||||
|
||||
init {
|
||||
viewMatrix = Matrix3f.identity()
|
||||
|
@ -3,7 +3,8 @@ package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||
|
||||
enum class VertexAttributeType(val type: GLType) {
|
||||
POSITION(GLType.VEC2F),
|
||||
POSITION(GLType.VEC3F),
|
||||
TEXTURE_INDEX(GLType.FLOAT),
|
||||
COLOR(GLType.VEC4F),
|
||||
UV(GLType.VEC2F),
|
||||
HUE_SHIFT(GLType.FLOAT)
|
||||
|
@ -7,6 +7,7 @@ import org.lwjgl.opengl.GL45.GL_UNSIGNED_BYTE
|
||||
import ru.dbotthepony.kstarbound.client.gl.BufferObject
|
||||
import ru.dbotthepony.kvector.api.IStruct2f
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
@ -191,6 +192,7 @@ class VertexBuilder(
|
||||
vertexCount = 0
|
||||
indexCount = 0
|
||||
elementCount = 0
|
||||
elementVertices = 0
|
||||
vertexMemory.position(0)
|
||||
indexMemory.position(0)
|
||||
ensureCapacity()
|
||||
@ -235,11 +237,15 @@ class VertexBuilder(
|
||||
elementCount++
|
||||
elementVertices = 0
|
||||
|
||||
val indexWriter = indexWriter
|
||||
val indexMemory = indexMemory
|
||||
val elementIndexOffset = elementIndexOffset
|
||||
|
||||
for (index in mode.indices.indices) {
|
||||
indexWriter.write(indexMemory, mode.indices.getInt(index) + elementIndexOffset)
|
||||
}
|
||||
|
||||
elementIndexOffset += mode.elements
|
||||
this.elementIndexOffset += mode.elements
|
||||
indexCount += mode.indices.size
|
||||
|
||||
ensureCapacity()
|
||||
@ -261,6 +267,12 @@ class VertexBuilder(
|
||||
return this
|
||||
}
|
||||
|
||||
fun vertex(transform: Matrix3f, x: Float, y: Float): VertexBuilder {
|
||||
vertex()
|
||||
position(transform, x, y)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun pushFloat(attr: VertexAttributes.Attribute, x: Float): VertexBuilder {
|
||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||
vertexMemory.putFloat(x)
|
||||
@ -269,6 +281,14 @@ class VertexBuilder(
|
||||
return this
|
||||
}
|
||||
|
||||
private fun pushInt(attr: VertexAttributes.Attribute, x: Int): VertexBuilder {
|
||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||
vertexMemory.putInt(x)
|
||||
vertexAttributes = vertexAttributes or attr.flag
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun pushFloat2(attr: VertexAttributes.Attribute, x: Float, y: Float): VertexBuilder {
|
||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||
vertexMemory.putFloat(x)
|
||||
@ -278,6 +298,16 @@ class VertexBuilder(
|
||||
return this
|
||||
}
|
||||
|
||||
private fun pushFloat3(attr: VertexAttributes.Attribute, x: Float, y: Float, z: Float): VertexBuilder {
|
||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||
vertexMemory.putFloat(x)
|
||||
vertexMemory.putFloat(y)
|
||||
vertexMemory.putFloat(z)
|
||||
vertexAttributes = vertexAttributes or attr.flag
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun pushFloat4(attr: VertexAttributes.Attribute, x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
||||
vertexMemory.position(vertexCount * attributes.vertexStride + attr.relativeOffset)
|
||||
vertexMemory.putFloat(x)
|
||||
@ -289,25 +319,45 @@ class VertexBuilder(
|
||||
return this
|
||||
}
|
||||
|
||||
private val position = attributes[VertexAttributeType.POSITION]
|
||||
private val uv = attributes[VertexAttributeType.UV]
|
||||
private val color = attributes[VertexAttributeType.COLOR]
|
||||
private val hueShift = attributes[VertexAttributeType.HUE_SHIFT]
|
||||
private val textureIndex = attributes[VertexAttributeType.TEXTURE_INDEX]
|
||||
|
||||
fun position(x: Float, y: Float): VertexBuilder {
|
||||
return pushFloat2(requireNotNull(attributes[VertexAttributeType.POSITION]) { "Vertex format does not have position attribute" }, x, y)
|
||||
return pushFloat3(requireNotNull(position) { "Vertex format does not have position attribute" }, x, y, 1f)
|
||||
}
|
||||
|
||||
fun position(value: IStruct2f) = position(value.component1(), value.component2())
|
||||
|
||||
fun position(transform: Matrix3f, x: Float, y: Float): VertexBuilder {
|
||||
return pushFloat3(
|
||||
requireNotNull(position) { "Vertex format does not have position attribute" },
|
||||
x * transform.c00 + y * transform.c10 + transform.c20,
|
||||
x * transform.c01 + y * transform.c11 + transform.c21,
|
||||
x * transform.c02 + y * transform.c12 + transform.c22)
|
||||
}
|
||||
|
||||
fun position(transform: Matrix3f, value: IStruct2f) = position(transform, value.component1(), value.component2())
|
||||
|
||||
fun uv(x: Float, y: Float): VertexBuilder {
|
||||
return pushFloat2(requireNotNull(attributes[VertexAttributeType.UV]) { "Vertex format does not have texture UV attribute" }, x, y)
|
||||
return pushFloat2(requireNotNull(uv) { "Vertex format does not have texture UV attribute" }, x, y)
|
||||
}
|
||||
|
||||
fun uv(value: IStruct2f) = position(value.component1(), value.component2())
|
||||
|
||||
fun color(x: Float, y: Float, z: Float, w: Float): VertexBuilder {
|
||||
return pushFloat4(requireNotNull(attributes[VertexAttributeType.COLOR]) { "Vertex format does not have color attribute" }, x, y, z, w)
|
||||
return pushFloat4(requireNotNull(color) { "Vertex format does not have color attribute" }, x, y, z, w)
|
||||
}
|
||||
|
||||
fun color(value: IStruct4f) = color(value.component1(), value.component2(), value.component3(), value.component4())
|
||||
|
||||
fun hueShift(x: Float): VertexBuilder {
|
||||
return pushFloat(requireNotNull(attributes[VertexAttributeType.HUE_SHIFT]) { "Vertex format does not have texture UV attribute" }, x)
|
||||
return pushFloat(requireNotNull(hueShift) { "Vertex format does not have texture UV attribute" }, x)
|
||||
}
|
||||
|
||||
fun textureIndex(x: Int): VertexBuilder {
|
||||
return pushFloat(requireNotNull(textureIndex) { "Vertex format does not have texture index attribute" }, x.toFloat())
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,30 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||
import org.lwjgl.opengl.GL45.*
|
||||
import org.lwjgl.system.MemoryUtil
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
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.VertexAttributes
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributeType
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||
import ru.dbotthepony.kstarbound.defs.image.IUVCoordinates
|
||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||
import ru.dbotthepony.kvector.arrays.Matrix3f
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.util2d.AABBi
|
||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.Vector2i
|
||||
import ru.dbotthepony.kvector.vector.Vector4f
|
||||
|
||||
private fun breakLines(text: String): List<String> {
|
||||
var nextLineBreak = text.indexOf('\n', 0)
|
||||
@ -55,12 +68,14 @@ enum class TextAlignY {
|
||||
TOP, CENTER, BOTTOM
|
||||
}
|
||||
|
||||
val FONT_VERTEX_FORMAT = VertexAttributes.of(VertexAttributeType.POSITION, VertexAttributeType.UV)
|
||||
|
||||
class Font(
|
||||
val font: String = "hobo.ttf",
|
||||
val size: Int = 48
|
||||
) {
|
||||
val state = StarboundClient.current()
|
||||
val face = state.freeType.Face(font, 0L)
|
||||
val client = StarboundClient.current()
|
||||
val face = client.freeType.Face(font, 0L)
|
||||
|
||||
init {
|
||||
face.setSize(0, size)
|
||||
@ -78,6 +93,37 @@ class Font(
|
||||
private val descender = face.nativeMemory.descender.toFloat() / ((size / 48f) * 32f)
|
||||
val lineHeight = ascender - descender
|
||||
|
||||
val bbox = AABBi(
|
||||
Vector2i(roundTowardsNegativeInfinity(face.nativeMemory.bbox.xMin.toInt() / (12.0 * 48.0 / size)), roundTowardsNegativeInfinity(face.nativeMemory.bbox.yMin.toInt() / (12.0 * 48.0 / size))),
|
||||
Vector2i(roundTowardsPositiveInfinity(face.nativeMemory.bbox.xMax.toInt() / (12.0 * 48.0 / size)), roundTowardsPositiveInfinity(face.nativeMemory.bbox.yMax.toInt() / (12.0 * 48.0 / size))),
|
||||
)
|
||||
|
||||
private val atlas = GLTexture2D()
|
||||
private val atlasWidth: Int = glGetInternalformati(GL_TEXTURE_2D, GL_RED, GL_MAX_WIDTH).coerceAtMost(4096)
|
||||
private val atlasHeight: Int = glGetInternalformati(GL_TEXTURE_2D, GL_RED, GL_MAX_HEIGHT).coerceAtMost(4096)
|
||||
private var nextAtlasX = 0
|
||||
private var nextAtlasY = 0
|
||||
|
||||
init {
|
||||
glTextureStorage2D(atlas.pointer, 1, GL_R8, atlasWidth, atlasHeight)
|
||||
checkForGLError()
|
||||
|
||||
atlas.textureMinFilter = GL_LINEAR
|
||||
atlas.textureMagFilter = GL_LINEAR
|
||||
atlas.textureWrapS = GL_CLAMP_TO_EDGE
|
||||
atlas.textureWrapT = GL_CLAMP_TO_EDGE
|
||||
}
|
||||
|
||||
private val builder = VertexBuilder(FONT_VERTEX_FORMAT, GeometryType.QUADS)
|
||||
private val vao = VertexArrayObject()
|
||||
private val vbo = BufferObject.VBO()
|
||||
private val ebo = BufferObject.EBO()
|
||||
|
||||
init {
|
||||
vao.elementBuffer = ebo
|
||||
vao.bindAttributes(vbo, FONT_VERTEX_FORMAT)
|
||||
}
|
||||
|
||||
fun render(
|
||||
text: List<String>,
|
||||
|
||||
@ -111,11 +157,7 @@ class Font(
|
||||
if (scale != 1f)
|
||||
model.scale(x = scale, y = scale)
|
||||
|
||||
state.programs.font.use()
|
||||
state.programs.font.colorMultiplier = color
|
||||
state.programs.font.worldMatrix = state.stack.last()
|
||||
state.activeTexture = 0
|
||||
state.programs.font.texture = 0
|
||||
builder.begin()
|
||||
|
||||
val space = getGlyph(' ')
|
||||
|
||||
@ -170,7 +212,19 @@ class Font(
|
||||
model.translate(x = -lineWidth - movedX, y = lineHeight)
|
||||
}
|
||||
|
||||
state.vao = null
|
||||
client.programs.font.use()
|
||||
client.programs.font.colorMultiplier = color
|
||||
client.programs.font.worldMatrix = client.stack.last()
|
||||
|
||||
builder.upload(vbo, ebo, GL_STREAM_DRAW)
|
||||
|
||||
client.activeTexture = 0
|
||||
atlas.bind()
|
||||
|
||||
client.vao = vao
|
||||
glDrawElements(GL_TRIANGLES, builder.indexCount, builder.indexType, 0L)
|
||||
checkForGLError()
|
||||
client.vao = null
|
||||
|
||||
return TextSize(totalX * scale, totalY * scale)
|
||||
}
|
||||
@ -207,10 +261,10 @@ class Font(
|
||||
if (chr == '\t') {
|
||||
if (lineGlyphs % 4 == 0) {
|
||||
lineWidth += space.advanceX * 4
|
||||
state.stack.last().translate(x = space.advanceX * 4)
|
||||
client.stack.last().translate(x = space.advanceX * 4)
|
||||
} else {
|
||||
lineWidth += space.advanceX * (lineGlyphs % 4)
|
||||
state.stack.last().translate(x = space.advanceX * (lineGlyphs % 4))
|
||||
client.stack.last().translate(x = space.advanceX * (lineGlyphs % 4))
|
||||
}
|
||||
|
||||
lineGlyphs += lineGlyphs % 4
|
||||
@ -243,8 +297,6 @@ class Font(
|
||||
}
|
||||
|
||||
private inner class Glyph(val char: Char) {
|
||||
private val texture: GLTexture2D?
|
||||
|
||||
val isEmpty: Boolean
|
||||
|
||||
val width: Float
|
||||
@ -255,13 +307,13 @@ class Font(
|
||||
val advanceX: Float
|
||||
val advanceY: Float
|
||||
|
||||
private val vao: VertexArrayObject?
|
||||
|
||||
private val indexCount: Int
|
||||
|
||||
private val elementIndexType: Int
|
||||
val uv: Vector4f
|
||||
|
||||
init {
|
||||
if (nextAtlasY + bbox.height > atlasHeight) {
|
||||
throw IllegalStateException("Font atlas is full")
|
||||
}
|
||||
|
||||
face.loadChar(char, LoadFlag.RENDER)
|
||||
val glyph = face.nativeMemory.glyph ?: throw IllegalStateException("Unable to load glyph data for $char (code ${char.code})")
|
||||
val bitmap = glyph.bitmap
|
||||
@ -282,43 +334,26 @@ class Font(
|
||||
// интересно, а что будет когда буфер будет "вычищен" из памяти? Чо, будет double free?
|
||||
val buff = bitmap.byteBuffer!!
|
||||
|
||||
texture = state.newTexture("$font:$char")
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
|
||||
texture.upload(GL_RED, bitmap.width, bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, buff)
|
||||
texture.generateMips()
|
||||
|
||||
texture.textureMinFilter = GL_LINEAR
|
||||
texture.textureMagFilter = GL_LINEAR
|
||||
texture.textureWrapS = GL_CLAMP_TO_EDGE
|
||||
texture.textureWrapT = GL_CLAMP_TO_EDGE
|
||||
try {
|
||||
uv = Vector4f(nextAtlasX / atlasWidth.toFloat(), nextAtlasY / atlasHeight.toFloat(), (nextAtlasX + bitmap.width) / atlasWidth.toFloat(), (nextAtlasY + bitmap.rows) / atlasHeight.toFloat())
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
|
||||
glTextureSubImage2D(atlas.pointer, 0, nextAtlasX, nextAtlasY, bitmap.width, bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, buff)
|
||||
checkForGLError("Uploading glyph texture data")
|
||||
|
||||
vao = state.newVAO()
|
||||
val ebo = state.newEBO()
|
||||
val vbo = state.newVBO()
|
||||
nextAtlasX += bbox.width
|
||||
|
||||
vao.elementBuffer = ebo
|
||||
vao.bindAttributes(vbo, VertexAttributes.POSITION_UV)
|
||||
|
||||
val builder = VertexBuilder(VertexAttributes.POSITION_UV, GeometryType.QUADS)
|
||||
|
||||
builder.vertex(0f, 0f).uv(0f, 0f)
|
||||
builder.vertex(width, 0f).uv(1f, 0f)
|
||||
builder.vertex(width, height).uv(1f, 1f)
|
||||
builder.vertex(0f, height).uv(0f, 1f)
|
||||
|
||||
builder.upload(vbo, ebo, GL_STATIC_DRAW)
|
||||
indexCount = builder.indexCount
|
||||
elementIndexType = builder.indexType
|
||||
if (nextAtlasX + bbox.width > atlasWidth) {
|
||||
nextAtlasX = 0
|
||||
nextAtlasY += bbox.height
|
||||
}
|
||||
} finally {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
|
||||
}
|
||||
} else {
|
||||
isEmpty = true
|
||||
indexCount = 0
|
||||
elementIndexType = 0
|
||||
|
||||
vao = null
|
||||
texture = null
|
||||
uv = Vector4f.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,14 +363,12 @@ class Font(
|
||||
return
|
||||
}
|
||||
|
||||
vao!!.bind()
|
||||
|
||||
model.translate(bearingX, -bearingY)
|
||||
|
||||
texture!!.bind()
|
||||
state.programs.font.modelMatrix = model
|
||||
glDrawElements(GL_TRIANGLES, indexCount, elementIndexType, 0L)
|
||||
checkForGLError()
|
||||
builder.vertex(model, 0f, 0f).uv(uv.x, uv.y)
|
||||
builder.vertex(model, width, 0f).uv(uv.z, uv.y)
|
||||
builder.vertex(model, width, height).uv(uv.z, uv.w)
|
||||
builder.vertex(model, 0f, height).uv(uv.x, uv.w)
|
||||
|
||||
model.translate(advanceX - bearingX, bearingY)
|
||||
}
|
||||
|
@ -270,12 +270,12 @@ class ClientWorld(
|
||||
if (obj.pos.x in client.viewportCellX .. client.viewportCellX + client.viewportCellWidth && obj.pos.y in client.viewportCellY .. client.viewportCellY + client.viewportCellHeight) {
|
||||
//layers.add(RenderLayer.Object.index) {
|
||||
layers.add(obj.orientation?.renderLayer ?: continue) {
|
||||
client.quadWireframe {
|
||||
/*client.quadWireframe {
|
||||
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat())
|
||||
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat())
|
||||
it.vertex(obj.pos.x.toFloat() + 1f, obj.pos.y.toFloat() + 1f)
|
||||
it.vertex(obj.pos.x.toFloat(), obj.pos.y.toFloat() + 1f)
|
||||
}
|
||||
}*/
|
||||
|
||||
obj.drawables.forEach {
|
||||
val (x, y) = obj.imagePosition
|
||||
|
@ -84,7 +84,7 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
||||
val texture = client.loadTexture(path.imagePath.value!!)
|
||||
val program = if (fullbright) client.programs.positionTexture else client.programs.positionTextureLightmap
|
||||
|
||||
program.modelMatrix = transform.map({ it }, {
|
||||
val mat = transform.map({ it }, {
|
||||
val mat = Matrix3f.identity()
|
||||
|
||||
it.scale.map({ mat.scale(it / PIXELS_IN_STARBOUND_UNITf, it / PIXELS_IN_STARBOUND_UNITf) }, { mat.scale(it / PIXELS_IN_STARBOUND_UNITf) })
|
||||
@ -105,6 +105,9 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
||||
mat
|
||||
})
|
||||
|
||||
//program.modelMatrix = mat
|
||||
program.modelMatrix = Matrix3f.identity()
|
||||
|
||||
program.worldMatrix = client.stack.last()
|
||||
program.use()
|
||||
program.texture0 = 0
|
||||
@ -112,10 +115,10 @@ sealed class Drawable(val position: Vector2f, val color: RGBAColor, val fullbrig
|
||||
texture.bind()
|
||||
|
||||
program.builder.builder.begin(GeometryType.QUADS)
|
||||
program.builder.builder.vertex(x, y).uv(sprite.u0, sprite.v0)
|
||||
program.builder.builder.vertex(x + sprite.width, y).uv(sprite.u1, sprite.v0)
|
||||
program.builder.builder.vertex(x + sprite.width, y + sprite.height).uv(sprite.u1, sprite.v1)
|
||||
program.builder.builder.vertex(x, y + sprite.height).uv(sprite.u0, sprite.v1)
|
||||
program.builder.builder.vertex(mat, x, y).uv(sprite.u0, sprite.v0)
|
||||
program.builder.builder.vertex(mat, x + sprite.width, y).uv(sprite.u1, sprite.v0)
|
||||
program.builder.builder.vertex(mat, x + sprite.width, y + sprite.height).uv(sprite.u1, sprite.v1)
|
||||
program.builder.builder.vertex(mat, x, y + sprite.height).uv(sprite.u0, sprite.v1)
|
||||
program.builder.upload()
|
||||
program.builder.draw()
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
||||
import ru.dbotthepony.kstarbound.world.api.TileView
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kstarbound.world.`object`.WorldObject
|
||||
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
||||
import ru.dbotthepony.kvector.api.IStruct2d
|
||||
import ru.dbotthepony.kvector.api.IStruct2i
|
||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
||||
|
@ -4,12 +4,7 @@ import ru.dbotthepony.kstarbound.world.Chunk
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
|
||||
abstract class Entity(
|
||||
/**
|
||||
* The world this entity in, never changes.
|
||||
*/
|
||||
val world: World<*, *>
|
||||
) {
|
||||
abstract class Entity(val world: World<*, *>) {
|
||||
/**
|
||||
* The chunk this entity currently in, it is automatically updated on each change of [position]
|
||||
*/
|
||||
@ -17,13 +12,9 @@ abstract class Entity(
|
||||
set(value) {
|
||||
if (!isSpawned) {
|
||||
throw IllegalStateException("Trying to set chunk this entity belong to before spawning in world")
|
||||
}
|
||||
|
||||
if (isRemoved) {
|
||||
} else if (isRemoved) {
|
||||
throw IllegalStateException("This entity was removed")
|
||||
}
|
||||
|
||||
if (value == field) {
|
||||
} else if (value == field) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ru.dbotthepony.kstarbound.world.`object`
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.JsonObject
|
@ -1,7 +1,7 @@
|
||||
|
||||
#version 330
|
||||
#version 450
|
||||
|
||||
uniform sampler2D texture0;
|
||||
layout (binding = 0) uniform sampler2D texture0;
|
||||
|
||||
uniform vec4 colorMultiplier;
|
||||
out vec4 colorResult;
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
layout (location = 0) in vec2 pos;
|
||||
layout (location = 0) in vec3 pos;
|
||||
|
||||
#ifdef TEXTURE
|
||||
layout (location = TEXTURE) in vec2 uvIn;
|
||||
@ -21,7 +21,7 @@ uniform mat3 worldMatrix; // matrix stack
|
||||
uniform mat3 modelMatrix; // local transformations
|
||||
|
||||
void main() {
|
||||
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||
vec3 result = viewMatrix * worldMatrix * modelMatrix * pos;
|
||||
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||
|
||||
#ifdef HUE_SHIFT
|
||||
|
@ -1,17 +1,16 @@
|
||||
|
||||
#version 330
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec2 pos;
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 uvData;
|
||||
|
||||
uniform mat3 viewMatrix; // projection (viewport)
|
||||
uniform mat3 worldMatrix; // world position
|
||||
uniform mat3 modelMatrix; // mesh/local transformations
|
||||
|
||||
out vec2 uvOut;
|
||||
|
||||
void main() {
|
||||
vec3 result = viewMatrix * worldMatrix * modelMatrix * vec3(pos, 1.0);
|
||||
vec3 result = viewMatrix * worldMatrix * pos;
|
||||
gl_Position = vec4(result.x, result.y, 0.0, result.z);
|
||||
|
||||
uvOut = uvData;
|
||||
|
Loading…
Reference in New Issue
Block a user